mirror of
https://github.com/expressjs/express.git
synced 2026-02-27 03:07:33 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53589b4f37 | ||
|
|
aaed3e134d | ||
|
|
91ced24359 |
@@ -1,11 +0,0 @@
|
||||
# https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.js,*.json,*.yml}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
@@ -1,2 +0,0 @@
|
||||
coverage
|
||||
node_modules
|
||||
@@ -1,14 +0,0 @@
|
||||
root: true
|
||||
env:
|
||||
es2022: true
|
||||
node: true
|
||||
rules:
|
||||
eol-last: error
|
||||
eqeqeq: [error, allow-null]
|
||||
indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }]
|
||||
no-trailing-spaces: error
|
||||
no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }]
|
||||
no-restricted-globals:
|
||||
- error
|
||||
- name: Buffer
|
||||
message: Use `import { Buffer } from "node:buffer"` instead of the global Buffer.
|
||||
17
.github/dependabot.yml
vendored
17
.github/dependabot.yml
vendored
@@ -1,17 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "23:00"
|
||||
timezone: Europe/London
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
117
.github/workflows/ci.yml
vendored
117
.github/workflows/ci.yml
vendored
@@ -1,117 +0,0 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- '4.x'
|
||||
- '5.x'
|
||||
- '5.0'
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Cancel in progress workflows
|
||||
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --ignore-scripts --include=dev
|
||||
|
||||
- name: Run lint
|
||||
run: npm run lint
|
||||
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node-version: [18, 19, 20, 21, 22, 23, 24, 25]
|
||||
# Node.js release schedule: https://nodejs.org/en/about/releases/
|
||||
|
||||
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Configure npm loglevel
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Output Node and NPM versions
|
||||
run: |
|
||||
echo "Node.js version: $(node -v)"
|
||||
echo "NPM version: $(npm -v)"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: npm run test-ci
|
||||
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
|
||||
path: ./coverage/lcov.info
|
||||
retention-days: 1
|
||||
|
||||
coverage:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install lcov
|
||||
shell: bash
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: ./coverage
|
||||
pattern: coverage-node-*
|
||||
|
||||
- name: Merge coverage reports
|
||||
shell: bash
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
||||
with:
|
||||
file: ./lcov.info
|
||||
74
.github/workflows/codeql.yml
vendored
74
.github/workflows/codeql.yml
vendored
@@ -1,74 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ["master"]
|
||||
schedule:
|
||||
- cron: "0 0 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [javascript, actions]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config: |
|
||||
paths-ignore:
|
||||
- test
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
101
.github/workflows/legacy.yml
vendored
101
.github/workflows/legacy.yml
vendored
@@ -1,101 +0,0 @@
|
||||
name: legacy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- '4.x'
|
||||
- '5.x'
|
||||
- '5.0'
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Cancel in progress workflows
|
||||
# in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node-version: [16, 17]
|
||||
# Node.js release schedule: https://nodejs.org/en/about/releases/
|
||||
|
||||
name: Node.js ${{ matrix.node-version }} - ${{matrix.os}}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Configure npm loglevel
|
||||
run: |
|
||||
npm config set loglevel error
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Output Node and NPM versions
|
||||
run: |
|
||||
echo "Node.js version: $(node -v)"
|
||||
echo "NPM version: $(npm -v)"
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: npm run test-ci
|
||||
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }}
|
||||
path: ./coverage/lcov.info
|
||||
retention-days: 1
|
||||
|
||||
coverage:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install lcov
|
||||
shell: bash
|
||||
run: sudo apt-get -y install lcov
|
||||
|
||||
- name: Collect coverage reports
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: ./coverage
|
||||
pattern: coverage-node-*
|
||||
|
||||
- name: Merge coverage reports
|
||||
shell: bash
|
||||
run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
||||
with:
|
||||
file: ./lcov.info
|
||||
72
.github/workflows/scorecard.yml
vendored
72
.github/workflows/scorecard.yml
vendored
@@ -1,72 +0,0 @@
|
||||
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||
# by a third-party and are governed by separate terms of service, privacy
|
||||
# policy, and support documentation.
|
||||
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
# For Branch-Protection check. Only the default branch is supported. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||
branch_protection_rule:
|
||||
# To guarantee Maintained check is occasionally updated. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||
schedule:
|
||||
- cron: '16 21 * * 1'
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
# Needed to publish results and get a badge (see publish_results below).
|
||||
id-token: write
|
||||
# Uncomment the permissions below if installing in a private repository.
|
||||
# contents: read
|
||||
# actions: read
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||
# - you are installing Scorecard on a *private* repository
|
||||
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
|
||||
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||
|
||||
# Public repositories:
|
||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||
# - Allows the repository to include the Scorecard badge.
|
||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||
# For private repositories:
|
||||
# - `publish_results` will always be set to `false`, regardless
|
||||
# of the value entered here.
|
||||
publish_results: true
|
||||
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
25
.gitignore
vendored
25
.gitignore
vendored
@@ -1,20 +1,7 @@
|
||||
# npm
|
||||
node_modules
|
||||
package-lock.json
|
||||
npm-shrinkwrap.json
|
||||
.DS_Store
|
||||
*.seed
|
||||
*.log
|
||||
*.gz
|
||||
|
||||
# Yarn
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
|
||||
# Coveralls
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
# Benchmarking
|
||||
benchmarks/graphs
|
||||
|
||||
# ignore additional files using core.excludesFile
|
||||
# https://git-scm.com/docs/gitignore
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
benchmarks/graphs
|
||||
15
.gitmodules
vendored
Normal file
15
.gitmodules
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
[submodule "lib/support/ext"]
|
||||
path = lib/support/ext
|
||||
url = git://github.com/visionmedia/ext.js.git
|
||||
[submodule "lib/support/sass"]
|
||||
path = lib/support/sass
|
||||
url = git://github.com/visionmedia/sass.js.git
|
||||
[submodule "lib/support/class"]
|
||||
path = lib/support/class
|
||||
url = git://github.com/visionmedia/class.js.git
|
||||
[submodule "lib/support/haml"]
|
||||
path = lib/support/haml
|
||||
url = git://github.com/visionmedia/haml.js.git
|
||||
[submodule "lib/support/multipart"]
|
||||
path = lib/support/multipart
|
||||
url = git://github.com/isaacs/multipart-js.git
|
||||
3631
History.md
3631
History.md
File diff suppressed because it is too large
Load Diff
24
LICENSE
24
LICENSE
@@ -1,24 +0,0 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
|
||||
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
39
Makefile
Normal file
39
Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
AB = ab
|
||||
ABFLAGS = -n 3000 -c 50
|
||||
NODE = node
|
||||
COFFEE = coffee
|
||||
|
||||
all: test
|
||||
|
||||
test:
|
||||
@$(NODE) spec/node.js all
|
||||
|
||||
app: app-chat
|
||||
|
||||
prof:
|
||||
@$(NODE) --prof --prof_auto examples/chat/app.js
|
||||
|
||||
app-chat:
|
||||
@$(NODE) examples/chat/app.js
|
||||
|
||||
app-hello-world:
|
||||
@$(NODE) examples/hello-world/app.js
|
||||
|
||||
app-upload:
|
||||
@$(NODE) examples/upload/app.js
|
||||
|
||||
app-coffee-upload: compile-coffee
|
||||
@$(NODE) examples/coffee-upload/app.js
|
||||
|
||||
compile-coffee:
|
||||
@$(COFFEE) examples/coffee-upload/app.coffee
|
||||
|
||||
benchmark: benchmarks/run
|
||||
@./benchmarks/run
|
||||
@./benchmarks/graph
|
||||
|
||||
graphs:
|
||||
@./benchmarks/graph
|
||||
|
||||
.PHONY: install test app benchmark graphs
|
||||
365
Readme.md
365
Readme.md
@@ -1,278 +1,137 @@
|
||||
[](https://expressjs.com/)
|
||||
|
||||
**Fast, unopinionated, minimalist web framework for [Node.js](https://nodejs.org).**
|
||||
# Express
|
||||
|
||||
Insanely fast (and small) server-side JavaScript web development framework
|
||||
built on **node.js** and the **V8 JavaScript engine**.
|
||||
|
||||
**This project has a [Code of Conduct].**
|
||||
* Visit the [Wiki](http://wiki.github.com/visionmedia/express) for documentation
|
||||
* Visit the [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
|
||||
Express will soon run on the [Connect](http://github.com/extjs/Connect) project, a middleware layer for nodejs.
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Installation](#installation)
|
||||
- [Features](#features)
|
||||
- [Docs \& Community](#docs--community)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Philosophy](#philosophy)
|
||||
- [Examples](#examples)
|
||||
- [Contributing](#contributing)
|
||||
- [Security Issues](#security-issues)
|
||||
- [Running Tests](#running-tests)
|
||||
- [Current project team members](#current-project-team-members)
|
||||
- [TC (Technical Committee)](#tc-technical-committee)
|
||||
- [TC emeriti members](#tc-emeriti-members)
|
||||
- [Triagers](#triagers)
|
||||
- [Emeritus Triagers](#emeritus-triagers)
|
||||
- [License](#license)
|
||||
|
||||
|
||||
[![NPM Version][npm-version-image]][npm-url]
|
||||
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
|
||||
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
|
||||
|
||||
|
||||
```js
|
||||
import express from 'express'
|
||||
|
||||
const app = express()
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World')
|
||||
})
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Server is running on http://localhost:3000')
|
||||
})
|
||||
```
|
||||
## Features (so far)
|
||||
|
||||
* Sexy DSL with robust sinatra-like routing
|
||||
* High performance
|
||||
* Session support
|
||||
* Cache API
|
||||
* RESTful HTTP client
|
||||
* Mime helpers
|
||||
* ETag support
|
||||
* Redirection helpers
|
||||
* Multipart file upload support
|
||||
* Test helpers (mock requests etc)
|
||||
* Environment based configuration
|
||||
* Light-weight JavaScript class implementation via [class.js](http://github.com/visionmedia/class.js/)
|
||||
* Persistent flash messages
|
||||
* Route passing
|
||||
* View support (ejs, haml, sass, etc)
|
||||
* View partials
|
||||
* View globals/helpers support
|
||||
* Full test coverage
|
||||
* Logger plugin with several formats
|
||||
* Upload size restrictions
|
||||
* Extremely readable specs
|
||||
* Cookie support
|
||||
|
||||
## Installation
|
||||
|
||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||
[npm registry](https://www.npmjs.com/).
|
||||
Install the [Kiwi package manager for nodejs](http://github.com/visionmedia/kiwi)
|
||||
and run:
|
||||
|
||||
$ kiwi install express
|
||||
|
||||
Before installing, [download and install Node.js](https://nodejs.org/en/download/).
|
||||
Node.js 18 or higher is required.
|
||||
or via npm:
|
||||
|
||||
If this is a brand new project, make sure to create a `package.json` first with
|
||||
the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
|
||||
$ npm install express
|
||||
|
||||
Installation is done using the
|
||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||
or
|
||||
|
||||
```bash
|
||||
npm install express
|
||||
```
|
||||
|
||||
Follow [our installing guide](https://expressjs.com/en/starter/installing.html)
|
||||
for more information.
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* Focus on high performance
|
||||
* Super-high test coverage
|
||||
* HTTP helpers (redirection, caching, etc)
|
||||
* View system supporting 14+ template engines
|
||||
* Content negotiation
|
||||
* Executable for generating applications quickly
|
||||
|
||||
## Docs & Community
|
||||
|
||||
* [Website and Documentation](https://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
|
||||
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* [Github Discussions](https://github.com/expressjs/discussions) for discussion on the development and usage of Express
|
||||
|
||||
**PROTIP** Be sure to read the [migration guide to v5](https://expressjs.com/en/guide/migrating-5)
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
|
||||
|
||||
Install the executable. The executable's major version will match Express's:
|
||||
|
||||
```bash
|
||||
npm install -g express-generator@4
|
||||
```
|
||||
|
||||
Create the app:
|
||||
|
||||
```bash
|
||||
express /tmp/foo && cd /tmp/foo
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Start the server:
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
View the website at: http://localhost:3000
|
||||
|
||||
## Philosophy
|
||||
|
||||
The Express philosophy is to provide small, robust tooling for HTTP servers, making
|
||||
it a great solution for single page applications, websites, hybrids, or public
|
||||
HTTP APIs.
|
||||
|
||||
Express does not force you to use any specific ORM or template engine. With support for over
|
||||
14 template engines via [@ladjs/consolidate](https://github.com/ladjs/consolidate),
|
||||
you can quickly craft your perfect framework.
|
||||
Install via git clone:
|
||||
|
||||
$ git clone git://github.com/visionmedia/express.git && cd express && git submodule update --init
|
||||
|
||||
## Examples
|
||||
|
||||
To view the examples, clone the Express repository:
|
||||
Below is a tiny Express application. View the [Wiki](http://wiki.github.com/visionmedia/express/) for detailed information.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/expressjs/express.git --depth 1 && cd express
|
||||
```
|
||||
require.paths.unshift('express/lib')
|
||||
require('express')
|
||||
|
||||
configure(function(){
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/user', function(){
|
||||
this.redirect('/user/' + this.currentUser.id)
|
||||
})
|
||||
|
||||
get('/user/:id', function(id){
|
||||
this.render('user.haml.html', {
|
||||
locals: {
|
||||
user: this.currentUser,
|
||||
usersOnline: Session.store.length()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Then install the dependencies:
|
||||
run()
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
Express uses the [JSpec](http://jspec.info) BDD JavaScript testing
|
||||
framework to write and run elegant spec suites. JSpec is frozen
|
||||
to spec/lib and **does not** require separate installation.
|
||||
|
||||
Then run whichever example you want:
|
||||
$ make test
|
||||
|
||||
Run individual suites:
|
||||
|
||||
```bash
|
||||
node examples/content-negotiation
|
||||
```
|
||||
$ node spec/node.js core
|
||||
$ node spec/node.js mime
|
||||
$ node spec/node.js routing
|
||||
...
|
||||
|
||||
The latest release of Express is compatible with node --version:
|
||||
v0.1.98
|
||||
|
||||
With _EDGE_ Express we do our best to keep up to date with node's _EDGE_
|
||||
|
||||
## More Information
|
||||
|
||||
## Contributing
|
||||
* [JavaScript Extensions & Utilities](http://github.com/visionmedia/ext.js)
|
||||
* [JavaScript Sass](http://github.com/visionmedia/sass.js)
|
||||
* Featured in [Advanced JavaScript e-book](http://www.dev-mag.com/2010/02/18/advanced-javascript/) for only $4
|
||||
* [Express vs Sinatra Benchmarks](http://tjholowaychuk.com/post/543953703/express-vs-sinatra-benchmarks)
|
||||
|
||||
## Contributors
|
||||
|
||||
The Express.js project welcomes all constructive contributions. Contributions take many forms,
|
||||
from code for bug fixes and enhancements, to additions and fixes to documentation, additional
|
||||
tests, triaging incoming pull requests and issues, and more!
|
||||
* TJ Holowaychuk (visionmedia) <tj@vision-media.ca>
|
||||
* Aaron Heckmann (aheckmann) <aaron.heckmann+github@gmail.com>
|
||||
* Ciaran Jessup (ciaranj) <ciaranj@gmail.com>
|
||||
* Gareth Jones (csausdev) <gareth.jones@sensis.com.au>
|
||||
|
||||
## License
|
||||
|
||||
See the [Contributing Guide] for more technical details on contributing.
|
||||
(The MIT License)
|
||||
|
||||
### Security Issues
|
||||
Copyright (c) 2009 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](https://github.com/expressjs/express/security/policy).
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
### Running Tests
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
To run the test suite, first install the dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Then run `npm test`:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
## Current project team members
|
||||
|
||||
For information about the governance of the express.js project, see [GOVERNANCE.md](https://github.com/expressjs/discussions/blob/HEAD/docs/GOVERNANCE.md).
|
||||
|
||||
The original author of Express is [TJ Holowaychuk](https://github.com/tj)
|
||||
|
||||
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
|
||||
|
||||
### TC (Technical Committee)
|
||||
|
||||
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
|
||||
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
|
||||
* [wesleytodd](https://github.com/wesleytodd) - **Wes Todd**
|
||||
* [LinusU](https://github.com/LinusU) - **Linus Unnebäck**
|
||||
* [blakeembrey](https://github.com/blakeembrey) - **Blake Embrey**
|
||||
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
|
||||
* [crandmck](https://github.com/crandmck) - **Rand McKinney**
|
||||
* [ctcpip](https://github.com/ctcpip) - **Chris de Almeida**
|
||||
|
||||
<details>
|
||||
<summary>TC emeriti members</summary>
|
||||
|
||||
#### TC emeriti members
|
||||
|
||||
* [dougwilson](https://github.com/dougwilson) - **Douglas Wilson**
|
||||
* [hacksparrow](https://github.com/hacksparrow) - **Hage Yaapa**
|
||||
* [jonathanong](https://github.com/jonathanong) - **jongleberry**
|
||||
* [niftylettuce](https://github.com/niftylettuce) - **niftylettuce**
|
||||
* [troygoode](https://github.com/troygoode) - **Troy Goode**
|
||||
</details>
|
||||
|
||||
|
||||
### Triagers
|
||||
|
||||
* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**
|
||||
* [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran**
|
||||
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
|
||||
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
|
||||
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
|
||||
* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)
|
||||
* [Phillip9587](https://github.com/Phillip9587) - **Phillip Barta**
|
||||
* [efekrskl](https://github.com/efekrskl) - **Efe Karasakal**
|
||||
* [rxmarbles](https://github.com/rxmarbles) - **Rick Markins** (he/him)
|
||||
* [krzysdz](https://github.com/krzysdz)
|
||||
* [GroophyLifefor](https://github.com/GroophyLifefor) - **Murat Kirazkaya**
|
||||
|
||||
<details>
|
||||
<summary>Triagers emeriti members</summary>
|
||||
|
||||
#### Emeritus Triagers
|
||||
|
||||
* [AuggieH](https://github.com/AuggieH) - **Auggie Hudak**
|
||||
* [G-Rath](https://github.com/G-Rath) - **Gareth Jones**
|
||||
* [MohammadXroid](https://github.com/MohammadXroid) - **Mohammad Ayashi**
|
||||
* [NawafSwe](https://github.com/NawafSwe) - **Nawaf Alsharqi**
|
||||
* [NotMoni](https://github.com/NotMoni) - **Moni**
|
||||
* [VigneshMurugan](https://github.com/VigneshMurugan) - **Vignesh Murugan**
|
||||
* [davidmashe](https://github.com/davidmashe) - **David Ashe**
|
||||
* [digitaIfabric](https://github.com/digitaIfabric) - **David**
|
||||
* [e-l-i-s-e](https://github.com/e-l-i-s-e) - **Elise Bonner**
|
||||
* [fed135](https://github.com/fed135) - **Frederic Charette**
|
||||
* [firmanJS](https://github.com/firmanJS) - **Firman Abdul Hakim**
|
||||
* [getspooky](https://github.com/getspooky) - **Yasser Ameur**
|
||||
* [ghinks](https://github.com/ghinks) - **Glenn**
|
||||
* [ghousemohamed](https://github.com/ghousemohamed) - **Ghouse Mohamed**
|
||||
* [gireeshpunathil](https://github.com/gireeshpunathil) - **Gireesh Punathil**
|
||||
* [jake32321](https://github.com/jake32321) - **Jake Reed**
|
||||
* [jonchurch](https://github.com/jonchurch) - **Jon Church**
|
||||
* [lekanikotun](https://github.com/lekanikotun) - **Troy Goode**
|
||||
* [marsonya](https://github.com/marsonya) - **Lekan Ikotun**
|
||||
* [mastermatt](https://github.com/mastermatt) - **Matt R. Wilson**
|
||||
* [maxakuru](https://github.com/maxakuru) - **Max Edell**
|
||||
* [mlrawlings](https://github.com/mlrawlings) - **Michael Rawlings**
|
||||
* [rodion-arr](https://github.com/rodion-arr) - **Rodion Abdurakhimov**
|
||||
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
|
||||
* [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav**
|
||||
* [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe**
|
||||
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
|
||||
* [0ss](https://github.com/0ss) - **Salah**
|
||||
* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)
|
||||
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
|
||||
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
|
||||
* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**
|
||||
* [dpopp07](https://github.com/dpopp07) - **Dustin Popp**
|
||||
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
|
||||
* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[coveralls-image]: https://img.shields.io/coverallsCoverage/github/expressjs/express?branch=master
|
||||
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
|
||||
[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/express/ci.yml?branch=master&label=ci
|
||||
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
|
||||
[npm-downloads-image]: https://img.shields.io/npm/dm/express
|
||||
[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true
|
||||
[npm-url]: https://npmjs.org/package/express
|
||||
[npm-version-image]: https://img.shields.io/npm/v/express
|
||||
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge
|
||||
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/express
|
||||
[Code of Conduct]: https://github.com/expressjs/.github/blob/HEAD/CODE_OF_CONDUCT.md
|
||||
[Contributing Guide]: https://github.com/expressjs/.github/blob/HEAD/CONTRIBUTING.md
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
16
benchmarks/express/haml.js
Normal file
16
benchmarks/express/haml.js
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
//enable('cache view contents')
|
||||
set('root', __dirname)
|
||||
set('views', __dirname + '/../shared')
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.render('page.html.haml')
|
||||
})
|
||||
|
||||
run()
|
||||
16
benchmarks/express/sass.js
Normal file
16
benchmarks/express/sass.js
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
//enable('cache view contents')
|
||||
set('root', __dirname)
|
||||
set('views', __dirname + '/../shared')
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.render('style.css.sass', { layout: false })
|
||||
})
|
||||
|
||||
run()
|
||||
9
benchmarks/express/simple.js
Normal file
9
benchmarks/express/simple.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
|
||||
get('/', function(){
|
||||
return 'Hello World'
|
||||
})
|
||||
|
||||
run()
|
||||
15
benchmarks/express/static.js
Normal file
15
benchmarks/express/static.js
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
use(Static)
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.sendfile('benchmarks/shared/jquery.js')
|
||||
})
|
||||
|
||||
run()
|
||||
15
benchmarks/express/static.large.js
Normal file
15
benchmarks/express/static.large.js
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
use(Static)
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.sendfile('benchmarks/shared/huge.js')
|
||||
})
|
||||
|
||||
run()
|
||||
84
benchmarks/graph
Executable file
84
benchmarks/graph
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
COL=${COL-9}
|
||||
|
||||
#
|
||||
# Log <msg ...>
|
||||
#
|
||||
# <msg ...>
|
||||
#
|
||||
|
||||
log(){
|
||||
echo "... $@"
|
||||
}
|
||||
|
||||
#
|
||||
# Output gnuplot script for line graph.
|
||||
#
|
||||
# <title> <node> <express> <sinatra>
|
||||
#
|
||||
|
||||
function line() {
|
||||
cat <<-EOF
|
||||
set terminal png
|
||||
set output "benchmarks/graphs/$1.png"
|
||||
set title "$1"
|
||||
set size 1,0.7
|
||||
set grid y
|
||||
set key left top
|
||||
set xlabel "request"
|
||||
set ylabel "response time (ms)"
|
||||
plot "benchmarks/$2" using $COL smooth sbezier with lines title "node", \\
|
||||
"benchmarks/$3" using $COL smooth sbezier with lines title "express", \\
|
||||
"benchmarks/$4" using $COL smooth sbezier with lines title "sinatra thin"
|
||||
EOF
|
||||
}
|
||||
|
||||
#
|
||||
# Output gnuplot script for bar graph.
|
||||
#
|
||||
# <title> <node> <express> <sinatra>
|
||||
#
|
||||
|
||||
function bar() {
|
||||
cat <<-EOF
|
||||
set terminal png
|
||||
set output "benchmarks/graphs/$1.rps.png"
|
||||
set title "$1"
|
||||
set size 0.7,0.5
|
||||
set grid y
|
||||
set key left top
|
||||
set ylabel "requests per second"
|
||||
plot "benchmarks/$1.rps.dat" using 2: xtic(1) with histogram title ""
|
||||
EOF
|
||||
}
|
||||
|
||||
mkdir -p benchmarks/graphs
|
||||
|
||||
for type in simple haml sass static static.large; do
|
||||
plot=benchmarks/graphs/$type.p
|
||||
log generating benchmarks/graphs/$type.png
|
||||
line $type \
|
||||
node/$type.js.dat \
|
||||
express/$type.js.dat \
|
||||
thin/$type.ru.dat \
|
||||
> $plot
|
||||
gnuplot $plot
|
||||
|
||||
log generating benchmarks/graphs/$type.rps.png
|
||||
plot=benchmarks/graphs/$type.rps.p
|
||||
dat=benchmarks/$type.rps.dat
|
||||
:> $dat
|
||||
for server in node express thin; do
|
||||
case $server in
|
||||
node|express) ext=js ;;
|
||||
thin) ext=ru ;;
|
||||
esac
|
||||
rps=$(cat benchmarks/$server/$type.$ext.out | grep "Requests per second:" | awk '{ print $4 }')
|
||||
echo $server $rps >> $dat
|
||||
done
|
||||
bar $type > $plot
|
||||
gnuplot $plot
|
||||
done
|
||||
|
||||
rm benchmarks/graphs/*.p
|
||||
8
benchmarks/node/simple.js
Normal file
8
benchmarks/node/simple.js
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
var fs = require('fs'),
|
||||
http = require('http')
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Length': 11 })
|
||||
res.end('Hello World', 'ascii')
|
||||
}).listen(3000, 'localhost')
|
||||
14
benchmarks/node/static.js
Normal file
14
benchmarks/node/static.js
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
var fs = require('fs'),
|
||||
http = require('http')
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain', 'Transfer-Encoding': 'chunked' })
|
||||
fs.createReadStream('benchmarks/shared/jquery.js')
|
||||
.addListener('data', function(data){
|
||||
res.write(data, 'binary')
|
||||
})
|
||||
.addListener('end', function(){
|
||||
res.end()
|
||||
})
|
||||
}).listen(3000, 'localhost')
|
||||
14
benchmarks/node/static.large.js
Normal file
14
benchmarks/node/static.large.js
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
var fs = require('fs'),
|
||||
http = require('http')
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain', 'Transfer-Encoding': 'chunked' })
|
||||
fs.createReadStream('benchmarks/shared/huge.js')
|
||||
.addListener('data', function(data){
|
||||
res.write(data, 'binary')
|
||||
})
|
||||
.addListener('end', function(){
|
||||
res.end()
|
||||
})
|
||||
}).listen(3000, 'localhost')
|
||||
61
benchmarks/run
Executable file
61
benchmarks/run
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SLEEP=${SLEEP-2}
|
||||
ABFLAGS=${ABFLAGS-"-n 2000 -c 50"}
|
||||
ADDR=${ADDR-http://127.0.0.1:3000/}
|
||||
AB=${AB-ab}
|
||||
|
||||
#
|
||||
# Log <msg ...>
|
||||
#
|
||||
# <msg ...>
|
||||
#
|
||||
|
||||
log(){
|
||||
echo "... $@"
|
||||
}
|
||||
|
||||
#
|
||||
# Benchmark <type> and <file>
|
||||
#
|
||||
# - starts the server
|
||||
# - allows $SLEEP seconds for startup
|
||||
# - runs $AB
|
||||
# - kills the server process
|
||||
#
|
||||
# <type> <file>
|
||||
#
|
||||
|
||||
bm(){
|
||||
local type=$1
|
||||
local file=$2
|
||||
log benchmarking $type $file
|
||||
case $type in
|
||||
node|express)
|
||||
node benchmarks/$type/$file &
|
||||
;;
|
||||
thin)
|
||||
thin -R benchmarks/thin/$file -p 3000 start &
|
||||
;;
|
||||
esac
|
||||
pid=$!
|
||||
sleep $SLEEP
|
||||
$AB $ABFLAGS -g benchmarks/$type/$file.dat $ADDR > benchmarks/$type/$file.out
|
||||
log $(cat benchmarks/$type/$file.out | grep Requests)
|
||||
kill -KILL $pid
|
||||
}
|
||||
|
||||
log ab $ABFLAGS $ADDR
|
||||
bm node simple.js
|
||||
bm node static.js
|
||||
bm node static.large.js
|
||||
bm express simple.js
|
||||
bm express static.js
|
||||
bm express static.large.js
|
||||
bm express haml.js
|
||||
bm express sass.js
|
||||
bm thin simple.ru
|
||||
bm thin static.ru
|
||||
bm thin static.large.ru
|
||||
bm thin haml.ru
|
||||
bm thin sass.ru
|
||||
145
benchmarks/shared/huge.js
Normal file
145
benchmarks/shared/huge.js
Normal file
File diff suppressed because one or more lines are too long
19
benchmarks/shared/jquery.js
vendored
Normal file
19
benchmarks/shared/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
benchmarks/shared/layout.haml
Normal file
6
benchmarks/shared/layout.haml
Normal file
@@ -0,0 +1,6 @@
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%title Wahoo
|
||||
%body
|
||||
#primary= yield
|
||||
6
benchmarks/shared/layout.html.haml
Normal file
6
benchmarks/shared/layout.html.haml
Normal file
@@ -0,0 +1,6 @@
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%title Wahoo
|
||||
%body
|
||||
#primary!= body
|
||||
10
benchmarks/shared/page.haml
Normal file
10
benchmarks/shared/page.haml
Normal file
@@ -0,0 +1,10 @@
|
||||
%h1 Some title
|
||||
%ul
|
||||
%li a
|
||||
%li b
|
||||
%li c
|
||||
%li
|
||||
%ol
|
||||
%li d
|
||||
%li e
|
||||
%li f
|
||||
10
benchmarks/shared/page.html.haml
Normal file
10
benchmarks/shared/page.html.haml
Normal file
@@ -0,0 +1,10 @@
|
||||
%h1 Some title
|
||||
%ul
|
||||
%li a
|
||||
%li b
|
||||
%li c
|
||||
%li
|
||||
%ol
|
||||
%li d
|
||||
%li e
|
||||
%li f
|
||||
73
benchmarks/shared/style.css.sass
Normal file
73
benchmarks/shared/style.css.sass
Normal file
@@ -0,0 +1,73 @@
|
||||
body
|
||||
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
|
||||
:font-size 13px
|
||||
:text-align center
|
||||
:color #555
|
||||
|
||||
h1, h2
|
||||
:margin 0
|
||||
:font-size 22px
|
||||
:color #343434
|
||||
h1
|
||||
:text-shadow 1px 2px 2px #ddd
|
||||
:font-size 60px
|
||||
|
||||
img.bubble
|
||||
:position absolute
|
||||
:top -25px
|
||||
:left 120px
|
||||
|
||||
#wrapper
|
||||
:position relative
|
||||
:margin 100px auto
|
||||
:width 500px
|
||||
:text-align left
|
||||
|
||||
ul
|
||||
:margin 0
|
||||
:padding 0
|
||||
:max-height 300px
|
||||
:overflow-x hidden
|
||||
li
|
||||
:margin 5px 0
|
||||
:padding 3px 8px
|
||||
:list-style none
|
||||
:border 1px solid #eee
|
||||
li:hover
|
||||
:cursor pointer
|
||||
:color #2E2E2E
|
||||
|
||||
input[type=text]
|
||||
:padding 5px
|
||||
:border 1px solid #ddd
|
||||
:outline none
|
||||
input[type=text]:focus
|
||||
:border-color #00C3FF
|
||||
|
||||
input[type=submit]
|
||||
:padding 6px 10px
|
||||
:border solid 1px #999
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
|
||||
:color #333
|
||||
:text-decoration none
|
||||
:cursor pointer
|
||||
:display inline-block
|
||||
:text-align center
|
||||
:text-shadow 0px 1px 1px #fff
|
||||
:line-height 1
|
||||
input[type=submit]:hover
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
|
||||
input[type=submit]:active
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
|
||||
input[name=name]
|
||||
:width 80px
|
||||
|
||||
a
|
||||
:color #1ABFF1
|
||||
a:hover
|
||||
:padding 0 5px
|
||||
a:hover:before
|
||||
:content 'visit: '
|
||||
|
||||
#online
|
||||
:font-size 12px
|
||||
14
benchmarks/thin/haml.ru
Normal file
14
benchmarks/thin/haml.ru
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
require 'rubygems'
|
||||
require 'sinatra'
|
||||
require 'haml'
|
||||
|
||||
configure do
|
||||
set 'views', File.dirname(__FILE__) + '/../shared'
|
||||
end
|
||||
|
||||
get '/' do
|
||||
haml :page, :ugly => true
|
||||
end
|
||||
|
||||
run Sinatra::Application
|
||||
14
benchmarks/thin/sass.ru
Normal file
14
benchmarks/thin/sass.ru
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
require 'rubygems'
|
||||
require 'sinatra'
|
||||
require 'sass'
|
||||
|
||||
configure do
|
||||
set 'views', File.dirname(__FILE__) + '/../shared'
|
||||
end
|
||||
|
||||
get '/' do
|
||||
sass :'style.css'
|
||||
end
|
||||
|
||||
run Sinatra::Application
|
||||
9
benchmarks/thin/simple.ru
Normal file
9
benchmarks/thin/simple.ru
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
require 'rubygems'
|
||||
require 'sinatra'
|
||||
|
||||
get '/' do
|
||||
'Hello World'
|
||||
end
|
||||
|
||||
run Sinatra::Application
|
||||
9
benchmarks/thin/static.large.ru
Normal file
9
benchmarks/thin/static.large.ru
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
require 'rubygems'
|
||||
require 'sinatra'
|
||||
|
||||
get '/' do
|
||||
send_file 'benchmarks/shared/huge.js'
|
||||
end
|
||||
|
||||
run Sinatra::Application
|
||||
9
benchmarks/thin/static.ru
Normal file
9
benchmarks/thin/static.ru
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
require 'rubygems'
|
||||
require 'sinatra'
|
||||
|
||||
get '/' do
|
||||
send_file 'benchmarks/shared/jquery.js'
|
||||
end
|
||||
|
||||
run Sinatra::Application
|
||||
@@ -1,29 +0,0 @@
|
||||
# Express examples
|
||||
|
||||
This page contains list of examples using Express.
|
||||
|
||||
- [auth](./auth) - Authentication with login and password
|
||||
- [content-negotiation](./content-negotiation) - HTTP content negotiation
|
||||
- [cookie-sessions](./cookie-sessions) - Working with cookie-based sessions
|
||||
- [cookies](./cookies) - Working with cookies
|
||||
- [downloads](./downloads) - Transferring files to client
|
||||
- [ejs](./ejs) - Working with Embedded JavaScript templating (ejs)
|
||||
- [error-pages](./error-pages) - Creating error pages
|
||||
- [error](./error) - Working with error middleware
|
||||
- [hello-world](./hello-world) - Simple request handler
|
||||
- [markdown](./markdown) - Markdown as template engine
|
||||
- [multi-router](./multi-router) - Working with multiple Express routers
|
||||
- [mvc](./mvc) - MVC-style controllers
|
||||
- [online](./online) - Tracking online user activity with `online` and `redis` packages
|
||||
- [params](./params) - Working with route parameters
|
||||
- [resource](./resource) - Multiple HTTP operations on the same resource
|
||||
- [route-map](./route-map) - Organizing routes using a map
|
||||
- [route-middleware](./route-middleware) - Working with route middleware
|
||||
- [route-separation](./route-separation) - Organizing routes per each resource
|
||||
- [search](./search) - Search API
|
||||
- [session](./session) - User sessions
|
||||
- [static-files](./static-files) - Serving static files
|
||||
- [vhost](./vhost) - Working with virtual hosts
|
||||
- [view-constructor](./view-constructor) - Rendering views dynamically
|
||||
- [view-locals](./view-locals) - Saving data in request object between middleware calls
|
||||
- [web-service](./web-service) - Simple API service
|
||||
@@ -1,134 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var hash = require('pbkdf2-password')()
|
||||
var path = require('node:path');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// config
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(express.urlencoded())
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'shhhh, very secret'
|
||||
}));
|
||||
|
||||
// Session-persisted message middleware
|
||||
|
||||
app.use(function(req, res, next){
|
||||
var err = req.session.error;
|
||||
var msg = req.session.success;
|
||||
delete req.session.error;
|
||||
delete req.session.success;
|
||||
res.locals.message = '';
|
||||
if (err) res.locals.message = '<p class="msg error">' + err + '</p>';
|
||||
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>';
|
||||
next();
|
||||
});
|
||||
|
||||
// dummy database
|
||||
|
||||
var users = {
|
||||
tj: { name: 'tj' }
|
||||
};
|
||||
|
||||
// when you create a user, generate a salt
|
||||
// and hash the password ('foobar' is the pass here)
|
||||
|
||||
hash({ password: 'foobar' }, function (err, pass, salt, hash) {
|
||||
if (err) throw err;
|
||||
// store the salt & hash in the "db"
|
||||
users.tj.salt = salt;
|
||||
users.tj.hash = hash;
|
||||
});
|
||||
|
||||
|
||||
// Authenticate using our plain-object database of doom!
|
||||
|
||||
function authenticate(name, pass, fn) {
|
||||
if (!module.parent) console.log('authenticating %s:%s', name, pass);
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(null, null)
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the hash against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
|
||||
if (err) return fn(err);
|
||||
if (hash === user.hash) return fn(null, user)
|
||||
fn(null, null)
|
||||
});
|
||||
}
|
||||
|
||||
function restrict(req, res, next) {
|
||||
if (req.session.user) {
|
||||
next();
|
||||
} else {
|
||||
req.session.error = 'Access denied!';
|
||||
res.redirect('/login');
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, function(req, res){
|
||||
res.send('Wahoo! restricted area, click to <a href="/logout">logout</a>');
|
||||
});
|
||||
|
||||
app.get('/logout', function(req, res){
|
||||
// destroy the user's session to log them out
|
||||
// will be re-created next request
|
||||
req.session.destroy(function(){
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/login', function(req, res){
|
||||
res.render('login');
|
||||
});
|
||||
|
||||
app.post('/login', function (req, res, next) {
|
||||
if (!req.body) return res.sendStatus(400)
|
||||
authenticate(req.body.username, req.body.password, function(err, user){
|
||||
if (err) return next(err)
|
||||
if (user) {
|
||||
// Regenerate session when signing in
|
||||
// to prevent fixation
|
||||
req.session.regenerate(function(){
|
||||
// Store the user's primary key
|
||||
// in the session store to be retrieved,
|
||||
// or in this case the entire user object
|
||||
req.session.user = user;
|
||||
req.session.success = 'Authenticated as ' + user.name
|
||||
+ ' click to <a href="/logout">logout</a>. '
|
||||
+ ' You may now access <a href="/restricted">/restricted</a>.';
|
||||
res.redirect(req.get('Referrer') || '/');
|
||||
});
|
||||
} else {
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('/login');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title><%= title %></title>
|
||||
<style>
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 13px Helvetica, Arial, sans-serif;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
.success {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
<%- include('head', { title: 'Authentication Example' }) -%>
|
||||
|
||||
<h1>Login</h1>
|
||||
<%- message %>
|
||||
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
|
||||
<form method="post" action="/login">
|
||||
<p>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" name="username" id="username">
|
||||
</p>
|
||||
<p>
|
||||
<label for="password">Password:</label>
|
||||
<input type="text" name="password" id="password">
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="Login">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<%- include('foot') -%>
|
||||
80
examples/chat/app.js
Normal file
80
examples/chat/app.js
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
var messages = [],
|
||||
utils = require('express/utils'),
|
||||
http = require('express/http')
|
||||
|
||||
configure(function(){
|
||||
use(Logger)
|
||||
use(MethodOverride)
|
||||
use(ContentLength)
|
||||
use(Cookie)
|
||||
use(Cache, { lifetime: (5).minutes, reapInterval: (1).minute })
|
||||
use(Session, { lifetime: (15).minutes, reapInterval: (1).minute })
|
||||
use(Static)
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.pass('/chat')
|
||||
})
|
||||
|
||||
get('/chat', function(){
|
||||
var self = this
|
||||
Session.store.length(function(err, len){
|
||||
self.render('chat.html.haml', {
|
||||
locals: {
|
||||
title: 'Chat',
|
||||
messages: messages,
|
||||
name: self.session.name,
|
||||
usersOnline: len
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
post('/chat', function(){
|
||||
this.session.name = this.param('name')
|
||||
messages
|
||||
.push(utils.escape(this.param('name')) + ': ' + utils.escape(this.param('message'))
|
||||
.replace(/(http:\/\/[^\s]+)/g, '<a href="$1" target="express-chat">$1</a>')
|
||||
.replace(/:\)/g, '<img src="http://icons3.iconfinder.netdna-cdn.com/data/icons/ledicons/emoticon_smile.png">'))
|
||||
this.respond(200)
|
||||
})
|
||||
|
||||
get('/chat/messages', function(){
|
||||
var self = this,
|
||||
previousLength = messages.length,
|
||||
timer = setInterval(function(){
|
||||
if (messages.length > previousLength)
|
||||
self.contentType('json'),
|
||||
previousLength = messages.length,
|
||||
self.respond(200, JSON.encode(messages)),
|
||||
clearInterval(timer)
|
||||
}, 100)
|
||||
})
|
||||
|
||||
get('/*.css', function(file){
|
||||
this.render(file + '.css.sass', { layout: false })
|
||||
})
|
||||
|
||||
get('/error/view', function(){
|
||||
this.render('does.not.exist')
|
||||
})
|
||||
|
||||
get('/error', function(){
|
||||
throw new Error('oh noes!')
|
||||
})
|
||||
|
||||
get('/simple', function(){
|
||||
return 'Hello :)'
|
||||
})
|
||||
|
||||
get('/favicon.ico', function(){
|
||||
this.notFound()
|
||||
})
|
||||
|
||||
run()
|
||||
BIN
examples/chat/public/images/bubble.png
Normal file
BIN
examples/chat/public/images/bubble.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
28
examples/chat/public/javascripts/app.js
Normal file
28
examples/chat/public/javascripts/app.js
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
$(function(){
|
||||
// Send message
|
||||
$('form').submit(function(){
|
||||
var message = $('input[name=message]'),
|
||||
name = $('input[name=name]')
|
||||
if (message.val())
|
||||
$.post('/chat', { name: name.val(), message: message.val() }, function(){
|
||||
message.val('')
|
||||
})
|
||||
else
|
||||
message.css('border', '1px solid red')
|
||||
return false
|
||||
})
|
||||
|
||||
// Longpoll
|
||||
;(function poll(){
|
||||
$.getJSON('/chat/messages', function(messages){
|
||||
$('#messages').empty()
|
||||
$.each(messages, function(i, msg){
|
||||
$('#messages')
|
||||
.append('<li>' + msg + '</li>')
|
||||
.get(0).scrollTop = $('#messages').get(0).scrollHeight
|
||||
})
|
||||
poll()
|
||||
})
|
||||
})()
|
||||
})
|
||||
19
examples/chat/public/javascripts/jquery.js
vendored
Normal file
19
examples/chat/public/javascripts/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
examples/chat/views/chat.html.haml
Normal file
9
examples/chat/views/chat.html.haml
Normal file
@@ -0,0 +1,9 @@
|
||||
%h1 Chat
|
||||
%img.bubble{ src: '/public/images/bubble.png' }
|
||||
%ul#messages
|
||||
!= this.partial('message.html.haml', { collection: messages })
|
||||
%form{ method: 'post' }
|
||||
%input{ type: 'hidden', name: '_method', value: 'put' }
|
||||
%input{ type: 'text', name: 'name', value: name || 'guest' }
|
||||
%input{ type: 'text', name: 'message' }
|
||||
%input{ type: 'submit', value: 'Send' }
|
||||
12
examples/chat/views/layout.html.haml
Normal file
12
examples/chat/views/layout.html.haml
Normal file
@@ -0,0 +1,12 @@
|
||||
%html
|
||||
%head
|
||||
%title= title
|
||||
%script{ src: '/public/javascripts/jquery.js' }
|
||||
%script{ src: '/public/javascripts/app.js' }
|
||||
%link{ rel: 'stylesheet', href: '/style.css' }
|
||||
%body
|
||||
#wrapper
|
||||
!= body
|
||||
#online
|
||||
Online:
|
||||
%strong= usersOnline
|
||||
1
examples/chat/views/partials/message.html.haml
Normal file
1
examples/chat/views/partials/message.html.haml
Normal file
@@ -0,0 +1 @@
|
||||
%li= message
|
||||
81
examples/chat/views/style.css.sass
Normal file
81
examples/chat/views/style.css.sass
Normal file
@@ -0,0 +1,81 @@
|
||||
body
|
||||
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
|
||||
:font-size 13px
|
||||
:text-align center
|
||||
=text-stroke 1px rgba(255, 255, 255, 0.1)
|
||||
:color #555
|
||||
|
||||
h1, h2
|
||||
:margin 0
|
||||
:font-size 22px
|
||||
:color #343434
|
||||
h1
|
||||
:text-shadow 1px 2px 2px #ddd
|
||||
:font-size 60px
|
||||
|
||||
img.bubble
|
||||
:position absolute
|
||||
:top -25px
|
||||
:left 120px
|
||||
|
||||
#wrapper
|
||||
:position relative
|
||||
:margin 100px auto
|
||||
:width 500px
|
||||
:text-align left
|
||||
|
||||
ul
|
||||
:margin 0
|
||||
:padding 0
|
||||
:max-height 300px
|
||||
:overflow-x hidden
|
||||
li
|
||||
:margin 5px 0
|
||||
:padding 3px 8px
|
||||
:list-style none
|
||||
:border 1px solid #eee
|
||||
=border-radius 3px
|
||||
=border-radius 3px
|
||||
li:hover
|
||||
:cursor pointer
|
||||
:color #2E2E2E
|
||||
|
||||
input[type=text]
|
||||
:padding 5px
|
||||
:border 1px solid #ddd
|
||||
:outline none
|
||||
=border-radius 2px
|
||||
input[type=text]:focus
|
||||
:border-color #00C3FF
|
||||
|
||||
input[type=submit]
|
||||
=border-radius 2px
|
||||
=box-shadow 0 1px 2px #ddd
|
||||
:padding 6px 10px
|
||||
:border solid 1px #999
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
|
||||
:color #333
|
||||
:text-decoration none
|
||||
:cursor pointer
|
||||
:display inline-block
|
||||
:text-align center
|
||||
:text-shadow 0px 1px 1px #fff
|
||||
:line-height 1
|
||||
input[type=submit]:hover
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
|
||||
input[type=submit]:active
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
|
||||
input[name=name]
|
||||
:width 80px
|
||||
|
||||
a
|
||||
:color #1ABFF1
|
||||
=transition-property padding
|
||||
=transition-duration 0.15s
|
||||
a:hover
|
||||
:padding 0 5px
|
||||
a:hover:before
|
||||
:content 'visit: '
|
||||
|
||||
#online
|
||||
:font-size 12px
|
||||
38
examples/coffee-upload/app.coffee
Normal file
38
examples/coffee-upload/app.coffee
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
require.paths.unshift 'lib'
|
||||
require 'express'
|
||||
require 'express/plugins'
|
||||
|
||||
sys: require 'sys'
|
||||
|
||||
configure ->
|
||||
use MethodOverride
|
||||
use ContentLength
|
||||
use Cookie
|
||||
use Session
|
||||
use Flash
|
||||
use Logger
|
||||
use Static
|
||||
set 'root', __dirname
|
||||
set 'views', __dirname + '/../upload/views'
|
||||
|
||||
get '/', ->
|
||||
@redirect('/upload')
|
||||
|
||||
get '/upload', ->
|
||||
@render 'upload.html.haml', {
|
||||
locals: {
|
||||
flashes: @flash 'info'
|
||||
}
|
||||
}
|
||||
|
||||
post '/upload', ->
|
||||
@param('images').each (image) =>
|
||||
sys.puts image.filename + ' -> ' + image.tempfile
|
||||
@flash 'info', 'Uploaded ' + image.filename
|
||||
@redirect '/upload'
|
||||
|
||||
get '/*.css', (file) ->
|
||||
@render file + '.css.sass', { layout: no }
|
||||
|
||||
run()
|
||||
19
examples/coffee-upload/public/javascripts/jquery.js
vendored
Normal file
19
examples/coffee-upload/public/javascripts/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,9 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
users.push({ name: 'Loki' });
|
||||
users.push({ name: 'Jane' });
|
||||
|
||||
module.exports = users;
|
||||
@@ -1,46 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var users = require('./db');
|
||||
|
||||
// so either you can deal with different types of formatting
|
||||
// for expected response in index.js
|
||||
app.get('/', function(req, res){
|
||||
res.format({
|
||||
html: function(){
|
||||
res.send('<ul>' + users.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('') + '</ul>');
|
||||
},
|
||||
|
||||
text: function(){
|
||||
res.send(users.map(function(user){
|
||||
return ' - ' + user.name + '\n';
|
||||
}).join(''));
|
||||
},
|
||||
|
||||
json: function(){
|
||||
res.json(users);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// or you could write a tiny middleware like
|
||||
// this to add a layer of abstraction
|
||||
// and make things a bit more declarative:
|
||||
|
||||
function format(path) {
|
||||
var obj = require(path);
|
||||
return function(req, res){
|
||||
res.format(obj);
|
||||
};
|
||||
}
|
||||
|
||||
app.get('/users', format('./users'));
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var users = require('./db');
|
||||
|
||||
exports.html = function(req, res){
|
||||
res.send('<ul>' + users.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('') + '</ul>');
|
||||
};
|
||||
|
||||
exports.text = function(req, res){
|
||||
res.send(users.map(function(user){
|
||||
return ' - ' + user.name + '\n';
|
||||
}).join(''));
|
||||
};
|
||||
|
||||
exports.json = function(req, res){
|
||||
res.json(users);
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var cookieSession = require('cookie-session');
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// add req.session cookie support
|
||||
app.use(cookieSession({ secret: 'manny is cool' }));
|
||||
|
||||
// do something with the session
|
||||
app.get('/', function (req, res) {
|
||||
req.session.count = (req.session.count || 0) + 1
|
||||
res.send('viewed ' + req.session.count + ' times\n')
|
||||
})
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
|
||||
// custom log format
|
||||
if (process.env.NODE_ENV !== 'test') app.use(logger(':method :url'))
|
||||
|
||||
// parses request cookies, populating
|
||||
// req.cookies and req.signedCookies
|
||||
// when the secret is passed, used
|
||||
// for signing the cookies.
|
||||
app.use(cookieParser('my secret here'));
|
||||
|
||||
// parses x-www-form-urlencoded
|
||||
app.use(express.urlencoded())
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
res.send('Remembered :). Click to <a href="/forget">forget</a>!.');
|
||||
} else {
|
||||
res.send('<form method="post"><p>Check to <label>'
|
||||
+ '<input type="checkbox" name="remember"/> remember me</label> '
|
||||
+ '<input type="submit" value="Submit"/>.</p></form>');
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/forget', function(req, res){
|
||||
res.clearCookie('remember');
|
||||
res.redirect(req.get('Referrer') || '/');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
var minute = 60000;
|
||||
|
||||
if (req.body && req.body.remember) {
|
||||
res.cookie('remember', 1, { maxAge: minute })
|
||||
}
|
||||
|
||||
res.redirect(req.get('Referrer') || '/');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
Only for test.
|
||||
The file name is faked.
|
||||
@@ -1 +0,0 @@
|
||||
what an amazing download
|
||||
@@ -1,3 +0,0 @@
|
||||
* milk
|
||||
* eggs
|
||||
* bread
|
||||
@@ -1,40 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('node:path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// path to where the files are stored on disk
|
||||
var FILES_DIR = path.join(__dirname, 'files')
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<ul>' +
|
||||
'<li>Download <a href="/files/notes/groceries.txt">notes/groceries.txt</a>.</li>' +
|
||||
'<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>' +
|
||||
'<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>' +
|
||||
'<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>' +
|
||||
'</ul>')
|
||||
});
|
||||
|
||||
// /files/* is accessed via req.params[0]
|
||||
// but here we name it :file
|
||||
app.get('/files/*file', function (req, res, next) {
|
||||
res.download(req.params.file.join('/'), { root: FILES_DIR }, function (err) {
|
||||
if (!err) return; // file sent
|
||||
if (err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
res.statusCode = 404;
|
||||
res.send('Cant find that file, sorry!');
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('node:path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// Register ejs as .html. If we did
|
||||
// not call this, we would need to
|
||||
// name our views foo.ejs instead
|
||||
// of foo.html. The __express method
|
||||
// is simply a function that engines
|
||||
// use to hook into the Express view
|
||||
// system by default, so if we want
|
||||
// to change "foo.ejs" to "foo.html"
|
||||
// we simply pass _any_ function, in this
|
||||
// case `ejs.__express`.
|
||||
|
||||
app.engine('.html', require('ejs').__express);
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// Path to our public directory
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// Without this you would need to
|
||||
// supply the extension to res.render()
|
||||
// ex: res.render('users.html').
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tobi', email: 'tobi@learnboost.com' },
|
||||
{ name: 'loki', email: 'loki@learnboost.com' },
|
||||
{ name: 'jane', email: 'jane@learnboost.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', {
|
||||
users: users,
|
||||
title: "EJS example",
|
||||
header: "Some users"
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
body {
|
||||
padding: 50px 80px;
|
||||
font: 14px "Helvetica Neue", "Lucida Grande", Arial, sans-serif;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,9 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title><%= title %></title>
|
||||
<link rel="stylesheet" href="/stylesheets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -1,10 +0,0 @@
|
||||
<%- include('header.html') -%>
|
||||
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><%= user.name %> <<%= user.email %>></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
|
||||
<%- include('footer.html') -%>
|
||||
@@ -1,103 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('node:path');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var silent = process.env.NODE_ENV === 'test'
|
||||
|
||||
// general config
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// our custom "verbose errors" setting
|
||||
// which we can use in the templates
|
||||
// via settings['verbose errors']
|
||||
app.enable('verbose errors');
|
||||
|
||||
// disable them in production
|
||||
// use $ NODE_ENV=production node examples/error-pages
|
||||
if (app.settings.env === 'production') app.disable('verbose errors')
|
||||
|
||||
silent || app.use(logger('dev'));
|
||||
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.ejs');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res, next){
|
||||
// trigger a 404 since no other middleware
|
||||
// will match /404 after this one, and we're not
|
||||
// responding here
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/403', function(req, res, next){
|
||||
// trigger a 403 error
|
||||
var err = new Error('not allowed!');
|
||||
err.status = 403;
|
||||
next(err);
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res, next){
|
||||
// trigger a generic (500) error
|
||||
next(new Error('keyboard cat!'));
|
||||
});
|
||||
|
||||
// Error handlers
|
||||
|
||||
// Since this is the last non-error-handling
|
||||
// middleware use()d, we assume 404, as nothing else
|
||||
// responded.
|
||||
|
||||
// $ curl http://localhost:3000/notfound
|
||||
// $ curl http://localhost:3000/notfound -H "Accept: application/json"
|
||||
// $ curl http://localhost:3000/notfound -H "Accept: text/plain"
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.status(404);
|
||||
|
||||
res.format({
|
||||
html: function () {
|
||||
res.render('404', { url: req.url })
|
||||
},
|
||||
json: function () {
|
||||
res.json({ error: 'Not found' })
|
||||
},
|
||||
default: function () {
|
||||
res.type('txt').send('Not found')
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// error-handling middleware, take the same form
|
||||
// as regular middleware, however they require an
|
||||
// arity of 4, aka the signature (err, req, res, next).
|
||||
// when connect has an error, it will invoke ONLY error-handling
|
||||
// middleware.
|
||||
|
||||
// If we were to next() here any remaining non-error-handling
|
||||
// middleware would then be executed, or if we next(err) to
|
||||
// continue passing the error, only error-handling middleware
|
||||
// would remain being executed, however here
|
||||
// we simply respond with an error page.
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
// we may use properties of the error object
|
||||
// here and next(err) appropriately, or if
|
||||
// we possibly recovered from the error, simply next().
|
||||
res.status(err.status || 500);
|
||||
res.render('500', { error: err });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<%- include('error_header') -%>
|
||||
<h2>Cannot find <%= url %></h2>
|
||||
<%- include('footer') -%>
|
||||
@@ -1,8 +0,0 @@
|
||||
<%- include('error_header') -%>
|
||||
<h2>Error: <%= error.message %></h2>
|
||||
<% if (settings['verbose errors']) { %>
|
||||
<pre><%= error.stack %></pre>
|
||||
<% } else { %>
|
||||
<p>An error occurred!</p>
|
||||
<% } %>
|
||||
<%- include('footer') -%>
|
||||
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Error</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>An error occurred!</h1>
|
||||
@@ -1,2 +0,0 @@
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Custom Pages Example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>My Site</h1>
|
||||
<h2>Pages Example</h2>
|
||||
|
||||
<ul>
|
||||
<li>visit <a href="/500">500</a></li>
|
||||
<li>visit <a href="/404">404</a></li>
|
||||
<li>visit <a href="/403">403</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,53 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var logger = require('morgan');
|
||||
var app = module.exports = express();
|
||||
var test = app.get('env') === 'test'
|
||||
|
||||
if (!test) app.use(logger('dev'));
|
||||
|
||||
// error handling middleware have an arity of 4
|
||||
// instead of the typical (req, res, next),
|
||||
// otherwise they behave exactly like regular
|
||||
// middleware, you may have several of them,
|
||||
// in different orders etc.
|
||||
|
||||
function error(err, req, res, next) {
|
||||
// log it
|
||||
if (!test) console.error(err.stack);
|
||||
|
||||
// respond with 500 "Internal Server Error".
|
||||
res.status(500);
|
||||
res.send('Internal Server Error');
|
||||
}
|
||||
|
||||
app.get('/', function () {
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
throw new Error('something broke!');
|
||||
});
|
||||
|
||||
app.get('/next', function(req, res, next){
|
||||
// We can also pass exceptions to next()
|
||||
// The reason for process.nextTick() is to show that
|
||||
// next() can be called inside an async operation,
|
||||
// in real life it can be a DB read or HTTP request.
|
||||
process.nextTick(function(){
|
||||
next(new Error('oh no!'));
|
||||
});
|
||||
});
|
||||
|
||||
// the error handler is placed after routes
|
||||
// if it were above it would not receive errors
|
||||
// from app.get() etc
|
||||
app.use(error);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
19
examples/hello-world/app.js
Normal file
19
examples/hello-world/app.js
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
|
||||
configure(function(){
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.render('front.html.ejs', {
|
||||
locals: {
|
||||
title: 'Hello World',
|
||||
name: 'Joe',
|
||||
items: ['one', 'two', 'three']
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
run()
|
||||
@@ -1,15 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express()
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
9
examples/hello-world/views/front.html.ejs
Normal file
9
examples/hello-world/views/front.html.ejs
Normal file
@@ -0,0 +1,9 @@
|
||||
<h1><%= title %></h1>
|
||||
<%= this.partial("greeting.html.ejs", { locals: { name: name } }) %>
|
||||
<% if (items && items.length) { %>
|
||||
<ul>
|
||||
<% for (var i = 0; i < items.length; ++i) { %>
|
||||
<li><%= items[i] %></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
<% } %>
|
||||
8
examples/hello-world/views/layout.html.ejs
Normal file
8
examples/hello-world/views/layout.html.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title><%= title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<%= body %>
|
||||
</body>
|
||||
</html>
|
||||
1
examples/hello-world/views/partials/greeting.html.ejs
Normal file
1
examples/hello-world/views/partials/greeting.html.ejs
Normal file
@@ -0,0 +1 @@
|
||||
Welcome back, <strong><%= name %></strong>!
|
||||
@@ -1,44 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var escapeHtml = require('escape-html');
|
||||
var express = require('../..');
|
||||
var fs = require('node:fs');
|
||||
var marked = require('marked');
|
||||
var path = require('node:path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// register .md as an engine in express view system
|
||||
|
||||
app.engine('md', function(path, options, fn){
|
||||
fs.readFile(path, 'utf8', function(err, str){
|
||||
if (err) return fn(err);
|
||||
var html = marked.parse(str).replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return escapeHtml(options[name] || '');
|
||||
});
|
||||
fn(null, html);
|
||||
});
|
||||
});
|
||||
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// make it the default, so we don't need .md
|
||||
app.set('view engine', 'md');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index', { title: 'Markdown Example' });
|
||||
});
|
||||
|
||||
app.get('/fail', function(req, res){
|
||||
res.render('missing', { title: 'Markdown Example' });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
# {title}
|
||||
|
||||
Just an example view rendered with _markdown_.
|
||||
@@ -1,15 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv1 = express.Router();
|
||||
|
||||
apiv1.get('/', function(req, res) {
|
||||
res.send('Hello from APIv1 root route.');
|
||||
});
|
||||
|
||||
apiv1.get('/users', function(req, res) {
|
||||
res.send('List of APIv1 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv1;
|
||||
@@ -1,15 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv2 = express.Router();
|
||||
|
||||
apiv2.get('/', function(req, res) {
|
||||
res.send('Hello from APIv2 root route.');
|
||||
});
|
||||
|
||||
apiv2.get('/users', function(req, res) {
|
||||
res.send('List of APIv2 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv2;
|
||||
@@ -1,18 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use('/api/v1', require('./controllers/api_v1'));
|
||||
app.use('/api/v2', require('./controllers/api_v2'));
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.send('Hello from root route.')
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'ejs';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next('route');
|
||||
req.pet = pet;
|
||||
next();
|
||||
};
|
||||
|
||||
exports.show = function(req, res, next){
|
||||
res.render('show', { pet: req.pet });
|
||||
};
|
||||
|
||||
exports.edit = function(req, res, next){
|
||||
res.render('edit', { pet: req.pet });
|
||||
};
|
||||
|
||||
exports.update = function(req, res, next){
|
||||
var body = req.body;
|
||||
req.pet.name = body.pet.name;
|
||||
res.message('Information updated!');
|
||||
res.redirect('/pet/' + req.pet.id);
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit <%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %></h1>
|
||||
<form action="/pet/<%= pet.id %>?_method=put" method="post">
|
||||
<label>Name: <input type="text" name="pet[name]" value="<%= pet.name %>"></label>
|
||||
<input type="submit" value="Update">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title><%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %> <a href="/pet/<%= pet.id %>/edit">edit</a></h1>
|
||||
|
||||
<p>You are viewing <%= pet.name %></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,22 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.name = 'pet';
|
||||
exports.prefix = '/user/:user_id';
|
||||
|
||||
exports.create = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
var user = db.users[id];
|
||||
var body = req.body;
|
||||
if (!user) return next('route');
|
||||
var pet = { name: body.pet.name };
|
||||
pet.id = db.pets.push(pet) - 1;
|
||||
user.pets.push(pet);
|
||||
res.message('Added pet ' + body.pet.name);
|
||||
res.redirect('/user/' + id);
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'hbs';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
if (!id) return next();
|
||||
// pretend to query a database...
|
||||
process.nextTick(function(){
|
||||
req.user = db.users[id];
|
||||
// cant find that user
|
||||
if (!req.user) return next('route');
|
||||
// found it, move on to the routes
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
exports.list = function(req, res, next){
|
||||
res.render('list', { users: db.users });
|
||||
};
|
||||
|
||||
exports.edit = function(req, res, next){
|
||||
res.render('edit', { user: req.user });
|
||||
};
|
||||
|
||||
exports.show = function(req, res, next){
|
||||
res.render('show', { user: req.user });
|
||||
};
|
||||
|
||||
exports.update = function(req, res, next){
|
||||
var body = req.body;
|
||||
req.user.name = body.user.name;
|
||||
res.message('Information updated!');
|
||||
res.redirect('/user/' + req.user.id);
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit {{user.name}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{user.name}}</h1>
|
||||
<form action="/user/{{user.id}}?_method=put" method="post">
|
||||
<label for="user[name]">Name:
|
||||
<input type="text" name="user[name]" value="{{user.name}}">
|
||||
</label>
|
||||
|
||||
<input type="submit" name="submit" value="Update">
|
||||
</form>
|
||||
|
||||
<form action="/user/{{user.id}}/pet" method="post">
|
||||
<label for="pet[name]">Pet:
|
||||
<input type="text" name="pet[name]" placeholder="Pet Name">
|
||||
</label>
|
||||
|
||||
<input type="submit" name="submit" value="Add">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Users</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Users</h1>
|
||||
<p>Click a user below to view their pets.</p>
|
||||
<ul>
|
||||
{{#each users}}
|
||||
<li><a href="/user/{{id}}">{{name}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,31 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>{{user.name}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{user.name}} <a href="/user/{{user.id}}/edit">edit</a></h1>
|
||||
|
||||
{{#if hasMessages}}
|
||||
<ul>
|
||||
{{#each messages}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if user.pets.length}}
|
||||
<p>View {{user.name}}'s pets:</p>
|
||||
<ul>
|
||||
{{#each user.pets}}
|
||||
<li><a href="/pet/{{id}}">{{name}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p>No pets!</p>
|
||||
{{/if}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// faux database
|
||||
|
||||
var pets = exports.pets = [];
|
||||
|
||||
pets.push({ name: 'Tobi', id: 0 });
|
||||
pets.push({ name: 'Loki', id: 1 });
|
||||
pets.push({ name: 'Jane', id: 2 });
|
||||
pets.push({ name: 'Raul', id: 3 });
|
||||
|
||||
var users = exports.users = [];
|
||||
|
||||
users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 });
|
||||
users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 });
|
||||
users.push({ name: 'Nathan', pets: [], id: 2 });
|
||||
@@ -1,95 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var path = require('node:path');
|
||||
var session = require('express-session');
|
||||
var methodOverride = require('method-override');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// set our default template engine to "ejs"
|
||||
// which prevents the need for using file extensions
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// set views for error and 404 pages
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// define a custom res.message() method
|
||||
// which stores messages in the session
|
||||
app.response.message = function(msg){
|
||||
// reference `req.session` via the `this.req` reference
|
||||
var sess = this.req.session;
|
||||
// simply add the msg to an array for later
|
||||
sess.messages = sess.messages || [];
|
||||
sess.messages.push(msg);
|
||||
return this;
|
||||
};
|
||||
|
||||
// log
|
||||
if (!module.parent) app.use(logger('dev'));
|
||||
|
||||
// serve static files
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// session support
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'some secret here'
|
||||
}));
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
|
||||
// allow overriding methods in query (?_method=put)
|
||||
app.use(methodOverride('_method'));
|
||||
|
||||
// expose the "messages" local variable when views are rendered
|
||||
app.use(function(req, res, next){
|
||||
var msgs = req.session.messages || [];
|
||||
|
||||
// expose "messages" local variable
|
||||
res.locals.messages = msgs;
|
||||
|
||||
// expose "hasMessages"
|
||||
res.locals.hasMessages = !! msgs.length;
|
||||
|
||||
/* This is equivalent:
|
||||
res.locals({
|
||||
messages: msgs,
|
||||
hasMessages: !! msgs.length
|
||||
});
|
||||
*/
|
||||
|
||||
next();
|
||||
// empty or "flush" the messages so they
|
||||
// don't build up
|
||||
req.session.messages = [];
|
||||
});
|
||||
|
||||
// load controllers
|
||||
require('./lib/boot')(app, { verbose: !module.parent });
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
// log it
|
||||
if (!module.parent) console.error(err.stack);
|
||||
|
||||
// error page
|
||||
res.status(500).render('5xx');
|
||||
});
|
||||
|
||||
// assume 404 since no middleware responded
|
||||
app.use(function(req, res, next){
|
||||
res.status(404).render('404', { url: req.originalUrl });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../..');
|
||||
var fs = require('node:fs');
|
||||
var path = require('node:path');
|
||||
|
||||
module.exports = function(parent, options){
|
||||
var dir = path.join(__dirname, '..', 'controllers');
|
||||
var verbose = options.verbose;
|
||||
fs.readdirSync(dir).forEach(function(name){
|
||||
var file = path.join(dir, name)
|
||||
if (!fs.statSync(file).isDirectory()) return;
|
||||
verbose && console.log('\n %s:', name);
|
||||
var obj = require(file);
|
||||
var name = obj.name || name;
|
||||
var prefix = obj.prefix || '';
|
||||
var app = express();
|
||||
var handler;
|
||||
var method;
|
||||
var url;
|
||||
|
||||
// allow specifying the view engine
|
||||
if (obj.engine) app.set('view engine', obj.engine);
|
||||
app.set('views', path.join(__dirname, '..', 'controllers', name, 'views'));
|
||||
|
||||
// generate routes based
|
||||
// on the exported methods
|
||||
for (var key in obj) {
|
||||
// "reserved" exports
|
||||
if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue;
|
||||
// route exports
|
||||
switch (key) {
|
||||
case 'show':
|
||||
method = 'get';
|
||||
url = '/' + name + '/:' + name + '_id';
|
||||
break;
|
||||
case 'list':
|
||||
method = 'get';
|
||||
url = '/' + name + 's';
|
||||
break;
|
||||
case 'edit':
|
||||
method = 'get';
|
||||
url = '/' + name + '/:' + name + '_id/edit';
|
||||
break;
|
||||
case 'update':
|
||||
method = 'put';
|
||||
url = '/' + name + '/:' + name + '_id';
|
||||
break;
|
||||
case 'create':
|
||||
method = 'post';
|
||||
url = '/' + name;
|
||||
break;
|
||||
case 'index':
|
||||
method = 'get';
|
||||
url = '/';
|
||||
break;
|
||||
default:
|
||||
/* istanbul ignore next */
|
||||
throw new Error('unrecognized route: ' + name + '.' + key);
|
||||
}
|
||||
|
||||
// setup
|
||||
handler = obj[key];
|
||||
url = prefix + url;
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
app[method](url, obj.before, handler);
|
||||
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), url, key);
|
||||
} else {
|
||||
app[method](url, handler);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), url, key);
|
||||
}
|
||||
}
|
||||
|
||||
// mount the app
|
||||
parent.use(app);
|
||||
});
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #107aff;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
h1 a {
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Not Found</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>404: Not Found</h1>
|
||||
<p>Sorry we can't find <%= url %></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Internal Server Error</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>500: Internal Server Error</h1>
|
||||
<p>Looks like something blew up!</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,61 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
// then:
|
||||
// $ npm install redis online
|
||||
// $ redis-server
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var online = require('online');
|
||||
var redis = require('redis');
|
||||
var db = redis.createClient();
|
||||
|
||||
// online
|
||||
|
||||
online = online(db);
|
||||
|
||||
// app
|
||||
|
||||
var app = express();
|
||||
|
||||
// activity tracking, in this case using
|
||||
// the UA string, you would use req.user.id etc
|
||||
|
||||
app.use(function(req, res, next){
|
||||
// fire-and-forget
|
||||
online.add(req.headers['user-agent']);
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* List helper.
|
||||
*/
|
||||
|
||||
function list(ids) {
|
||||
return '<ul>' + ids.map(function(id){
|
||||
return '<li>' + id + '</li>';
|
||||
}).join('') + '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* GET users online.
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res, next){
|
||||
online.last(5, function(err, ids){
|
||||
if (err) return next(err);
|
||||
res.send('<p>Users online: ' + ids.length + '</p>' + list(ids));
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user