Missing content!
{% endblock %}Repository: koajs/examples Branch: master Commit: 40c77dbecec6 Files: 62 Total size: 50.1 KB Directory structure: gitextract_ti2c8rw1/ ├── .editorconfig ├── .eslintrc.yml ├── .gitignore ├── .jshintignore ├── .travis.yml ├── 404/ │ ├── app.js │ └── test.js ├── Readme.md ├── base-auth/ │ ├── app.js │ └── test.js ├── blog/ │ ├── app.js │ ├── lib/ │ │ └── render.js │ ├── test.js │ └── views/ │ ├── index.html │ ├── layout.html │ ├── list.html │ ├── new.html │ └── show.html ├── body-parsing/ │ ├── app.js │ └── test.js ├── compose/ │ ├── app.js │ └── test.js ├── conditional-middleware/ │ └── app.js ├── cookies/ │ ├── app.js │ └── test.js ├── csrf/ │ ├── app.js │ └── test.js ├── errors/ │ ├── app.js │ └── test.js ├── flash-messages/ │ ├── app.js │ └── test.js ├── hello-world/ │ ├── app.js │ └── test.js ├── multipart/ │ ├── app.js │ └── test.js ├── negotiation/ │ ├── app.js │ └── test.js ├── package.json ├── stream-file/ │ ├── README.md │ ├── app.js │ └── test.js ├── stream-objects/ │ ├── README.md │ ├── app.js │ └── test.js ├── stream-server-side-events/ │ ├── README.md │ ├── app.js │ ├── db.js │ └── sse.js ├── stream-view/ │ ├── README.md │ ├── app.js │ ├── test.js │ └── view.js ├── templates/ │ ├── app.js │ ├── test.js │ └── views/ │ └── user.ejs ├── upload/ │ ├── app.js │ └── public/ │ ├── 404.html │ └── index.html └── vhost/ ├── app.js ├── apps/ │ ├── array.js │ └── koa.js └── test.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false [Makefile] indent_style = tab ================================================ FILE: .eslintrc.yml ================================================ env: mocha: true extends: koa rules: no-var: 0 prefer-arrow-callback: 0 ================================================ FILE: .gitignore ================================================ # Compiled source # ################### *.com *.class *.dll *.exe *.o *.so # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods *.7z *.dmg *.gz *.iso *.jar *.rar *.tar *.zip # Logs and databases # ###################### *.log *.sql *.sqlite # OS generated files # ###################### .DS_Store* ehthumbs.db Icon? Thumbs.db .idea # Node.js # ########### lib-cov *.seed *.log *.csv *.dat *.out *.pid *.gz pids logs results node_modules npm-debug.log # Components # ############## /build /components # ImageMagick # ############### *.cache *.mpc # Other # ######### test/*.2 test/*/*.2 test/*.mp4 test/images/originalSideways.jpg.2 # Traceur output # ################## out/* ================================================ FILE: .jshintignore ================================================ node_modules */test.js ================================================ FILE: .travis.yml ================================================ node_js: - stable - 8 language: node_js script: - npm run lint - npm test ================================================ FILE: 404/app.js ================================================ const Koa = require('koa'); const app = module.exports = new Koa(); app.use(async function pageNotFound(ctx) { // we need to explicitly set 404 here // so that koa doesn't assign 200 on body= ctx.status = 404; switch (ctx.accepts('html', 'json')) { case 'html': ctx.type = 'html'; ctx.body = '
Page Not Found
'; break; case 'json': ctx.body = { message: 'Page Not Found' }; break; default: ctx.type = 'text'; ctx.body = 'Page Not Found'; } }); if (!module.parent) app.listen(3000); ================================================ FILE: 404/test.js ================================================ const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('404', function() { after(function() { server.close(); }); describe('when GET /', function() { it('should return the 404 page', function(done) { request .get('/') .expect(404) .expect(/Page Not Found/, done); }); }); }); ================================================ FILE: Readme.md ================================================ # Koa Examples A repository containing small examples to illustrate the use of Koa for creating web applications and other HTTP servers. # Running tests ```bash npm test npm run lint ``` ## Included Examples - [404](404) - 404 handling - [base-auth](base-auth) - middleware base auth example - [blog](blog) - multi-route & view rendering - [body-parsing](body-parsing) - request body parsing - [compose](compose) - compose middlewares example - [conditional-middleware](conditional-middleware) - shows how middleware may be conditionally applied - [cookies](cookies) - cookie usage example - [csrf](csrf) - middleware csrf example - [errors](errors) - error handling & propagation - [flash-messages](flash-messages) - flash example - [hello-world](hello-world) - hello world application - [multipart](multipart) - multipart example downloading files using co-busboy - [negotiation](negotiation) - negotiation usage example - [stream-file](stream-file) - simple file streaming - [stream-objects](stream-objects) - objects streaming - [stream-server-side-events](stream-server-side-events) - server side events streaming - [stream-view](stream-view) - view streaming - [templates](templates) - simple view rendering - [upload](upload) - multi-file uploading - [vhost](vhost) - virtual host example ## Example Repositories - [coko](https://github.com/bhaskarmelkani/coko) - A minimal convention over configuration framework/boilerplate for Koa 2. - [kails](https://github.com/embbnux/kails) - A Web App like Rails build with Koa v2, Webpack and Postgres - [muffin](https://github.com/muffinjs/server) - A content management system build on top of Koa v2 - [links](https://github.com/juliangruber/links) - experimental content sharing and collaboration platform - [component-crawler](https://github.com/component/crawler.js) - crawl users and organizations for repositories with `component.json`s - [bigpipe](https://github.com/jonathanong/bigpipe-example) - Facebook's BigPipe implementation in koa and component - [webcam-mjpeg-stream](https://github.com/jonathanong/webcam-mjpeg-stream) - stream JPEG snapshots from your Mac - [cnpmjs.org](https://github.com/cnpm/cnpmjs.org) - Private npm registry and web for Enterprise, base on koa, MySQL and Simple Store Service - [blog-mongo](https://github.com/marcusoftnet/koablog-mongo) - the blog example from this repo, but using a MongoDb database, and tests - [koa-rest](https://github.com/hemanth/koa-rest) - A simple app to demo REST API - [koajs-rest-skeleton](https://github.com/ria-com/node-koajs-rest-skeleton) - A simple Koa REST Skeleton Application - [koa-bookshelf](https://github.com/Tomsqualm/koa-bookshelf) - Koa example with CRUD, using MongoDB and Heroku comptability - [todo](https://github.com/koajs/todo) - A todo example written in koa and [react](http://facebook.github.io/react/) - [koa-skeleton](https://github.com/danneu/koa-skeleton) - A simple made-to-be-forked Koa app that uses Postgres and deploys to Heroku. - Live demo:You have 0 posts!
'); done(); }); }); }); describe('POST /post/new', function() { it('should create post and redirect to /', function(done) { request .post('/post') .send({title: 'Title', body: 'Contents'}) .end(function(err, res) { if (err) return done(err); res.header.location.should.be.equal('/'); done(); }); }); }); describe('GET /post/0', function() { it('should see post', function(done) { request .get('/post/0') .expect(200, function(err, res) { if (err) return done(err); res.should.be.html; res.text.should.include('Contents
'); done(); }); }); }); }); ================================================ FILE: blog/views/index.html ================================================Missing content!
{% endblock %}You have {{ posts.length }} posts!
Create a new post.
{% endblock %} ================================================ FILE: blog/views/show.html ================================================ {% extends 'layout.html' %} {% block title %}{{ post.title }}{% endblock %} {% block content %}{{ post.body }}
{% endblock %} ================================================ FILE: body-parsing/app.js ================================================ const Koa = require('koa'); const koaBody = require('koa-body'); const app = module.exports = new Koa(); app.use(koaBody({ jsonLimit: '1kb' })); // POST .name to /uppercase // co-body accepts application/json // and application/x-www-form-urlencoded app.use(async function(ctx) { const body = ctx.request.body; if (!body.name) ctx.throw(400, '.name required'); ctx.body = { name: body.name.toUpperCase() }; }); if (!module.parent) app.listen(3000); ================================================ FILE: body-parsing/test.js ================================================ const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Body Parsing', function() { after(function() { server.close(); }); describe('POST /uppercase', function() { describe('with JSON', function() { it('should work', function(done) { request .post('/uppercase') .send({ name: 'tobi' }) .expect(200) .expect({ name: 'TOBI' }, done); }); }); describe('with urlencoded', function() { it('should work', function(done) { request .post('/uppercase') .send('name=tj') .expect(200) .expect({ name: 'TJ' }, done); }); }); describe('when length > limit', function() { it('should 413', function(done) { request .post('/json') .send({ name: Array(5000).join('a') }) .expect(413, done); }); }); describe('when no name is sent', function() { it('should 400', function(done) { request .post('/uppsercase') .send('age=10') .expect(400, done); }); }); }); }); ================================================ FILE: compose/app.js ================================================ /** * Each `app.use()` only accepts a single generator function. * If you want to combine multiple generator functions into a single one, * you can use `koa-compose` to do so. * This allows you to use `app.use()` only once. * Your code will end up looking something like: * * app.use(compose([ * function *(){}, * function *(){}, * function *(){} * ])) */ const compose = require('koa-compose'); const Koa = require('koa'); const app = module.exports = new Koa(); // x-response-time async function responseTime(ctx, next) { const start = new Date(); await next(); const ms = new Date() - start; ctx.set('X-Response-Time', ms + 'ms'); } // logger async function logger(ctx, next) { const start = new Date(); await next(); const ms = new Date() - start; if ('test' != process.env.NODE_ENV) { console.log('%s %s - %s', ctx.method, ctx.url, ms); } } // response async function respond(ctx, next) { await next(); if ('/' != ctx.url) return; ctx.body = 'Hello World'; } // composed middleware const all = compose([ responseTime, logger, respond ]); app.use(all); if (!module.parent) app.listen(3000); ================================================ FILE: compose/test.js ================================================ const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Compose', function() { after(function() { server.close(); }); describe('when GET /', function() { it('should say "Hello World"', function(done) { request .get('/') .expect(200) .expect('Hello World', done); }); it('should set X-Response-Time', function(done) { request .get('/') .expect('X-Response-Time', /ms$/) .expect(200, done); }); }); describe('when not GET /', function() { it('should 404', function(done) { request .get('/aklsjdf') .expect(404, done); }); }); }); ================================================ FILE: conditional-middleware/app.js ================================================ const logger = require('koa-logger'); const Koa = require('koa'); const app = new Koa(); // passing any middleware to this middleware // will make it conditional, and will not be used // when an asset is requested, illustrating how // middleware may "wrap" other middleware. function ignoreAssets(mw) { return async function(ctx, next) { if (/(\.js|\.css|\.ico)$/.test(ctx.path)) { await next(); } else { // must .call() to explicitly set the receiver await mw.call(this, ctx, next); } }; } // TRY: // $ curl http://localhost:3000/ // $ curl http://localhost:3000/style.css // $ curl http://localhost:3000/some.html app.use(ignoreAssets(logger())); app.use(async function(ctx) { ctx.body = 'Hello World'; }); app.listen(3000); ================================================ FILE: cookies/app.js ================================================ /** * This example simply sets the number of views from the same client * both as a cookie and as a response string. */ const Koa = require('koa'); const app = module.exports = new Koa(); app.use(async function(ctx) { const n = ~~ctx.cookies.get('view') + 1; ctx.cookies.set('view', n); ctx.body = n + ' views'; }); if (!module.parent) app.listen(3000); ================================================ FILE: cookies/test.js ================================================ const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Cookies Views', function() { after(function() { server.close(); }); [1, 2, 3].forEach(function(i) { describe('on iteration #' + i, function() { it('should set the views as a cookie and as the body', function(done) { request .get('/') .expect(200) .expect('Set-Cookie', new RegExp('view=' + i)) .expect(i + ' views', done); }); }); }); }); ================================================ FILE: csrf/app.js ================================================ const Koa = require('koa'); const koaBody = require('koa-body'); const session = require('koa-session'); const CSRF = require('koa-csrf'); const router = require('@koa/router')(); const app = module.exports = new Koa(); /** * csrf need session */ app.keys = ['session key', 'csrf example']; app.use(session(app)); app.use(koaBody()); /** * maybe a bodyparser */ /** * csrf middleware */ app.use(new CSRF()); /** * route */ router.get('/token', token) .post('/post', post); app.use(router.routes()); async function token(ctx) { ctx.body = ctx.csrf; } async function post(ctx) { ctx.body = {ok: true}; } if (!module.parent) app.listen(3000); ================================================ FILE: csrf/test.js ================================================ require('should'); const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); let token; let cookie; describe('csrf', function() { after(function() { server.close(); }); describe('GET /token', function() { it('should get token', function(done) { request .get('/token') .expect(200) .end(function(err, res) { token = res.text; token.should.be.String; cookie = res.headers['set-cookie'].join(';'); done(err); }); }); }); describe('POST /post', function() { it('should 403 without token', function(done) { request .post('/post') .send({foo: 'bar'}) .expect(403, done); }); it('should 403 with wrong token', function(done) { request .post('/post') .send({foo: 'bar'}) .set('x-csrf-token', 'wrong token') .expect(403, done); }); it('should 200 with token in head', function(done) { request .post('/post') .set('Cookie', cookie) .set('x-csrf-token', token) .send({foo: 'bar'}) .expect(200, done); }); it('should 200 with token in body', function(done) { request .post('/post') .set('Cookie', cookie) .send({_csrf: token}) .expect(200, done); }); }); }); ================================================ FILE: errors/app.js ================================================ const Koa = require('koa'); const app = module.exports = new Koa(); // look ma, error propagation! app.use(async function(ctx, next) { try { await next(); } catch (err) { // some errors will have .status // however this is not a guarantee ctx.status = err.status || 500; ctx.type = 'html'; ctx.body = 'Something exploded, please contact Maru.
'; // since we handled this manually we'll // want to delegate to the regular app // level error handling as well so that // centralized still functions correctly. ctx.app.emit('error', err, ctx); } }); // response app.use(async function() { throw new Error('boom boom'); }); // error handler app.on('error', function(err) { if (process.env.NODE_ENV != 'test') { console.log('sent error %s to the cloud', err.message); console.log(err); } }); if (!module.parent) app.listen(3000); ================================================ FILE: errors/test.js ================================================ require('should'); const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Errors', function() { after(function() { server.close(); }); it('should catch the error', function(done) { request .get('/') .expect(500) .expect('Content-Type', /text\/html/, done); }); it('should emit the error on app', function(done) { app.once('error', function(err, ctx) { err.message.should.equal('boom boom'); ctx.should.be.ok; done(); }); request .get('/') .end(function() {}); }); }); ================================================ FILE: flash-messages/app.js ================================================ /** * A very simple flash example. * Only uses JSON for simplicity. */ const Koa = require('koa'); const rawBody = require('raw-body'); const session = require('koa-session'); const app = module.exports = new Koa(); // required for signed cookie sessions app.keys = ['key1', 'key2']; app.use(session(app)); app.use(async function(ctx, next) { if (ctx.method !== 'GET' || ctx.path !== '/messages') return await next(); // get any messages saved in the session const messages = ctx.session.messages || []; ctx.body = messages; // delete the messages as they've been deliverd delete ctx.session.messages; }); app.use(async function(ctx, next) { if (ctx.method !== 'POST' || ctx.path !== '/messages') return await next(); // the request string is the flash message const message = await rawBody(ctx.req, { encoding: 'utf8' }); // push the message to the session ctx.session.messages = ctx.session.messages || []; ctx.session.messages.push(message); // tell the client everything went okay ctx.status = 204; }); if (!module.parent) app.listen(3000); ================================================ FILE: flash-messages/test.js ================================================ require('should'); const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Flash Messages', function() { after(function() { server.close(); }); it('GET should return an empty array', function(done) { request .get('/messages') .expect(200) .expect('content-type', 'application/json; charset=utf-8') .expect('[]', done); }); it('POST should return 204', function(done) { request .post('/messages') .send('hello') .expect(204, done); }); it('GET should return the message', function(done) { request .get('/messages') .expect(200) .expect('content-type', 'application/json; charset=utf-8') .expect('["hello"]', done); }); it('GET should return no more messages', function(done) { request .get('/messages') .expect(200) .expect('content-type', 'application/json; charset=utf-8') .expect('[]', done); }); }); ================================================ FILE: hello-world/app.js ================================================ const Koa = require('koa'); const app = module.exports = new Koa(); app.use(async function(ctx) { ctx.body = 'Hello World'; }); if (!module.parent) app.listen(3000); ================================================ FILE: hello-world/test.js ================================================ const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Hello World', function() { after(function() { server.close(); }); it('should say "Hello World"', function(done) { request .get('/') .expect(200) .expect('Hello World', done); }); }); ================================================ FILE: multipart/app.js ================================================ /** * Multipart example downloading all the files to disk using co-busboy. * If all you want is to download the files to a temporary folder, * just use https://github.com/cojs/multipart instead of copying this code * as it handles file descriptor limits whereas this does not. */ const os = require('os'); const path = require('path'); const Koa = require('koa'); const fs = require('fs-promise'); const koaBody = require('koa-body'); const app = module.exports = new Koa(); app.use(koaBody({ multipart: true })); app.use(async function(ctx) { // create a temporary folder to store files const tmpdir = path.join(os.tmpdir(), uid()); // make the temporary directory await fs.mkdir(tmpdir); const filePaths = []; const files = ctx.request.files || {}; for (let key in files) { const file = files[key]; const filePath = path.join(tmpdir, file.name); const reader = fs.createReadStream(file.path); const writer = fs.createWriteStream(filePath); reader.pipe(writer); filePaths.push(filePath); } ctx.body = filePaths; }); if (!module.parent) app.listen(3000); function uid() { return Math.random().toString(36).slice(2); } ================================================ FILE: multipart/test.js ================================================ require('should'); const fs = require('fs'); const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); // https://github.com/mscdex/busboy/blob/master/test/test-types-multipart.js const ct = 'multipart/form-data; boundary=---------------------------paZqsnEHRufoShdX6fh0lUhXBP4k'; const body = [ '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', 'Content-Disposition: form-data; name="file_name_0"', '', 'super alpha file', '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', 'Content-Disposition: form-data; name="file_name_1"', '', 'super beta file', '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', 'Content-Disposition: form-data; name="upload_file_0"; filename="1k_a.dat"', 'Content-Type: application/octet-stream', '', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k', 'Content-Disposition: form-data; name="upload_file_1"; filename="1k_b.dat"', 'Content-Type: application/octet-stream', '', 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', '-----------------------------paZqsnEHRufoShdX6fh0lUhXBP4k--' ].join('\r\n'); describe('Multipart Files', function() { after(function() { server.close(); }); it('should store all the files', function(done) { request .post('/') .set('Content-Type', ct) .send(body) .expect(200) .end(function(err, res) { if (err) return done(err); const files = res.body; files.should.have.length(2); fs.stat(files[0], function(err) { if (err) return done(err); fs.stat(files[1], done); }); }); }); }); ================================================ FILE: negotiation/app.js ================================================ const Koa = require('koa'); const app = module.exports = new Koa(); const tobi = { _id: '123', name: 'tobi', species: 'ferret' }; const loki = { _id: '321', name: 'loki', species: 'ferret' }; const users = { tobi: tobi, loki: loki }; // content negotiation middleware. // note that you should always check for // presence of a body, and sometimes you // may want to check the type, as it may // be a stream, buffer, string, etc. app.use(async function(ctx, next) { await next(); // no body? nothing to format, early return if (!ctx.body) return; // Check which type is best match by giving // a list of acceptable types to `req.accepts()`. const type = ctx.accepts('json', 'html', 'xml', 'text'); // not acceptable if (type === false) ctx.throw(406); // accepts json, koa handles this for us, // so just return if (type === 'json') return; // accepts xml if (type === 'xml') { ctx.type = 'xml'; ctx.body = 'Hello World
'); done(); }); }); }); ================================================ FILE: stream-view/view.js ================================================ 'use strict'; const Readable = require('stream').Readable; const co = require('co'); module.exports = class View extends Readable { constructor(context) { super(); // render the view on a different loop co.call(this, this.render).catch(context.onerror); } _read() {} *render() { // push the immediately this.push('Hello World
')); }; this.push('' + body + ''); // close the document this.push(''); // end the stream this.push(null); }; }; ================================================ FILE: templates/app.js ================================================ const path = require('path'); const views = require('koa-views'); const Koa = require('koa'); const app = module.exports = new Koa(); // setup views, appending .ejs // when no extname is given to render() app.use(views(path.join(__dirname, '/views'), { extension: 'ejs' })); // dummy data const user = { name: { first: 'Tobi', last: 'Holowaychuk' }, species: 'ferret', age: 3 }; // render app.use(async function(ctx) { await ctx.render('user', { user }); }); if (!module.parent) app.listen(3000); ================================================ FILE: templates/test.js ================================================ const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Templates', function() { after(function() { server.close(); }); describe('GET /', function() { it('should respond with a rendered view', function(done) { request .get('/') .expect(200) .expect('Tobi is a 3 year old ferret.
', done); }); }); }); ================================================ FILE: templates/views/user.ejs ================================================<%= user.name.first %> is a <%= user.age %> year old <%= user.species %>.
================================================ FILE: upload/app.js ================================================ /** * Module dependencies. */ const logger = require('koa-logger'); const serve = require('koa-static'); const koaBody = require('koa-body'); const Koa = require('koa'); const fs = require('fs'); const app = new Koa(); const os = require('os'); const path = require('path'); // log requests app.use(logger()); app.use(koaBody({ multipart: true })); // custom 404 app.use(async function(ctx, next) { await next(); if (ctx.body || !ctx.idempotent) return; ctx.redirect('/404.html'); }); // serve files from ./public app.use(serve(path.join(__dirname, '/public'))); // handle uploads app.use(async function(ctx, next) { // ignore non-POSTs if ('POST' != ctx.method) return await next(); const file = ctx.request.files.file; const reader = fs.createReadStream(file.path); const stream = fs.createWriteStream(path.join(os.tmpdir(), Math.random().toString())); reader.pipe(stream); console.log('uploading %s -> %s', file.name, stream.path); ctx.redirect('/'); }); // listen app.listen(3000); console.log('listening on port 3000'); ================================================ FILE: upload/public/404.html ================================================The page you requested cannot be found.
================================================ FILE: upload/public/index.html ================================================Try uploading multiple files at a time.
================================================ FILE: vhost/app.js ================================================ const compose = require('koa-compose'); const Koa = require('koa'); const app = module.exports = new Koa(); // virtual host apps const wwwSubdomain = composer(require('./apps/koa')); const barSubdomain = composer(require('./apps/array')); // compose koa apps and middleware arrays // to be used later in our host switch generator function composer(app) { const middleware = app instanceof Koa ? app.middleware : app; return compose(middleware); } // look ma, global response logging for all our apps! app.use(async function(ctx, next) { const start = new Date(); await next(); const ms = new Date() - start; if ('test' != process.env.NODE_ENV) { console.log('%s %s %s - %sms', ctx.host, ctx.method, ctx.url, ms); } }); // switch between appropriate hosts calling their // composed middleware with the appropriate context. app.use(async function(ctx, next) { switch (ctx.hostname) { case 'example.com': case 'www.example.com': // displays `Hello from main app` // and sets a `X-Custom` response header return await wwwSubdomain.apply(this, [ctx, next]); case 'bar.example.com': // displays `Howzit? From bar middleware bundle` // and sets a `X-Response-Time` response header return await barSubdomain.apply(this, [ctx, next]); } // everything else, eg: 127.0.0.1:3000 // will propagate to 404 Not Found return await next(); }); if (!module.parent) app.listen(3000); ================================================ FILE: vhost/apps/array.js ================================================ // rather than koa apps we can also use array // bundles of middleware to the same effect. async function responseTime(ctx, next) { const start = new Date(); await next(); const ms = new Date() - start; ctx.set('X-Response-Time', ms + 'ms'); } async function index(ctx, next) { await next(); if ('/' != ctx.url) return; ctx.body = 'Howzit? From bar middleware bundle'; } module.exports = [ responseTime, index ]; ================================================ FILE: vhost/apps/koa.js ================================================ const Koa = require('koa'); // koa app const app = new Koa(); app.use(async function(ctx, next) { await next(); ctx.set('X-Custom', 'Dub Dub Dub App'); }); app.use(async function(ctx, next) { await next(); if ('/' != ctx.url) return; ctx.body = 'Hello from www app'; }); module.exports = app; ================================================ FILE: vhost/test.js ================================================ const app = require('./app'); const server = app.listen(); const request = require('supertest').agent(server); describe('Virtual Host', function() { after(function() { server.close(); }); describe('www subdomain koa app', function() { describe('when GET /', function() { it('should say "Hello from www app"', function(done) { request .get('/') .set('Host', 'www.example.com') .expect(200) .expect('Hello from www app', done); }); it('should set X-Custom', function(done) { request .get('/') .set('Host', 'www.example.com') .expect('X-Custom', 'Dub Dub Dub App') .expect(200, done); }); }); describe('when GET / without subdomain', function() { it('should say "Hello from www app"', function(done) { request .get('/') .set('Host', 'example.com') .expect(200) .expect('Hello from www app', done); }); it('should set X-Custom', function(done) { request .get('/') .set('Host', 'example.com') .expect('X-Custom', 'Dub Dub Dub App') .expect(200, done); }); }); describe('when not GET /', function() { it('should 404', function(done) { request .get('/aklsjdf') .set('Host', 'example.com') .expect(404, done); }); }); }); describe('bar subdomain array bundle', function() { describe('when GET /', function() { it('should say "Howzit? From bar middleware bundle"', function(done) { request .get('/') .set('Host', 'bar.example.com') .expect(200) .expect('Howzit? From bar middleware bundle', done); }); it('should set X-Response-Time', function(done) { request .get('/') .set('Host', 'bar.example.com') .expect('X-Response-Time', /ms$/) .expect(200, done); }); }); describe('when not GET /', function() { it('should 404', function(done) { request .get('/aklsjdf') .set('Host', 'bar.example.com') .expect(404, done); }); }); }); describe('default vhost', function() { describe('when GET /', function() { it('should 404', function(done) { request .get('/') .set('Host', '127.0.0.1') .expect(404, done); }); }); }); });