moving to koa 2.0; + api

This commit is contained in:
tmshv committed 2016-03-23 14:56:59 +03:00
1 parent 44724132c0
commit 0ffa403b60
17 files changed
+398 -339

No files matched your search

+5 -3
View File
@@ -22,15 +22,17 @@
"babel-preset-stage-0": "^6.5.0",
"co": "^4.4.0",
"instagram-node": "^0.5.1",
"koa-bodyparser": "^2.0.1",
"koa": "^2.0.0-alpha.3",
"koa-bodyparser": "^3.0.0",
"koa-compose": "^3.1.0",
"koa-conditional-get": "^1.0.2",
"koa-convert": "^1.2.0",
"koa-etag": "^2.0.0",
"koa-helmet": "^1.0.0",
"koa-logger": "^1.2.2",
"koa-prerender": "^1.0.3",
"koa-route": "^2.4.0",
"koa-route": "^3.1.0",
"koa-static": "^2.0.0",
"koa": "^1.2.0",
"mz": "^2.3.1",
"promised-mongo": "git+https://git@github.com/tmshv/promised-mongo",
"sitemap": "^1.5.0",
+2 -2
View File
@@ -1,7 +1,7 @@
import {c} from '../core/db';
export function* getCollective(query={}, show={}) {
let data = yield c('collective').find(query, show);
export async function getCollective(query={}, show={}) {
let data = await c('collective').find(query, show);
if (!data) {
return null;
}
+6 -6
View File
@@ -6,8 +6,8 @@ import {populate, scheduleDate} from '../models/schedule';
import {shortifyName} from '../models/collective';
import {courses} from '../models/course';
export function* getSchedule(period, semester) {
let data = yield c('schedules').findOne({
export async function getSchedule(period, semester) {
let data = await c('schedules').findOne({
period: period,
semester: semester
});
@@ -15,8 +15,8 @@ export function* getSchedule(period, semester) {
return null;
}
export function* getScheduleList() {
let list = yield c('schedules')
export async function getScheduleList() {
let list = await c('schedules')
.find({}, {period: 1, semester: 1})
.toArray();
return list
@@ -28,8 +28,8 @@ export function* getScheduleList() {
});
}
export function* populateSchedule(schedule) {
let collective = yield getCollective({}, {id: 1, name: 1});
export async function populateSchedule(schedule) {
let collective = await getCollective({}, {id: 1, name: 1});
let collective_dict = collective.reduce((dict, i) => {
dict[i.id] = i.name;
return dict;
+14
View File
@@ -0,0 +1,14 @@
import {checkAuth} from '../core/service';
export function ensureServiceAuth() {
return async (ctx, next) => {
let service = ctx.request.headers['x-service-name'];
let token = ctx.request.headers['x-service-token'];
if (!checkAuth(service, token)) {
ctx.status = 401;
} else {
await next();
}
};
}
+13 -13
View File
@@ -1,26 +1,26 @@
import {index, accepts} from './';
export default function () {
return function *(next) {
yield next;
return async(ctx) => {
if (ctx.status != 404) return;
if (404 != this.status) return;
ctx.status = 404;
ctx.body = 'lol found';
this.status = 404;
yield accepts({
await accepts({
'text/html': index(),
'text/plain': function *() {
this.type = 'text/plain';
this.body = 'Page not found';
'text/plain': async ctx => {
ctx.type = 'text/plain';
ctx.body = 'Page not found';
},
'application/json': function *() {
this.type = 'application/json';
this.body = {
'application/json': async ctx => {
ctx.type = 'application/json';
ctx.body = {
error: 404,
message: 'Not Found'
};
}
});
})(ctx);
};
};
+15
View File
@@ -0,0 +1,15 @@
import {put} from 'koa-route';
import compose from 'koa-compose';
import {ensureServiceAuth} from '../../middlewares/ensureServiceAuth';
export function add() {
return put('/api/v1/feed', compose([
ensureServiceAuth(),
async ctx => {
let post = ctx.body;
ctx.body = {
pong: post
};
}
]));
}
+8
View File
@@ -0,0 +1,8 @@
import {add as feedAdd} from './feed';
import compose from 'koa-compose';
export default function () {
return compose([
feedAdd()
]);
};
+17 -27
View File
@@ -1,38 +1,28 @@
import route from 'koa-route';
import {c} from '../core/db';
import {accepts, index} from './';
import {sortBy} from '../utils/sort';
export default function (app) {
async function exists(ctx, id) {
let i = await article(id);
return i ? true : false;
}
function* exists(id) {
let i = yield c('events').findOne({id: id});
if (!i) {
i = yield c('timeline').findOne({id: id});
}
return i ? true : false;
async function article(id){
let i = await c('events').findOne({id: id});
if (!i) {
i = await c('timeline').findOne({id: id});
}
return i;
}
app.use(route.get('/article/:id', accepts({
//'text/html': function* (id){
// let test = yield exists(id);
// if(test){
// return yield index();
// }else{
// this.status = 404;
// return yield index();
// }
//},
export default function () {
return route.get('/article/:id', accepts({
'text/html': index(exists),
'text/plain': index(exists),
'application/json': function* (id) {
let i = yield c('events').findOne({id: id});
if (!i) {
i = yield c('timeline').findOne({id: id});
}
if (!i) this.status = 404;
else this.body = i;
'application/json': async (ctx, id) => {
let i = await article(id);
if (!i) ctx.status = 404;
else ctx.body = i;
}
})));
}))
};
+62 -57
View File
@@ -1,68 +1,73 @@
import compose from 'koa-compose';
import route from 'koa-route';
import {index, json, accepts} from './';
import {c} from '../core/db';
function* exists(id) {
let i = yield c('documents').findOne({id: id});
async function exists(ctx, id) {
let i = await c('documents').findOne({id: id});
return i ? true : false;
}
export default function (app) {
[
[
'/documents',
json(function *() {
let query = {};
this.body = yield c('documents')
.find(query)
.toArray();
})
],
[
'/documents/type/:type',
function *(type) {
let types = {
'document': ['documents', {type: 'document'}, populateDocPreview],
'award': ['documents', {type: 'award'}, populateDocPreview]
};
if (type in types) {
let [collection, query, mapFn] = types[type];
let docs = yield c(collection)
.find(query)
.toArray();
if (mapFn) docs = yield docs.map(mapFn);
this.body = docs;
} else {
this.status = 404;
}
}
],
[
'/documents/:id',
accepts({
'text/html': index(exists),
'text/plain': index(exists),
'application/json': function* (id) {
let i = yield c('documents').findOne({id: id});
if (!i) this.status = 404;
else {
i = yield populateDocPreview(i);
this.body = i;
}
}
})
]
].forEach(item => {
let [path, fn] = item;
app.use(route.get(path, fn));
});
export default function () {
return compose([
documents(),
document(),
type()
]);
};
function* populateDocPreview(doc) {
doc.preview = yield c('images').findOne({_id: doc.preview});
function documents() {
return route.get('/documents', json(
async(ctx) => {
let query = {};
ctx.body = await c('documents')
.find(query)
.toArray();
}
));
}
function document() {
return route.get('/documents/:id', accepts({
'text/html': index(exists),
'text/plain': index(exists),
'application/json': async(ctx, id) => {
let i = await c('documents').findOne({id: id});
if (!i) ctx.status = 404;
else {
i = await populateDocPreview(i);
ctx.body = i;
}
}
}));
}
function type() {
return route.get('/documents/type/:type', async(ctx, type) => {
let types = {
'document': ['documents', {type: 'document'}, populateDocPreview],
'award': ['documents', {type: 'award'}, populateDocPreview]
};
if (type in types) {
let [collection, query, mapFn] = types[type];
let docs = await c(collection)
.find(query)
.toArray();
if (mapFn) docs = await Promise.all(
docs.map(mapFn)
);
ctx.body = docs;
} else {
ctx.status = 404;
}
});
}
async function populateDocPreview(doc) {
doc.preview = await c('images').findOne({_id: doc.preview});
return doc;
}
+13 -13
View File
@@ -3,34 +3,34 @@ import {c} from '../core/db';
import {json} from './';
import {sortBy} from '../utils/sort';
export default function (app) {
let sortByDate = sortBy(
i => (new Date(i.date))
.getTime()
);
let sortByDate = sortBy(
i => (new Date(i.date))
.getTime()
);
app.use(route.get('/events/:page?', json(
function *() {
let limit = parseInt(this.query['limit']) || 10;
let skip = parseInt(this.query['skip']) || 0;
export default function () {
return route.get('/events/:page?', json(
async(ctx) => {
let limit = parseInt(ctx.query['limit']) || 10;
let skip = parseInt(ctx.query['skip']) || 0;
let query = {};
let documents = yield c('events')
let documents = await c('events')
.find(query)
.sort({date: -1})
.skip(skip)
.limit(limit)
.toArray();
let total = yield c('events')
let total = await c('events')
.find(query).count();
this.body = {
ctx.body = {
skip: skip,
limit: limit,
total: total,
data: documents
};
}
)));
));
};
+41 -42
View File
@@ -1,33 +1,14 @@
import co from 'co';
import compose from 'koa-compose';
import route from 'koa-route';
import {c} from '../core/db';
import {json} from './';
export default function (app) {
app.use(route.get('/gallery', json(
function *() {
let query = {};
let fields = ['_id', 'id', 'title', 'date'];
//.reduce((f, i) => {
// f[i] = 1;
// return f;
//}, {});
let albums = yield c('albums')
.find(query)
.toArray();
//albums = albums.map(i => {
// return fields.reduce((a, key)=> {
// a[key] = i[key];
// return a;
// }, {});
//});
albums = yield albums.map(processAlbum);
this.body = albums;
}
)));
export default function () {
return compose([
gallery(),
album()
]);
//app.use(route.get('/gallery/:year', json(
// function *(year) {
@@ -45,28 +26,46 @@ export default function (app) {
// this.body = albums;
// }
//)));
};
app.use(route.get('/album/:id', json(
function *(id) {
//year = parseInt(year);
//course = '/' + course;
//album = '/' + album;
//let start = new Date(year, 0, 1);
//let end = new Date(year, 11, 31);
//let query = {
// date: {$gte: start, $lt: end},
// course_uri: course,
// uri: album
//};
function gallery(){
return route.get('/gallery', json(
async(ctx) => {
let query = {};
let fields = ['_id', 'id', 'title', 'date'];
//.reduce((f, i) => {
// f[i] = 1;
// return f;
//}, {});
let record = yield c('albums').findOne({
let albums = await c('albums')
.find(query)
.toArray();
//albums = albums.map(i => {
// return fields.reduce((a, key)=> {
// a[key] = i[key];
// return a;
// }, {});
//});
albums = await Promise.all(albums.map(processAlbum));
ctx.body = albums;
}
));
}
function album(){
return route.get('/album/:id', json(
async(ctx, id) => {
let record = await c('albums').findOne({
id: id
});
record = yield processAlbum(record);
this.body = record;
record = await processAlbum(record);
ctx.body = record;
}
)));
};
));
}
let processAlbum = album => co(function *() {
//album.url = `/gallery/{year}{course}{album}`
+38 -38
View File
@@ -1,5 +1,6 @@
import fs from 'mz/fs';
import route from 'koa-route';
import compose from 'koa-compose';
import {map, assign} from '../utils/common';
import config from '../config';
@@ -16,19 +17,19 @@ import articles from './articles';
let main = () => route.get('/', index());
export function routes(app){
app.use(main());
sitemap(app);
schedule(app);
news(app);
gallery(app);
documents(app);
events(app);
articles(app);
teachers(app);
app.use(error404());
export function routes(){
return compose([
main(),
sitemap(),
schedule(),
news(),
gallery(),
documents(),
teachers(),
events(),
articles(),
error404()
]);
}
export function queryObject() {
@@ -36,9 +37,9 @@ export function queryObject() {
let toFalse = map(false, i => i === 'false');
const processors = [toTrue, toFalse];
return function *(next) {
let query = this.query;
this.query = Object.keys(query)
return async (ctx, next) => {
let query = ctx.query;
ctx.query = Object.keys(query)
.reduce((query, key) => assign(
query,
key,
@@ -46,23 +47,22 @@ export function queryObject() {
.reduce((value, fn) => fn(value), query[key])
), query);
yield next;
await next();
}
}
export function accepts(routes, def) {
return function *() {
let req = this.request;
export function accepts(routes) {
return async function (ctx) {
let request = ctx.request;
let types = Object.keys(routes)
.filter(type => req.accepts(type));
.filter(type => request.accepts(type));
if (types.length) {
let type = types[0];
yield routes[type].apply(this, arguments);
} else if (def) {
yield def;
let fn = routes[types[0]];
await fn.apply(ctx, arguments);
} else {
this.status = 406;
ctx.status = 406;
}
};
}
@@ -70,21 +70,21 @@ export function accepts(routes, def) {
export function index(fn) {
let filename = config['defaultIndex'];
return function *() {
return async function (ctx){
let test = true;
if(fn) test = yield fn.apply(this, arguments);
if (fn) test = await fn.apply(ctx, arguments);
if(!test) {
this.status = 404;
}
if (!test) ctx.status = 404;
this.type = 'text/html';
this.body = fs.createReadStream(filename);
ctx.type = 'text/html';
ctx.body = fs.createReadStream(filename);
}
}
export const json = fn => accepts({
'text/html': index(),
'text/plain': index(),
'application/json': fn
});
export function json(fn){
return accepts({
'text/html': index(),
'text/plain': index(),
'application/json': fn
});
}
+36 -32
View File
@@ -1,32 +1,38 @@
import compose from 'koa-compose';
import route from 'koa-route';
import {accepts, index} from './';
import {accepts} from './';
import {c} from '../core/db';
import {sortBy} from '../utils/sort';
export default function (app) {
let sortNewsByDate = sortBy(
i => (new Date(i.date))
.getTime()
);
const sortNewsByDate = sortBy(
i => (new Date(i.date))
.getTime()
);
app.use(route.get('/news', accepts({
'text/html': function *() {
this.redirect('/');
},
'text/plain': function *() {
this.redirect('/');
},
'application/json': function *() {
let limit = parseInt(this.query['limit']) || 10;
let skip = parseInt(this.query['skip']) || 0;
const redirect = async ctx => ctx.redirect('/');
export default function () {
return compose([
news(),
pinned()
]);
};
function news() {
return route.get('/news', accepts({
'text/html': redirect,
'text/plain': redirect,
'application/json': async ctx => {
let limit = parseInt(ctx.query['limit']) || 10;
let skip = parseInt(ctx.query['skip']) || 0;
let query = {};
if (this.query.type) query.type = this.query.type;
if (ctx.query.type) query.type = ctx.query.type;
let pinned = [];
if(skip == 0) {
const now = new Date();
pinned = yield c('timeline')
pinned = await c('timeline')
.find({until: {$gte: now}})
.toArray()
.then(i => i.sort(sortNewsByDate));
@@ -35,7 +41,7 @@ export default function (app) {
let sid = id => id + '';
let pinnedIds = pinned.map(p => sid(p['_id']));
let posts = yield c('timeline')
let posts = await c('timeline')
.find(query)
.sort({date: -1})
.skip(skip)
@@ -45,23 +51,21 @@ export default function (app) {
i => pinnedIds.indexOf(sid(i['_id'])) < 0
));
this.body = pinned.concat(posts);
ctx.body = pinned.concat(posts);
}
})));
}));
}
app.use(route.get('/news/pinned', accepts({
'text/html': function *() {
this.redirect('/');
},
'text/plain': function *() {
this.redirect('/');
},
'application/json': function *() {
function pinned(){
return route.get('/news/pinned', accepts({
'text/html': redirect,
'text/plain': redirect,
'application/json': async ctx => {
let now = new Date();
this.body = yield c('timeline')
ctx.body = await c('timeline')
.find({until: {$gte: now}})
.toArray()
.then(i => i.sort(sortNewsByDate));
}
})));
};
}));
}
+39 -32
View File
@@ -1,38 +1,45 @@
import compose from 'koa-compose';
import co from 'co';
import route from 'koa-route';
import {json} from './';
import {getSchedule, getScheduleList, populateSchedule} from '../core/schedule';
export default function (app) {
app.use(
route.get('/schedule/list', json(
function *() {
this.type = 'application/json';
let data = yield getScheduleList();
if (!data) this.status = 404;
else this.body = data;
}
))
);
app.use(
route.get('/schedule/:period?/:semester?', json(
function *(period, semester) {
this.type = 'application/json';
let data = yield getSchedule(period, semester);
if (!data) {
this.status = 404;
return;
}
let doPopulate = this.query.populate || false;
if (doPopulate) {
data.schedule = yield populateSchedule(data.schedule);
}
this.body = data;
}
))
);
export default function () {
return compose([
list(),
schedule()
]);
};
function list() {
return route.get('/schedule/list', json(
async(ctx) => {
ctx.type = 'application/json';
let data = await getScheduleList();
if (!data) ctx.status = 404;
else ctx.body = data;
}
))
}
function schedule() {
return route.get('/schedule/:period?/:semester?', json(
async(ctx, period, semester) => {
ctx.type = 'application/json';
let data = await getSchedule(period, semester);
if (!data) {
ctx.status = 404;
return;
}
let doPopulate = ctx.query.populate || false;
if (doPopulate) {
data.schedule = await populateSchedule(data.schedule);
}
ctx.body = data;
}
))
}
+21 -23
View File
@@ -5,41 +5,39 @@ import {c} from '../core/db';
import {homeUrl, sitemapCacheTime} from '../config';
import menu from '../models/menu';
export default function (app) {
app.use(route.get('/sitemap.xml', function *() {
let urls = yield [
export default function () {
return route.get('/sitemap.xml', async ctx => {
let urls = await Promise.all([
getMenuUrls(),
getGalleryUrls(),
getTeacherUrls(),
getEventsUrls(),
getNewsUrls()
];
]);
urls = urls.reduce((urls, i) => urls.concat(i));
let sm = sitemap.createSitemap({
let map = sitemap.createSitemap({
hostname: homeUrl,
cacheTime: sitemapCacheTime,
urls: urls
});
this.set('Content-Type', 'application/xml');
this.body = yield new Promise(resolve => sm.toXML(resolve));
}));
ctx.set('Content-Type', 'application/xml');
ctx.body = map.toString();
})
};
function *getMenuUrls() {
async function getMenuUrls(frequency='daily') {
return menu
.filter(i => 'url' in i)
.map(function (item) {
return {
url: item.url,
changefreq: 'daily'
}
});
.map(i => ({
url: i.url,
changefreq: frequency
}));
}
function *getGalleryUrls() {
let docs = yield c('albums')
async function getGalleryUrls() {
let docs = await c('albums')
.find({})
.toArray();
@@ -51,8 +49,8 @@ function *getGalleryUrls() {
});
}
function *getTeacherUrls() {
let docs = yield c('collective')
async function getTeacherUrls() {
let docs = await c('collective')
.find({})
.toArray();
@@ -64,8 +62,8 @@ function *getTeacherUrls() {
});
}
function *getEventsUrls() {
let docs = yield c('events')
async function getEventsUrls() {
let docs = await c('events')
.find({})
.toArray();
@@ -77,8 +75,8 @@ function *getEventsUrls() {
});
}
function *getNewsUrls() {
let docs = yield c('timeline')
async function getNewsUrls() {
let docs = await c('timeline')
.find({type: 'post'})
.toArray();
+48 -34
View File
@@ -1,4 +1,5 @@
import co from 'co';
import compose from 'koa-compose';
import route from 'koa-route';
import {index, json} from './';
import {sortByPattern} from '../utils/sort';
@@ -7,42 +8,55 @@ import {c} from '../core/db';
let split = d => s => s.split(d);
let toArray = split(',');
export default function (app) {
app.use(route.get('/collective', index()));
app.use(route.get('/teacher/list', json(
function *() {
this.type = 'application/json';
let pattern = toArray(this.query.sort || '');
let data = yield c('collective').find();
if (!data) {
this.status = 404;
return;
}
let collective = yield data
.map(processProfile);
this.body = sortByPattern(collective, pattern, i => i.id);
}
)));
app.use(route.get('/teacher/:id', json(
function *(id) {
this.type = 'application/json';
let data = yield c('collective').findOne({id: id});
if (!data) {
this.status = 404;
return;
}
this.body = yield processProfile(data);
}
)))
export default function () {
return compose([
collective(),
list(),
teacher()
]);
};
function collective() {
return route.get('/collective', index());
}
function list() {
return route.get('/teacher/list', json(
async(ctx) => {
ctx.type = 'application/json';
let pattern = toArray(ctx.query.sort || '');
let data = await c('collective').find();
if (!data) {
ctx.status = 404;
return;
}
let collective = await Promise.all(
data.map(processProfile)
);
ctx.body = sortByPattern(collective, pattern, i => i.id);
}
));
}
function teacher() {
return route.get('/teacher/:id', json(
async (ctx, id) => {
ctx.type = 'application/json';
let data = await c('collective').findOne({id: id});
if (!data) {
ctx.status = 404;
return;
}
ctx.body = await processProfile(data);
}
))
}
let processProfile = profile => co(function *() {
let imageId = profile.picture;
profile.picture = yield c('images').findOne({_id: imageId});
+20 -17
View File
@@ -1,7 +1,8 @@
import path from 'path';
import koa from 'koa';
import Koa from 'koa';
import serve from 'koa-static';
import logger from 'koa-logger';
import convert from 'koa-convert';
import conditional from 'koa-conditional-get';
import etag from 'koa-etag';
import prerender from 'koa-prerender';
@@ -11,40 +12,42 @@ import bodyParser from 'koa-bodyparser';
import config from './config';
import {redirectionTable} from './config';
import {routes, queryObject} from './routes';
// import api from './routes/api';
import api from './routes/api';
import {redirect} from './routes/redirect';
const dirPublic = path.join(__dirname, '../public');
const dirTemplates = path.join(__dirname, 'templates');
export function server(){
const app = koa();
const $ = convert;
const app = new Koa();
app.proxy = true;
app.use(bodyParser());
app.use(logger());
// api(app);
app.use(conditional());
app.use(etag());
app.use($(logger()));
app.use(api());
app.use($(conditional()));
app.use($(etag()));
app.use(prerenderRmFragment());
app.use(prerender(config.prerender));
app.use(serve(dirPublic));
app.use(serve(dirTemplates));
app.use(redirect(redirectionTable));
app.use(helmet());
app.use($(prerender(config.prerender)));
app.use($(serve(dirPublic)));
app.use($(serve(dirTemplates)));
app.use($(redirect(redirectionTable)));
app.use($(helmet()));
app.use(queryObject());
routes(app);
app.use(routes());
return app;
}
function prerenderRmFragment(){
return function *(next) {
yield* next;
return async (ctx, next) => {
await next();
try {
let xp = this.response.header['x-prerender'] == 'true';
if (xp) this.body = this.body.replace('<meta name="fragment" content="!">', '');
let xp = ctx.response.header['x-prerender'] == 'true';
if (xp) ctx.body = ctx.body.replace('<meta name="fragment" content="!">', '');
} catch (e) {
}
}