Commit 330b9b93 authored by Sidson's avatar Sidson
Browse files

Merge branch 'update/package-core' into 'master'

Update/package core

See merge request m/affluence/affluence-serv!1
parents 8b835abb 53586e4b
......@@ -3,4 +3,5 @@ node_modules/
# Ignore config files that expose private data.
db.config.json
*.config.json
*.log
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Start Debug",
"type": "node",
"request": "attach",
"name": "Start debug and listen",
"address": "127.0.0.1",
"port": 9229,
"restart": true
}
]
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "start:debug"], //changement ici
"port": 9230,
"skipFiles": ["<node_internals>/**"],
"outputCapture": "std"
}
]
}
# Affluence Serv
Affluence backend Nodejs app.
## Install
`npm install`
## Run the app
* `npm run start:dev` to run the server in dev mode (hot reload/compile).
* `npm run start:debug` to run the server in debug mode (hot reload/compile + debuger).
* `npm run build && npm run start` to run the server in production mode.
## REST API
| Resource | Method | Type | Documentation |
| ------------------- | ------ | ---- | ------------------- |
| `/occupancy/report` | POST | JSON | Save a user report. |
......@@ -8,9 +8,8 @@ Backend Nodejs de l'application Affluence.
## Exécution
* `npm run start:dev` pour lancer le serveur en mode dev (rechargement/compilation à chaud).
* `npm run build` pour lancer la compilation (en mode production).
* `npm run start:debug` pour lancer le serveur en mode debug (rechargement/compilation à chaud + debugueur).
* `npm run build && npm run start` pour lancer le serveur en mode production.
## REST API
......
This diff is collapsed.
This diff is collapsed.
......@@ -4,48 +4,26 @@
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --env=prod",
"run:debug": "nodemon --quiet --inspect dist/app.js",
"run:dev": "nodemon --quiet dist/app.js",
"start:dev": "webpack --env=dev",
"start:debug": "webpack --env=debug",
"start": "node dist/app.js"
"build": "npm exec m-cli --config=production",
"start:debug": "m-cli --config=debug"
},
"author": "",
"license": "ISC",
"dependencies": {
"@koa/cors": "^3.1.0",
"axios": "^0.19.2",
"chokidar": "^3.3.1",
"cron": "^1.8.2",
"koa": "^2.11.0",
"koa-joi-router": "^6.0.2",
"@m-backend/cli": "^1.1.14",
"@m-backend/core": "^1.1.6",
"koa-ratelimit": "^4.2.1",
"moment": "^2.24.0",
"pg": "^8.2.0",
"reflect-metadata": "^0.1.13",
"tsyringe": "^4.1.0",
"winston": "^3.2.1"
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
"@types/cron": "^1.7.2",
"@types/koa": "^2.11.2",
"@types/koa-joi-router": "^5.2.3",
"@types/koa__cors": "^3.1.1",
"@types/koa-joi-router": "^8.0.0",
"@types/koa-ratelimit": "^4.2.1",
"@types/koa__cors": "^3.0.1",
"@types/moment": "^2.13.0",
"@types/node": "^13.9.4",
"@types/pg": "^7.14.3",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.1.1",
"nodemon": "^2.0.2",
"ts-loader": "^6.2.2",
"ts-transform-auto-require": "^1.1.0",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "^3.8.3",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11",
"webpack-node-externals": "^1.7.2",
"webpack-shell-plugin-next": "^1.1.7"
"@types/pg": "^7.14.3"
}
}
{
"port": 3015,
"externalUrl": ["https://www.metromobilite.fr","https://www.mobilites-m.fr","http://localhost:4200"],
"logger": {
"level": "silly",
"transports": [
{ "type": "console" },
{
"type": "file",
"options": {
"filename": "affluence.log",
"maxsize": 100000,
"maxFiles": 10,
"tailable": true,
"zippedArchive": true
}
}
]
}
}
import MainApp from '@m-backend/core/src/lib/applications/main.app'
import { CoreModule, Module } from "@m-backend/core";
import UserReportController from './user-report/user-report.controller';
import DbCron from './db.cron';
import CorsMiddleware from './cors.middleware';
import JoiErrors from './joi-error.middleware';
import RateLimitMiddleware from './ratelimit.middleware';
@Module({
imports: [CoreModule],
applications: [MainApp],
controllers: [UserReportController],
crons: [DbCron],
middlewares: [CorsMiddleware, RateLimitMiddleware, JoiErrors]
})
export class AppModule { }
import { Context, Next } from 'koa';
import cors from '@koa/cors';
import { Middleware } from "@decorators/middleware.decorator";
import { MiddlewareInterface } from "@core/middleware.interface";
import { config } from '@core/config';
import { config, Middleware, MiddlewareInterface } from '@m-backend/core';
@Middleware({ autoloadIn: 'main' })
export default class CorsMiddleware implements MiddlewareInterface {
async use(ctx: Context, next: Next) {
return await cors({
origin: (ctx) => {
let origin = ctx.header.origin;
if (config.app.externalUrl.indexOf(origin) !== -1) {
return origin;
}
return false;
},
origin: config.app.externalUrl,
allowMethods: 'GET,HEAD,PUT,POST,DELETE'
})(ctx, next);
}
......
import { CronJob } from "cron";
import { OnTick, OnInit, OnComplete } from "@core/cron.interface";
import { Cron } from "@decorators/cron.decorator";
import { LoggerService } from "@core/logger.service";
import { Cron, OnTick, OnInit, LoggerService, config } from "@m-backend/core";
import { ClientFactoryService } from "./db/client.factory";
import { config } from '@core/config';
import { loggers } from "winston";
import { UserReportService } from "./user-report/user-report.service";
@Cron({
......@@ -28,7 +23,7 @@ export default class DbCron implements OnTick, OnInit {
await client.query(`CREATE TABLE IF NOT EXISTS ${config.db.affluence.schema}.user_report (stop_code text NOT NULL,time timestamp with time zone NOT NULL,data json, CONSTRAINT user_report_pk PRIMARY KEY(time,stop_code)) WITH (oids = false)`);
client.end();
} catch (e) {
} catch (e: any) {
client?.end();
this.log.logger.error(`[cron][DbCron][onInit] ${e}`);
}
......@@ -45,7 +40,7 @@ export default class DbCron implements OnTick, OnInit {
}
client.end();
} catch (e) {
} catch (e: any) {
client?.end();
this.log.logger.error(`[cron][DbCron][onTick] ${e}`);
}
......
import { Client, QueryResult, QueryConfig } from "pg";
import { config } from '@core/config';
import { LoggerService } from "@core/logger.service";
import { config, LoggerService } from "@m-backend/core";
import { Client, QueryConfig } from "pg";
export class AffluenceClient extends Client {
......@@ -12,18 +11,7 @@ export class AffluenceClient extends Client {
await super.connect();
await this.query(`set search_path = '${config.db.affluence.schema}';`);
}
/*async insertReturning(table: string, columns: string, values: string, params: Array<any>): Promise<number> {
try {
let query = `insert into ${table} ${columns} values ${values} returning ident;`;
this.log.logger.silly(`[client][AffluenceClient][insertReturning] ${query}`);
let result = await this.query<{ ident: number }>(query, params);
if (result.rows.length === 0) throw new Error(`${table} : Unable to perform ${result.command}.`);
return result.rows[0].ident;
} catch (e) {
if (e.details) this.log.logger.error(`[client][AffluenceClient][insertReturning] ${e.details}`);
throw (e);
}
}*/
async insert(table: string, columns: string, values: string, params: Array<any>, name?: string): Promise<boolean> {
try {
let queryConfig: QueryConfig = {
......@@ -35,29 +23,9 @@ export class AffluenceClient extends Client {
let result = await this.query<{ ident: number }>(queryConfig);
if (result.rowCount !== 1) throw new Error(`${table} : Unable to perform ${result.command}.`);
return true;
} catch (e) {
} catch (e: any) {
if (e.details) this.log.logger.error(`[client][AffluenceClient][insert] ${e.details}`);
throw (e);
}
}
/*async update(table: string, columns: string, values: string, where: string, params: Array<any>) {
try {
let query = `update ${table} set ${columns} = ${values} where 1=1 ${where};`;
this.log.logger.silly(`[client][AffluenceClient][update] ${query}`);
return await this.query(query, params);
} catch (e) {
if (e.details) this.log.logger.error(`[client][AffluenceClient][update] ${e.details}`);
throw (e);
}
}
async delete(table: string, where: string, params: Array<any>) {
try {
let query = `delete from ${table} where 1=1 ${where};`;
this.log.logger.silly(`[client][AffluenceClient][delete] ${query}`);
return await this.query(query, params);
} catch (e) {
if (e.details) this.log.logger.error(`[client][AffluenceClient][delete] ${e.details}`);
throw (e);
}
}*/
}
import { AffluenceClient } from "@app/db/affluence.client";
import { LoggerService } from "@core/logger.service";
import { Service } from "@decorators/service.decorator";
import { Service, LoggerService } from "@m-backend/core";
@Service()
export class ClientFactoryService {
......
import { Middleware } from "@decorators/middleware.decorator";
import { MiddlewareInterface } from "@core/middleware.interface";
import { Middleware, MiddlewareInterface, LoggerService } from "@m-backend/core";
import { Next, Context } from "koa";
import { LoggerService } from "@core/logger.service";
@Middleware()
export default class JoiErrors implements MiddlewareInterface {
......@@ -11,7 +9,7 @@ export default class JoiErrors implements MiddlewareInterface {
async use(ctx: Context, next: Next) {
try {
await next();
} catch (e) {
} catch (e: any) {
if (e.isJoi) {
this.log.logger.error(`[middleware][JoiErrors] ${e}`);
}
......
import { Middleware, MiddlewareInterface } from '@m-backend/core';
import { Context, Next } from 'koa';
import limit from 'koa-ratelimit';
import { Middleware } from "@decorators/middleware.decorator";
import { MiddlewareInterface } from "@core/middleware.interface";
const db = new Map();
......@@ -15,7 +14,11 @@ export default class RateLimitMiddleware implements MiddlewareInterface {
duration: 1000 * 60, //1 min
max: 2,
id: (ctx) => {
return (ctx.request.header['x-real-ip'] ? ctx.request.header['x-real-ip'] : ctx.request.ip);
let header;
if (Array.isArray(ctx.request.header['x-real-ip'])) header = ctx.request.header['x-real-ip'][0];
else header = ctx.request.header['x-real-ip'];
return (header ? header : ctx.request.ip);
},
errorMessage: 'Qu\'est ce qu\'il nous fait Noureyef, là ?'
//blackList: ['127.0.0.1']
......
import { Context } from "koa";
import { Controller, Post, AttachMiddleware } from "@decorators/controller.decorator";
import { LoggerService } from "@core/logger.service";
import { AffluenceClient } from "@app/db/affluence.client";
import JoiErrors from "@app/joi-error.middleware";
import { Joi } from "koa-joi-router";
import { ClientFactoryService } from "@app/db/client.factory";
import { UserReportService } from "./user-report.service";
import RateLimitMiddleware from "@app/ratelimit.middleware";
import { AttachMiddleware, Controller, LoggerService, Post } from "@m-backend/core";
@AttachMiddleware([JoiErrors])
@Controller()
......@@ -24,8 +22,8 @@ export default class UserReportController {
time: Joi.date().timestamp().required(),
stopId: Joi.string().required(),
routeId: Joi.string().required(),
directionId: Joi.number().only(1, 2).required(),
occupancy: Joi.string().only('LOW', 'MIDDLE', 'HIGH').required(),
directionId: Joi.number().valid(1, 2).required(),
occupancy: Joi.string().valid('LOW', 'MIDDLE', 'HIGH').required(),
headsign: Joi.string()
}
})
......@@ -45,7 +43,7 @@ export default class UserReportController {
})
client = this.clientFactory.getAffluenceClient();
ctx.response.body = { success: true, status: 200 };
} catch (e) {
} catch (e: any) {
client?.end();
this.log.logger.error(`[controller][UserReportController][postUserReport] ${e}`);
ctx.response.body = { errors: [{ code: 'OPERATION_FAILED', message: e.message }] };
......
import { Service } from "@decorators/service.decorator";
import { config } from '@core/config';
import { Service, LoggerService } from "@m-backend/core";
import { UserReport } from "./user-report.model";
import { LoggerService } from "@core/logger.service";
@Service()
export class UserReportService {
......
import Koa from 'koa';
import { LoggerService } from '@core/logger.service';
import { MiddlewareInterface } from '@core/middleware.interface';
import { MIDDLEWARE_META } from "@decorators/middleware.decorator";
export abstract class AbstractApp {
public instance: Koa;
// Name and port properties are injected during the decorator 'application' function call.
public name: string | undefined;
public port: string | undefined;
constructor(public log: LoggerService, middlewares: MiddlewareInterface[]) {
this.instance = new Koa();
middlewares.forEach(middleware => {
const autoloadIn = Reflect.getMetadata(MIDDLEWARE_META.AUTOLOAD_IN, middleware.constructor);
if (autoloadIn === this.name) {
this.instance.use(async (ctx, next) => {
return await middleware.use(ctx, next);
});
}
});
}
public listen() {
this.instance.listen(this.port, () => this.log.logger.info(`[application][${this.name}] listening on http://localhost:${this.port}`));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment