🚀Bumped version to v0.3.1

This commit is contained in:
Nicolai Ort 2021-02-13 21:10:26 +01:00
parent 2d031dae03
commit dcde424b77
6 changed files with 176 additions and 176 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-document-server", "name": "@odit/lfk-document-server",
"version": "0.3.0", "version": "0.3.1",
"description": "The document generation server for the LfK! runner system. This generates certificates, sponsoring aggreements and more", "description": "The document generation server for the LfK! runner system. This generates certificates, sponsoring aggreements and more",
"main": "src/app.ts", "main": "src/app.ts",
"scripts": { "scripts": {

View File

@ -1,33 +1,33 @@
import { MetadataArgsStorage } from 'routing-controllers'; import { MetadataArgsStorage } from 'routing-controllers';
import { routingControllersToSpec } from 'routing-controllers-openapi'; import { routingControllersToSpec } from 'routing-controllers-openapi';
import { config } from './config'; import { config } from './config';
/** /**
* This function generates a the openapi spec from route metadata and type schemas. * This function generates a the openapi spec from route metadata and type schemas.
* @param storage MetadataArgsStorage object generated by routing-controllers. * @param storage MetadataArgsStorage object generated by routing-controllers.
* @param schemas MetadataArgsStorage object generated by class-validator-jsonschema. * @param schemas MetadataArgsStorage object generated by class-validator-jsonschema.
*/ */
export function generateSpec(storage: MetadataArgsStorage, schemas) { export function generateSpec(storage: MetadataArgsStorage, schemas) {
return routingControllersToSpec( return routingControllersToSpec(
storage, storage,
{}, {},
{ {
components: { components: {
schemas, schemas,
"securitySchemes": { "securitySchemes": {
"AuthToken": { "AuthToken": {
"type": "apiKey", "type": "apiKey",
"in": "query", "in": "query",
"name": "key", "name": "key",
description: "A simple api key. See the README's env section for more details." description: "A simple api key. See the README's env section for more details."
} }
} }
}, },
info: { info: {
description: "The the API for the LfK! document server.", description: "The the API for the LfK! document server.",
title: "LfK! document server API", title: "LfK! document server API",
version: config.version version: config.version
}, },
} }
); );
} }

View File

@ -1,31 +1,31 @@
import consola from "consola"; import consola from "consola";
import "reflect-metadata"; import "reflect-metadata";
import { createExpressServer } from "routing-controllers"; import { createExpressServer } from "routing-controllers";
import { config, e as errors } from './config'; import { config, e as errors } from './config';
import loaders from "./loaders/index"; import loaders from "./loaders/index";
import AuthChecker from './middlewares/AuthChecker'; import AuthChecker from './middlewares/AuthChecker';
import { ErrorHandler } from './middlewares/ErrorHandler'; import { ErrorHandler } from './middlewares/ErrorHandler';
const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts'; const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts';
const app = createExpressServer({ const app = createExpressServer({
middlewares: [ErrorHandler], middlewares: [ErrorHandler],
authorizationChecker: AuthChecker, authorizationChecker: AuthChecker,
development: config.development, development: config.development,
cors: true, cors: true,
controllers: [`${__dirname}/controllers/*.${CONTROLLERS_FILE_EXTENSION}`], controllers: [`${__dirname}/controllers/*.${CONTROLLERS_FILE_EXTENSION}`],
}); });
async function main() { async function main() {
await loaders(app); await loaders(app);
app.listen(config.internal_port, () => { app.listen(config.internal_port, () => {
consola.success( consola.success(
`⚡️[server]: Server is running at http://localhost:${config.internal_port}` `⚡️[server]: Server is running at http://localhost:${config.internal_port}`
); );
}); });
} }
if (errors === 0) { if (errors === 0) {
main(); main();
} else { } else {
consola.error("error"); consola.error("error");
// something's wrong // something's wrong
} }

View File

@ -1,52 +1,52 @@
import consola from "consola"; import consola from "consola";
import { config as configDotenv } from 'dotenv'; import { config as configDotenv } from 'dotenv';
configDotenv(); configDotenv();
export const config = { export const config = {
internal_port: parseInt(process.env.APP_PORT) || 4010, internal_port: parseInt(process.env.APP_PORT) || 4010,
development: process.env.NODE_ENV === "production", development: process.env.NODE_ENV === "production",
version: process.env.VERSION || require('../package.json').version, version: process.env.VERSION || require('../package.json').version,
eventname: process.env.EVENT_NAME || "Please set the event name", eventname: process.env.EVENT_NAME || "Please set the event name",
currency_symbol: process.env.CURRENCY_SYMBOL || "€", currency_symbol: process.env.CURRENCY_SYMBOL || "€",
sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10", sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10",
codeformat: process.env.CODEFORMAT || "qrcode", codeformat: process.env.CODEFORMAT || "qrcode",
sponor_logos: getSponsorLogos(), sponor_logos: getSponsorLogos(),
api_key: getApiKey(), api_key: getApiKey(),
} }
let errors = 0 let errors = 0
if (typeof config.internal_port !== "number") { if (typeof config.internal_port !== "number") {
errors++ errors++
} }
if (typeof config.development !== "boolean") { if (typeof config.development !== "boolean") {
errors++ errors++
} }
function getSponsorLogos(): string[] { function getSponsorLogos(): string[] {
try { try {
const logos = JSON.parse(process.env.SPONOR_LOGOS); const logos = JSON.parse(process.env.SPONOR_LOGOS);
if (!Array.isArray(logos)) { throw new Error("Not an array.") } if (!Array.isArray(logos)) { throw new Error("Not an array.") }
return logos; return logos;
} catch (error) { } catch (error) {
return ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="]; return ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="];
} }
} }
function getApiKey(): string { function getApiKey(): string {
const key = process.env.API_KEY; const key = process.env.API_KEY;
if (!key) { if (!key) {
consola.info("No API key set - generating a random one..."); consola.info("No API key set - generating a random one...");
let result = ''; let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length; const charactersLength = characters.length;
for (var i = 0; i < 64; i++) { for (var i = 0; i < 64; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength)); result += characters.charAt(Math.floor(Math.random() * charactersLength));
} }
consola.info(`API KEY: ${result}`) consola.info(`API KEY: ${result}`)
return result; return result;
} }
if (key.length < 64) { if (key.length < 64) {
consola.error(`API key is too short - minimum: 64, current: ${key.length}`) consola.error(`API key is too short - minimum: 64, current: ${key.length}`)
throw new Error("API_KEY too short.") throw new Error("API_KEY too short.")
} }
return key return key
} }
export let e = errors export let e = errors

View File

@ -1,48 +1,48 @@
import { Authorized, Body, JsonController, Post, QueryParam, Res } from 'routing-controllers'; import { Authorized, Body, JsonController, Post, QueryParam, Res } from 'routing-controllers';
import { OpenAPI } from 'routing-controllers-openapi'; import { OpenAPI } from 'routing-controllers-openapi';
import { Runner } from '../models/Runner'; import { Runner } from '../models/Runner';
import { RunnerCard } from '../models/RunnerCard'; import { RunnerCard } from '../models/RunnerCard';
import { PdfCreator } from '../PdfCreator'; import { PdfCreator } from '../PdfCreator';
/** /**
* The pdf controller handels all endpoints concerning pdf generation. * The pdf controller handels all endpoints concerning pdf generation.
* It therefore is the hearth of the document-generation server's endpoints. * It therefore is the hearth of the document-generation server's endpoints.
* All endpoints have to accept a locale query-param to support i18n. * All endpoints have to accept a locale query-param to support i18n.
*/ */
@JsonController() @JsonController()
@Authorized() @Authorized()
@OpenAPI({ security: [{ "AuthToken": [] }] }) @OpenAPI({ security: [{ "AuthToken": [] }] })
export class PdfController { export class PdfController {
private pdf: PdfCreator = new PdfCreator(); private pdf: PdfCreator = new PdfCreator();
private initialized: boolean = false; private initialized: boolean = false;
@Post('/contracts') @Post('/contracts')
@OpenAPI({ description: "Generate Sponsoring contract pdfs from runner objects.<br>You can choose your prefered locale by passing the 'locale' query-param.<br> If you provide more than 100 runenrs this could take a moment or two (we tested up to 1000 runners in about 70sec so far)." }) @OpenAPI({ description: "Generate Sponsoring contract pdfs from runner objects.<br>You can choose your prefered locale by passing the 'locale' query-param.<br> If you provide more than 100 runenrs this could take a moment or two (we tested up to 1000 runners in about 70sec so far)." })
async generateContracts(@Body({ validate: true, options: { limit: "500mb" } }) runners: Runner | Runner[], @Res() res: any, @QueryParam("locale") locale: string, @QueryParam("codeformat") codeformat: string) { async generateContracts(@Body({ validate: true, options: { limit: "500mb" } }) runners: Runner | Runner[], @Res() res: any, @QueryParam("locale") locale: string, @QueryParam("codeformat") codeformat: string) {
if (!this.initialized) { if (!this.initialized) {
await this.pdf.init(); await this.pdf.init();
this.initialized = true; this.initialized = true;
} }
if (!Array.isArray(runners)) { if (!Array.isArray(runners)) {
runners = [runners]; runners = [runners];
} }
const contracts = await this.pdf.generateSponsoringContract(runners, locale, codeformat); const contracts = await this.pdf.generateSponsoringContract(runners, locale, codeformat);
res.setHeader('content-type', 'application/pdf'); res.setHeader('content-type', 'application/pdf');
return contracts; return contracts;
} }
@Post('/cards') @Post('/cards')
@OpenAPI({ description: "Generate runner card pdfs from runner card objects.<br>You can choose your prefered locale by passing the 'locale' query-param." }) @OpenAPI({ description: "Generate runner card pdfs from runner card objects.<br>You can choose your prefered locale by passing the 'locale' query-param." })
async generateCards(@Body({ validate: true, options: { limit: "500mb" } }) cards: RunnerCard | RunnerCard[], @Res() res: any, @QueryParam("locale") locale: string) { async generateCards(@Body({ validate: true, options: { limit: "500mb" } }) cards: RunnerCard | RunnerCard[], @Res() res: any, @QueryParam("locale") locale: string) {
if (!this.initialized) { if (!this.initialized) {
await this.pdf.init(); await this.pdf.init();
this.initialized = true; this.initialized = true;
} }
if (!Array.isArray(cards)) { if (!Array.isArray(cards)) {
cards = [cards]; cards = [cards];
} }
const contracts = await this.pdf.generateRunnerCards(cards, locale); const contracts = await this.pdf.generateRunnerCards(cards, locale);
res.setHeader('content-type', 'application/pdf'); res.setHeader('content-type', 'application/pdf');
return contracts; return contracts;
} }
} }

View File

@ -1,14 +1,14 @@
import { Action } from "routing-controllers"; import { Action } from "routing-controllers";
import { config } from '../config'; import { config } from '../config';
/** /**
* Handles authentication via jwt's (Bearer authorization header) for all api endpoints using the @Authorized decorator. * Handles authentication via jwt's (Bearer authorization header) for all api endpoints using the @Authorized decorator.
* @param action Routing-Controllers action object that provides request and response objects among other stuff. * @param action Routing-Controllers action object that provides request and response objects among other stuff.
* @param permissions The permissions that the endpoint using @Authorized requires. * @param permissions The permissions that the endpoint using @Authorized requires.
*/ */
const AuthChecker = async (action: Action) => { const AuthChecker = async (action: Action) => {
const provided_token = action.request.query.key; const provided_token = action.request.query.key;
return provided_token == config.api_key; return provided_token == config.api_key;
} }
export default AuthChecker export default AuthChecker