🚀Bumped version to v0.3.1
This commit is contained in:
parent
2d031dae03
commit
dcde424b77
@ -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": {
|
||||||
|
@ -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
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
62
src/app.ts
62
src/app.ts
@ -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
|
||||||
}
|
}
|
||||||
|
102
src/config.ts
102
src/config.ts
@ -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
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user