mirror of
https://github.com/laurent22/joplin.git
synced 2024-11-27 08:21:03 +02:00
Merge branch 'dev' into release-2.0
This commit is contained in:
commit
d89bbc5571
@ -9,6 +9,8 @@ version: '3'
|
||||
services:
|
||||
db:
|
||||
image: postgres:13.1
|
||||
volumes:
|
||||
- ./data/postgres:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
restart: unless-stopped
|
||||
|
@ -26,12 +26,12 @@ if [ "$RESET_ALL" == "1" ]; then
|
||||
|
||||
echo "config keychain.supported 0" >> "$CMD_FILE"
|
||||
echo "config sync.target 9" >> "$CMD_FILE"
|
||||
echo "config sync.9.path http://localhost:22300" >> "$CMD_FILE"
|
||||
echo "config sync.9.path http://api-joplincloud.local:22300" >> "$CMD_FILE"
|
||||
echo "config sync.9.username $USER_EMAIL" >> "$CMD_FILE"
|
||||
echo "config sync.9.password 123456" >> "$CMD_FILE"
|
||||
|
||||
if [ "$1" == "1" ]; then
|
||||
curl --data '{"action": "createTestUsers"}' -H 'Content-Type: application/json' http://localhost:22300/api/debug
|
||||
curl --data '{"action": "createTestUsers"}' -H 'Content-Type: application/json' http://api-joplincloud.local:22300/api/debug
|
||||
|
||||
echo 'mkbook "shared"' >> "$CMD_FILE"
|
||||
echo 'mkbook "other"' >> "$CMD_FILE"
|
||||
|
@ -130,6 +130,8 @@ async function main() {
|
||||
appLogger().info(`Starting server (${env}) on port ${config().port} and PID ${process.pid}...`);
|
||||
appLogger().info('Running in Docker:', runningInDocker());
|
||||
appLogger().info('Public base URL:', config().baseUrl);
|
||||
appLogger().info('API base URL:', config().apiBaseUrl);
|
||||
appLogger().info('User content base URL:', config().userContentBaseUrl);
|
||||
appLogger().info('Log dir:', config().logDir);
|
||||
appLogger().info('DB Config:', markPasswords(config().database));
|
||||
|
||||
@ -158,7 +160,7 @@ async function main() {
|
||||
// }
|
||||
// }
|
||||
|
||||
appLogger().info(`Call this for testing: \`curl ${config().baseUrl}/api/ping\``);
|
||||
appLogger().info(`Call this for testing: \`curl ${config().apiBaseUrl}/api/ping\``);
|
||||
|
||||
// const tree: any = {
|
||||
// '000000000000000000000000000000F1': {},
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { rtrimSlashes } from '@joplin/lib/path-utils';
|
||||
import { Config, DatabaseConfig, DatabaseConfigClient, MailerConfig } from './utils/types';
|
||||
import { Config, DatabaseConfig, DatabaseConfigClient, MailerConfig, RouteType } from './utils/types';
|
||||
import * as pathUtils from 'path';
|
||||
|
||||
export interface EnvVariables {
|
||||
APP_BASE_URL?: string;
|
||||
USER_CONTENT_BASE_URL?: string;
|
||||
API_BASE_URL?: string;
|
||||
|
||||
APP_PORT?: string;
|
||||
DB_CLIENT?: string;
|
||||
RUNNING_IN_DOCKER?: string;
|
||||
@ -96,6 +99,7 @@ export function initConfig(env: EnvVariables, overrides: any = null) {
|
||||
const rootDir = pathUtils.dirname(__dirname);
|
||||
const viewDir = `${pathUtils.dirname(__dirname)}/src/views`;
|
||||
const appPort = env.APP_PORT ? Number(env.APP_PORT) : 22300;
|
||||
const baseUrl = baseUrlFromEnv(env, appPort);
|
||||
|
||||
config_ = {
|
||||
rootDir: rootDir,
|
||||
@ -106,11 +110,27 @@ export function initConfig(env: EnvVariables, overrides: any = null) {
|
||||
database: databaseConfigFromEnv(runningInDocker_, env),
|
||||
mailer: mailerConfigFromEnv(env),
|
||||
port: appPort,
|
||||
baseUrl: baseUrlFromEnv(env, appPort),
|
||||
baseUrl,
|
||||
apiBaseUrl: env.API_BASE_URL ? env.API_BASE_URL : baseUrl,
|
||||
userContentBaseUrl: env.USER_CONTENT_BASE_URL ? env.USER_CONTENT_BASE_URL : baseUrl,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export function baseUrl(type: RouteType): string {
|
||||
if (type === RouteType.Web) return config().baseUrl;
|
||||
if (type === RouteType.Api) return config().apiBaseUrl;
|
||||
if (type === RouteType.UserContent) return config().userContentBaseUrl;
|
||||
throw new Error(`Unknown type: ${type}`);
|
||||
}
|
||||
|
||||
// User content URL is not supported for now so only show the URL if the
|
||||
// user content is hosted on the same domain. Needs to get cookie working
|
||||
// across domains to get user content url working.
|
||||
export function showItemUrls(config: Config): boolean {
|
||||
return config.userContentBaseUrl === config.baseUrl;
|
||||
}
|
||||
|
||||
function config(): Config {
|
||||
if (!config_) throw new Error('Config has not been initialized!');
|
||||
return config_;
|
||||
|
@ -1,15 +1,6 @@
|
||||
import { routeResponseFormat, Response, RouteResponseFormat, execRequest } from '../utils/routeUtils';
|
||||
import { AppContext, Env } from '../utils/types';
|
||||
import { isView, View } from '../services/MustacheService';
|
||||
// import config from '../config';
|
||||
|
||||
// let mustache_: MustacheService = null;
|
||||
// function mustache(): MustacheService {
|
||||
// if (!mustache_) {
|
||||
// mustache_ = new MustacheService(config().viewDir, config().baseUrl);
|
||||
// }
|
||||
// return mustache_;
|
||||
// }
|
||||
|
||||
export default async function(ctx: AppContext) {
|
||||
ctx.appLogger().info(`${ctx.request.method} ${ctx.path}`);
|
||||
@ -44,7 +35,9 @@ export default async function(ctx: AppContext) {
|
||||
|
||||
const responseFormat = routeResponseFormat(ctx);
|
||||
|
||||
if (responseFormat === RouteResponseFormat.Html) {
|
||||
if (error.code === 'invalidOrigin') {
|
||||
ctx.response.body = error.message;
|
||||
} else if (responseFormat === RouteResponseFormat.Html) {
|
||||
ctx.response.set('Content-Type', 'text/html');
|
||||
const view: View = {
|
||||
name: 'error',
|
||||
|
@ -2,10 +2,11 @@ import config from '../../config';
|
||||
import { createTestUsers } from '../../tools/debugTools';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import { AppContext } from '../../utils/types';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
router.public = true;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ErrorNotFound } from '../../utils/errors';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import { AppContext } from '../../utils/types';
|
||||
|
||||
@ -14,7 +15,7 @@ const supportedEvents: Record<string, Function> = {
|
||||
},
|
||||
};
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
router.post('api/events', async (_path: SubPath, ctx: AppContext) => {
|
||||
const event = await bodyFields<Event>(ctx.req);
|
||||
|
@ -2,6 +2,7 @@ import { Item, Uuid } from '../../db';
|
||||
import { formParse } from '../../utils/requestUtils';
|
||||
import { respondWithItemContent, SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import * as fs from 'fs-extra';
|
||||
import { ErrorMethodNotAllowed, ErrorNotFound } from '../../utils/errors';
|
||||
@ -10,7 +11,7 @@ import { requestDeltaPagination, requestPagination } from '../../models/utils/pa
|
||||
import { AclAction } from '../../models/BaseModel';
|
||||
import { safeRemove } from '../../utils/fileUtils';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
// Note about access control:
|
||||
//
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
router.public = true;
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { ErrorForbidden } from '../../utils/errors';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import { User } from '../../db';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
router.public = true;
|
||||
|
||||
|
@ -2,10 +2,11 @@ import { ErrorBadRequest, ErrorNotFound } from '../../utils/errors';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { AclAction } from '../../models/BaseModel';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
router.patch('api/share_users/:id', async (path: SubPath, ctx: AppContext) => {
|
||||
const shareUserModel = ctx.models.shareUser();
|
||||
|
@ -3,6 +3,7 @@ import { Share, ShareType } from '../../db';
|
||||
import { bodyFields, ownerRequired } from '../../utils/requestUtils';
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { AclAction } from '../../models/BaseModel';
|
||||
|
||||
@ -11,7 +12,7 @@ interface ShareApiInput extends Share {
|
||||
note_id?: string;
|
||||
}
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
router.public = true;
|
||||
|
||||
|
@ -2,12 +2,13 @@ import { User } from '../../db';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { ErrorNotFound } from '../../utils/errors';
|
||||
import { AclAction } from '../../models/BaseModel';
|
||||
import uuidgen from '../../utils/uuidgen';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
async function fetchUser(path: SubPath, ctx: AppContext): Promise<User> {
|
||||
const user = await ctx.models.user().load(path.id);
|
||||
|
@ -4,7 +4,7 @@ import { ErrorNotFound, ErrorForbidden } from '../utils/errors';
|
||||
import { dirname, normalize } from 'path';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import * as fs from 'fs-extra';
|
||||
import { AppContext } from '../utils/types';
|
||||
import { AppContext, RouteType } from '../utils/types';
|
||||
import { localFileFromUrl } from '../utils/joplinUtils';
|
||||
const { mime } = require('@joplin/lib/mime-utils.js');
|
||||
|
||||
@ -44,7 +44,7 @@ async function findLocalFile(path: string): Promise<string> {
|
||||
return localPath;
|
||||
}
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.public = true;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { changeTypeToString } from '../../db';
|
||||
import { PaginationOrderDir } from '../../models/utils/pagination';
|
||||
@ -7,8 +8,9 @@ import { formatDateTime } from '../../utils/time';
|
||||
import defaultView from '../../utils/defaultView';
|
||||
import { View } from '../../services/MustacheService';
|
||||
import { makeTablePagination, Table, Row, makeTableView } from '../../utils/views/table';
|
||||
import config, { showItemUrls } from '../../config';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.get('changes', async (_path: SubPath, ctx: AppContext) => {
|
||||
const pagination = makeTablePagination(ctx.query, 'updated_time', PaginationOrderDir.DESC);
|
||||
@ -40,7 +42,7 @@ router.get('changes', async (_path: SubPath, ctx: AppContext) => {
|
||||
{
|
||||
value: change.item_name,
|
||||
stretch: true,
|
||||
url: items.find(i => i.id === change.item_id) ? ctx.models.item().itemContentUrl(change.item_id) : '',
|
||||
url: showItemUrls(config()) ? (items.find(i => i.id === change.item_id) ? ctx.models.item().itemContentUrl(change.item_id) : '') : null,
|
||||
},
|
||||
{
|
||||
value: changeTypeToString(change.type),
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { contextSessionId } from '../../utils/requestUtils';
|
||||
import { ErrorMethodNotAllowed } from '../../utils/errors';
|
||||
import defaultView from '../../utils/defaultView';
|
||||
|
||||
const router: Router = new Router();
|
||||
const router: Router = new Router(RouteType.Web);
|
||||
|
||||
router.get('home', async (_path: SubPath, ctx: AppContext) => {
|
||||
contextSessionId(ctx);
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { SubPath, redirect, respondWithItemContent } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { formParse } from '../../utils/requestUtils';
|
||||
import { ErrorNotFound } from '../../utils/errors';
|
||||
import config from '../../config';
|
||||
import config, { showItemUrls } from '../../config';
|
||||
import { formatDateTime } from '../../utils/time';
|
||||
import defaultView from '../../utils/defaultView';
|
||||
import { View } from '../../services/MustacheService';
|
||||
@ -11,7 +12,7 @@ import { makeTablePagination, makeTableView, Row, Table } from '../../utils/view
|
||||
import { PaginationOrderDir } from '../../models/utils/pagination';
|
||||
const prettyBytes = require('pretty-bytes');
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.get('items', async (_path: SubPath, ctx: AppContext) => {
|
||||
const pagination = makeTablePagination(ctx.query, 'name', PaginationOrderDir.ASC);
|
||||
@ -46,7 +47,7 @@ router.get('items', async (_path: SubPath, ctx: AppContext) => {
|
||||
{
|
||||
value: item.name,
|
||||
stretch: true,
|
||||
url: `${config().baseUrl}/items/${item.id}/content`,
|
||||
url: showItemUrls(config()) ? `${config().userContentBaseUrl}/items/${item.id}/content` : null,
|
||||
},
|
||||
{
|
||||
value: prettyBytes(item.content_size),
|
||||
@ -75,7 +76,7 @@ router.get('items/:id/content', async (path: SubPath, ctx: AppContext) => {
|
||||
const item = await itemModel.loadWithContent(path.id);
|
||||
if (!item) throw new ErrorNotFound();
|
||||
return respondWithItemContent(ctx.response, item, item.content);
|
||||
});
|
||||
}, RouteType.UserContent);
|
||||
|
||||
router.post('items', async (_path: SubPath, ctx: AppContext) => {
|
||||
const body = await formParse(ctx.req);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { SubPath, redirect } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { formParse } from '../../utils/requestUtils';
|
||||
import config from '../../config';
|
||||
@ -13,7 +14,7 @@ function makeView(error: any = null): View {
|
||||
return view;
|
||||
}
|
||||
|
||||
const router: Router = new Router();
|
||||
const router: Router = new Router(RouteType.Web);
|
||||
|
||||
router.public = true;
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { SubPath, redirect } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import config from '../../config';
|
||||
import { contextSessionId } from '../../utils/requestUtils';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.post('logout', async (_path: SubPath, ctx: AppContext) => {
|
||||
const sessionId = contextSessionId(ctx, false);
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { SubPath } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import { ErrorNotFound } from '../../utils/errors';
|
||||
import { Notification } from '../../db';
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.patch('notifications/:id', async (path: SubPath, ctx: AppContext) => {
|
||||
const fields: Notification = await bodyFields(ctx.req);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { SubPath, ResponseType, Response } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { ErrorNotFound } from '../../utils/errors';
|
||||
import { Item, Share } from '../../db';
|
||||
@ -18,7 +19,7 @@ async function renderItem(context: AppContext, item: Item, share: Share): Promis
|
||||
};
|
||||
}
|
||||
|
||||
const router: Router = new Router();
|
||||
const router: Router = new Router(RouteType.Web);
|
||||
|
||||
router.public = true;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { SubPath, redirect } from '../../utils/routeUtils';
|
||||
import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext, HttpMethod } from '../../utils/types';
|
||||
import { bodyFields, formParse } from '../../utils/requestUtils';
|
||||
import { ErrorForbidden, ErrorUnprocessableEntity } from '../../utils/errors';
|
||||
@ -52,7 +53,7 @@ function userIsMe(path: SubPath): boolean {
|
||||
return path.id === 'me';
|
||||
}
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.get('users', async (_path: SubPath, ctx: AppContext) => {
|
||||
const userModel = ctx.models.user();
|
||||
@ -152,7 +153,7 @@ router.post('users/:id/confirm', async (path: SubPath, ctx: AppContext) => {
|
||||
return redirect(ctx, `${config().baseUrl}/home`);
|
||||
} catch (error) {
|
||||
const endPoint = router.findEndPoint(HttpMethod.GET, 'users/:id/confirm');
|
||||
return endPoint(path, ctx, error);
|
||||
return endPoint.handler(path, ctx, error);
|
||||
}
|
||||
});
|
||||
|
||||
@ -192,7 +193,7 @@ router.post('users', async (path: SubPath, ctx: AppContext) => {
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorForbidden) throw error;
|
||||
const endPoint = router.findEndPoint(HttpMethod.GET, 'users/:id');
|
||||
return endPoint(path, ctx, user, error);
|
||||
return endPoint.handler(path, ctx, user, error);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { ErrorMethodNotAllowed, ErrorNotFound } from './errors';
|
||||
import { HttpMethod } from './types';
|
||||
import { HttpMethod, RouteType } from './types';
|
||||
import { RouteResponseFormat, RouteHandler } from './routeUtils';
|
||||
|
||||
interface RouteInfo {
|
||||
handler: RouteHandler;
|
||||
type?: RouteType;
|
||||
}
|
||||
|
||||
export default class Router {
|
||||
|
||||
// When the router is public, we do not check that a valid session is
|
||||
@ -13,22 +18,29 @@ export default class Router {
|
||||
|
||||
public responseFormat: RouteResponseFormat = null;
|
||||
|
||||
private routes_: Record<string, Record<string, RouteHandler>> = {};
|
||||
private routes_: Record<string, Record<string, RouteInfo>> = {};
|
||||
private aliases_: Record<string, Record<string, string>> = {};
|
||||
private type_: RouteType;
|
||||
|
||||
public findEndPoint(method: HttpMethod, schema: string): RouteHandler {
|
||||
public constructor(type: RouteType) {
|
||||
this.type_ = type;
|
||||
}
|
||||
|
||||
public findEndPoint(method: HttpMethod, schema: string): RouteInfo {
|
||||
if (this.aliases_[method]?.[schema]) { return this.findEndPoint(method, this.aliases_[method]?.[schema]); }
|
||||
|
||||
if (!this.routes_[method]) { throw new ErrorMethodNotAllowed(`Not allowed: ${method} ${schema}`); }
|
||||
const endPoint = this.routes_[method][schema];
|
||||
if (!endPoint) { throw new ErrorNotFound(`Not found: ${method} ${schema}`); }
|
||||
|
||||
let endPointFn = endPoint;
|
||||
let endPointInfo = endPoint;
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
if (typeof endPointFn === 'string') {
|
||||
endPointFn = this.routes_[method]?.[endPointFn];
|
||||
if (typeof endPointInfo === 'string') {
|
||||
endPointInfo = this.routes_[method]?.[endPointInfo];
|
||||
} else {
|
||||
return endPointFn;
|
||||
const output = { ...endPointInfo };
|
||||
if (!output.type) output.type = this.type_;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,29 +56,29 @@ export default class Router {
|
||||
this.aliases_[method][path] = target;
|
||||
}
|
||||
|
||||
public get(path: string, handler: RouteHandler) {
|
||||
public get(path: string, handler: RouteHandler, type: RouteType = null) {
|
||||
if (!this.routes_.GET) { this.routes_.GET = {}; }
|
||||
this.routes_.GET[path] = handler;
|
||||
this.routes_.GET[path] = { handler, type };
|
||||
}
|
||||
|
||||
public post(path: string, handler: RouteHandler) {
|
||||
public post(path: string, handler: RouteHandler, type: RouteType = null) {
|
||||
if (!this.routes_.POST) { this.routes_.POST = {}; }
|
||||
this.routes_.POST[path] = handler;
|
||||
this.routes_.POST[path] = { handler, type };
|
||||
}
|
||||
|
||||
public patch(path: string, handler: RouteHandler) {
|
||||
public patch(path: string, handler: RouteHandler, type: RouteType = null) {
|
||||
if (!this.routes_.PATCH) { this.routes_.PATCH = {}; }
|
||||
this.routes_.PATCH[path] = handler;
|
||||
this.routes_.PATCH[path] = { handler, type };
|
||||
}
|
||||
|
||||
public del(path: string, handler: RouteHandler) {
|
||||
public del(path: string, handler: RouteHandler, type: RouteType = null) {
|
||||
if (!this.routes_.DELETE) { this.routes_.DELETE = {}; }
|
||||
this.routes_.DELETE[path] = handler;
|
||||
this.routes_.DELETE[path] = { handler, type };
|
||||
}
|
||||
|
||||
public put(path: string, handler: RouteHandler) {
|
||||
public put(path: string, handler: RouteHandler, type: RouteType = null) {
|
||||
if (!this.routes_.PUT) { this.routes_.PUT = {}; }
|
||||
this.routes_.PUT[path] = handler;
|
||||
this.routes_.PUT[path] = { handler, type };
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ export class ErrorMethodNotAllowed extends ApiError {
|
||||
export class ErrorNotFound extends ApiError {
|
||||
public static httpCode: number = 404;
|
||||
|
||||
public constructor(message: string = 'Not Found') {
|
||||
super(message, ErrorNotFound.httpCode);
|
||||
public constructor(message: string = 'Not Found', code: string = undefined) {
|
||||
super(message, ErrorNotFound.httpCode, code);
|
||||
Object.setPrototypeOf(this, ErrorNotFound.prototype);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { baseUrl } from '../config';
|
||||
import { Item, ItemAddressingType } from '../db';
|
||||
import { ErrorBadRequest, ErrorForbidden, ErrorNotFound } from './errors';
|
||||
import Router from './Router';
|
||||
@ -166,14 +167,15 @@ export async function execRequest(routes: Routers, ctx: AppContext) {
|
||||
const match = findMatchingRoute(ctx.path, routes);
|
||||
if (!match) throw new ErrorNotFound();
|
||||
|
||||
const routeHandler = match.route.findEndPoint(ctx.request.method as HttpMethod, match.subPath.schema);
|
||||
const endPoint = match.route.findEndPoint(ctx.request.method as HttpMethod, match.subPath.schema);
|
||||
if (ctx.URL.origin !== baseUrl(endPoint.type)) throw new ErrorNotFound('Invalid origin', 'invalidOrigin');
|
||||
|
||||
// This is a generic catch-all for all private end points - if we
|
||||
// couldn't get a valid session, we exit now. Individual end points
|
||||
// might have additional permission checks depending on the action.
|
||||
if (!match.route.isPublic(match.subPath.schema) && !ctx.owner) throw new ErrorForbidden();
|
||||
|
||||
return routeHandler(match.subPath, ctx);
|
||||
return endPoint.handler(match.subPath, ctx);
|
||||
}
|
||||
|
||||
// In a path such as "/api/files/SOME_ID/content" we want to find:
|
||||
|
@ -67,6 +67,8 @@ export interface Config {
|
||||
logDir: string;
|
||||
tempDir: string;
|
||||
baseUrl: string;
|
||||
apiBaseUrl: string;
|
||||
userContentBaseUrl: string;
|
||||
database: DatabaseConfig;
|
||||
mailer: MailerConfig;
|
||||
}
|
||||
@ -79,4 +81,10 @@ export enum HttpMethod {
|
||||
HEAD = 'HEAD',
|
||||
}
|
||||
|
||||
export enum RouteType {
|
||||
Web = 1,
|
||||
Api = 2,
|
||||
UserContent = 3,
|
||||
}
|
||||
|
||||
export type KoaNext = ()=> Promise<void>;
|
||||
|
Loading…
Reference in New Issue
Block a user