222 lines
6.2 KiB
TypeScript
222 lines
6.2 KiB
TypeScript
import { IncomingMessage, Server, ServerResponse } from 'http'
|
|
import { Service, ServiceBroker, Context } from 'moleculer'
|
|
|
|
import ApiGateway from 'moleculer-web'
|
|
|
|
import express, { Application } from 'express'
|
|
|
|
/* Middleware */
|
|
import shrinkRay from 'shrink-ray-current'
|
|
|
|
import AdminBro from 'admin-bro'
|
|
import AdminBroExpress from '@admin-bro/express'
|
|
import AdminBroMongoose from '@admin-bro/mongoose'
|
|
import { theme } from '../util/admin.theme'
|
|
|
|
AdminBro.registerAdapter(AdminBroMongoose)
|
|
|
|
import mongoose from 'mongoose'
|
|
import { mongooseOptions } from '../util/mongoose.options'
|
|
|
|
/* Models */
|
|
import '../models/literature-list'
|
|
|
|
/* TODO: ts-env */
|
|
const PORT = process.env.PORT ?? 3000
|
|
|
|
export default class ApiService extends Service {
|
|
|
|
private app: Application
|
|
private server: Server
|
|
|
|
public constructor(broker: ServiceBroker) {
|
|
super(broker);
|
|
// @ts-ignore
|
|
this.parseServiceSchema({
|
|
name: 'api',
|
|
mixins: [ApiGateway],
|
|
/* More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html */
|
|
settings: {
|
|
server: false, /* Disable integral web server */
|
|
|
|
routes: [{
|
|
whitelist: [
|
|
/* Access to all yt actions */
|
|
'youtube.*',
|
|
/* Read only access to anything that exposes DB operations wwwwwww*/
|
|
'*.list',
|
|
'*.find',
|
|
'*.get'
|
|
],
|
|
use: [
|
|
/* Only use this for things specific to moleculer api gateway */
|
|
],
|
|
|
|
mergeParams: true,
|
|
authentication: false,
|
|
authorization: false,
|
|
|
|
/*
|
|
* The auto-alias feature allows you to declare your route alias directly in your services.
|
|
* The gateway will dynamically build the full routes from service schema.
|
|
*/
|
|
autoAliases: true,
|
|
|
|
aliases:{},
|
|
|
|
// Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options
|
|
callingOptions: {},
|
|
|
|
bodyParsers: {
|
|
json: {
|
|
strict: false,
|
|
limit: '1MB',
|
|
},
|
|
urlencoded: {
|
|
extended: true,
|
|
limit: '1MB',
|
|
},
|
|
},
|
|
|
|
/* Mapping policy setting. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Mapping-policy */
|
|
mappingPolicy: 'all', // Available values: 'all', 'restrict'
|
|
|
|
/* Enable/disable logging */
|
|
logging: true,
|
|
}],
|
|
/* Do not log client side errors (does not log an error response when the error.code is 400<=X<500) */
|
|
log4XXResponses: false,
|
|
/* Logging the request parameters. Set to any log level to enable it. E.g. 'info' */
|
|
logRequestParams: null,
|
|
/* Logging the response data. Set to any log level to enable it. E.g. 'info' */
|
|
logResponseData: null
|
|
},
|
|
|
|
methods: {
|
|
/* NOTE: Leave these in place for easing any future expansion */
|
|
/* You can safely fold methods */
|
|
|
|
/**
|
|
* Authenticate the request. It check the `Authorization` token value in the request header.
|
|
* Check the token value & resolve the user by the token.
|
|
* The resolved user will be available in `ctx.meta.user`
|
|
*
|
|
* PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION!
|
|
*
|
|
* @param {Context} ctx
|
|
* @param {any} route
|
|
* @param {IncomingMessage} req
|
|
* @returns {Promise}
|
|
|
|
async authenticate = (ctx: Context, route: any, req: IncomingMessage): Promise < any > => {
|
|
// Read the token from header
|
|
const auth = req.headers.authorization;
|
|
|
|
if (auth && auth.startsWith('Bearer')) {
|
|
const token = auth.slice(7);
|
|
|
|
// Check the token. Tip: call a service which verify the token. E.g. `accounts.resolveToken`
|
|
if (token === '123456') {
|
|
// Returns the resolved user. It will be set to the `ctx.meta.user`
|
|
return {
|
|
id: 1,
|
|
name: 'John Doe',
|
|
};
|
|
|
|
} else {
|
|
// Invalid token
|
|
throw new ApiGateway.Errors.UnAuthorizedError(ApiGateway.Errors.ERR_INVALID_TOKEN, {
|
|
error: 'Invalid Token',
|
|
});
|
|
}
|
|
|
|
} else {
|
|
// No token. Throw an error or do nothing if anonymous access is allowed.
|
|
// Throw new E.UnAuthorizedError(E.ERR_NO_TOKEN);
|
|
return null;
|
|
}
|
|
},
|
|
*/
|
|
|
|
/**
|
|
* Authorize the request. Check that the authenticated user has right to access the resource.
|
|
*
|
|
* PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION!
|
|
*
|
|
* @param {Context} ctx
|
|
* @param {Object} route
|
|
* @param {IncomingMessage} req
|
|
* @returns {Promise}
|
|
|
|
async authorize = (ctx: Context < any, {
|
|
user: string;
|
|
} > , route: Record<string, undefined>, req: IncomingMessage): Promise < any > => {
|
|
// Get the authenticated user.
|
|
const user = ctx.meta.user;
|
|
|
|
// It check the `auth` property in action schema.
|
|
// @ts-ignore
|
|
if (req.$action.auth === 'required' && !user) {
|
|
throw new ApiGateway.Errors.UnAuthorizedError('NO_RIGHTS', {
|
|
error: 'Unauthorized',
|
|
});
|
|
}
|
|
},
|
|
*/
|
|
},
|
|
|
|
/* Mount lifecycle-events et al here */
|
|
created: this.created,
|
|
started: this.started,
|
|
stopped: this.stopped
|
|
})
|
|
}
|
|
|
|
async setupAdminBro () {
|
|
const db = await mongoose.connect(process.env.MONGO_URI, mongooseOptions)
|
|
|
|
const adminBro = new AdminBro({
|
|
databases: [db],
|
|
branding: {
|
|
companyName: 'Red Plateaus',
|
|
theme,
|
|
logo: '/RP_pote_tb.svg',
|
|
softwareBrothers: false
|
|
},
|
|
rootPath: '/admin'
|
|
})
|
|
|
|
return AdminBroExpress.buildRouter(adminBro)
|
|
}
|
|
|
|
created () {
|
|
this.app = express()
|
|
|
|
/* Remove things we don't need from here */
|
|
this.app.use(express.static('public'))
|
|
|
|
/* Compression */
|
|
this.app.use(shrinkRay())
|
|
|
|
/* Mount Moleculer service gateway*/
|
|
this.app.use('/api', this.express())
|
|
}
|
|
|
|
async started () {
|
|
if (process.env.MONGO_URI) {
|
|
/* Adminbro specifies we have to await Db connection, so */
|
|
this.app.use('/admin', await this.setupAdminBro())
|
|
}
|
|
|
|
/* Hello my friend, stay a while and */
|
|
this.server = this.app.listen(PORT, () => {
|
|
this.logger.info(`API Gateway listening on ${PORT}`)
|
|
})
|
|
}
|
|
|
|
async stopped () {
|
|
if (!this.server) this.logger.debug('Server was dead in the water')
|
|
this.server?.close() /* Server instance might not exist, prevent crashes */
|
|
}
|
|
}
|