Compare commits
3 Commits
d0f35e1af6
...
2319347f03
| Author | SHA1 | Date | |
|---|---|---|---|
|
2319347f03
|
|||
|
a64e973269
|
|||
|
918d0efa1e
|
@ -17,24 +17,12 @@
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins":[
|
||||
"@typescript-eslint",
|
||||
"unused-imports",
|
||||
"promise"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "unused-imports", "promise"],
|
||||
"rules": {
|
||||
"no-console":[
|
||||
"error"
|
||||
],
|
||||
"no-console": ["error"],
|
||||
"semi": "off",
|
||||
"@typescript-eslint/semi":[
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"quotes":[
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"@typescript-eslint/semi": ["error", "always"],
|
||||
"quotes": ["error", "double"],
|
||||
"no-multiple-empty-lines": [
|
||||
"error",
|
||||
{
|
||||
@ -42,10 +30,7 @@
|
||||
"maxEOF": 1
|
||||
}
|
||||
],
|
||||
"spaced-comment":[
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"spaced-comment": ["error", "always"],
|
||||
"padding-line-between-statements": [
|
||||
"error",
|
||||
{
|
||||
@ -122,20 +107,42 @@
|
||||
"error",
|
||||
{
|
||||
"ignoreArrayIndexes": true,
|
||||
"ignore":[
|
||||
-1,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
3
|
||||
]
|
||||
"ignore": [-1, 1, 0, 2, 3]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn",
|
||||
"@typescript-eslint/no-unnecessary-condition": "warn",
|
||||
"@typescript-eslint/no-unnecessary-qualifier": "warn",
|
||||
"@typescript-eslint/no-unnecessary-type-arguments": "warn",
|
||||
"@typescript-eslint/member-ordering":"warn",
|
||||
"@typescript-eslint/member-ordering": [
|
||||
"error",
|
||||
{
|
||||
"default": {
|
||||
"memberTypes": [
|
||||
"public-static-field",
|
||||
"public-field",
|
||||
"protected-static-field",
|
||||
"protected-field",
|
||||
"private-static-field",
|
||||
"private-field",
|
||||
"public-constructor",
|
||||
"protected-constructor",
|
||||
"private-constructor",
|
||||
["public-get", "public-set"],
|
||||
["protected-get", "protected-set"],
|
||||
["private-get", "private-set"],
|
||||
"signature",
|
||||
"public-static-method",
|
||||
"public-method",
|
||||
"protected-static-method",
|
||||
"protected-method",
|
||||
"private-static-method",
|
||||
"private-method"
|
||||
],
|
||||
"order": "alphabetically"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
||||
"@typescript-eslint/prefer-as-const": "warn",
|
||||
"@typescript-eslint/prefer-for-of": "warn",
|
||||
@ -166,6 +173,6 @@
|
||||
],
|
||||
"indent": "off",
|
||||
"@typescript-eslint/indent": ["error", 2],
|
||||
"import/no-unresolved": ["off"]
|
||||
"import/no-unresolved": [2, { "ignore": ["^#.+$"] }]
|
||||
}
|
||||
}
|
||||
19
.vscode/launch.json
vendored
19
.vscode/launch.json
vendored
@ -1,11 +1,18 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Node Inspector",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": ["${workspaceRoot}/src/server.ts"],
|
||||
"runtimeArgs": ["--loader", "./src/myloader.mjs", "--experimental-top-level-await", "--experimental-specifier-resolution=node", "--experimental-specifier-resolution=node"],
|
||||
"args": ["${workspaceRoot}/src/app.ts", "start", "-p", "1234"],
|
||||
"runtimeArgs": [
|
||||
"--no-warnings",
|
||||
"--loader",
|
||||
"./src/myloader.mjs",
|
||||
"--experimental-modules",
|
||||
"--es-module-specifier-resolution=node"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"protocol": "inspector",
|
||||
"sourceMaps": true,
|
||||
@ -14,7 +21,9 @@
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"env": {
|
||||
"TS_NODE_IGNORE": "false",
|
||||
"NODE_ENV": "development"
|
||||
"NODE_ENV": "development",
|
||||
"TS_NODE_COMPILER": "ttypescript"
|
||||
}
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
25
.vscode/settings.json
vendored
25
.vscode/settings.json
vendored
@ -11,7 +11,7 @@
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.formatOnSave": false
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
@ -21,5 +21,26 @@
|
||||
},
|
||||
"typescript.referencesCodeLens.enabled": true,
|
||||
"appService.zipIgnorePattern": [".vscode{,/**}"],
|
||||
"appService.deploySubpath": ""
|
||||
"appService.deploySubpath": "",
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#2f7c47",
|
||||
"activityBar.activeBorder": "#422c74",
|
||||
"activityBar.background": "#2f7c47",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#422c74",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"sash.hoverBorder": "#2f7c47",
|
||||
"statusBar.background": "#215732",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#2f7c47",
|
||||
"statusBarItem.remoteBackground": "#215732",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#215732",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#21573299",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
"peacock.color": "#215732",
|
||||
"docwriter.hotkey.windows": "Alt + ."
|
||||
}
|
||||
33
package.json
33
package.json
@ -5,11 +5,13 @@
|
||||
"main": "server.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node --no-warnings --loader ./src/myloader.mjs --experimental-modules --es-module-specifier-resolution=node ./dist/server.js",
|
||||
"dev": "node --no-warnings --loader ./src/myloader.mjs --experimental-modules --es-module-specifier-resolution=node ./src/server.ts",
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"start": "node ./dist/app.js",
|
||||
"dev": "cross-env TS_NODE_COMPILER=ttypescript node --no-warnings --loader ./src/myloader.mjs --experimental-modules --es-module-specifier-resolution=node ./src/app.ts",
|
||||
"build": "ttsc -p tsconfig.json && tsc-alias -p tsconfig.json",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint --fix --ext ts src/"
|
||||
"lint": "eslint --fix --ext ts src/",
|
||||
"upgrade": "yarn upgrade --latest",
|
||||
"xxx": "node --loader ts-node/esm ./src/test.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -23,26 +25,43 @@
|
||||
"author": "Joshua Schnabel",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@automapper/classes": "^8.3.0",
|
||||
"@automapper/core": "^8.3.0",
|
||||
"@gquittet/graceful-server": "^2.5.2",
|
||||
"@nestjs/common": "^8.4.3",
|
||||
"@nestjs/core": "^8.4.3",
|
||||
"@nestjs/passport": "^8.2.1",
|
||||
"@nestjs/platform-fastify": "^8.4.3",
|
||||
"args-command-parser": "^1.2.4",
|
||||
"cli-color": "^2.0.1",
|
||||
"fastify": "^3.25.3",
|
||||
"fastify-plugin": "^3.0.0",
|
||||
"fastify-sensible": "^3.1.2",
|
||||
"passport": "^0.5.2",
|
||||
"passport-http": "^0.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.5.5",
|
||||
"winston": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"@types/cli-color": "^2.0.2",
|
||||
"@types/node": "^17.0.9",
|
||||
"@types/passport": "^1.0.7",
|
||||
"@types/passport-http": "^0.3.9",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"builder-pattern": "^1.3.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"tsc-alias": "^1.6.6",
|
||||
"ttypescript": "^1.5.13",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"@logger/*": "./dist/logger/*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
class InfrastructureAutoLoadable {
|
||||
public load (): void {console.log("TEST");}
|
||||
}
|
||||
|
||||
export default new InfrastructureAutoLoadable();
|
||||
10
src/app.module.ts
Normal file
10
src/app.module.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { FrameworkModule } from "#framework/framework.module";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { PersistenceModule } from "./application/persistance.module";
|
||||
import { RestModule } from "./application/rest.module";
|
||||
|
||||
@Module({
|
||||
imports: [RestModule, FrameworkModule, PersistenceModule]
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class AppModule {}
|
||||
76
src/app.ts
Normal file
76
src/app.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
||||
/* eslint-disable no-console */
|
||||
import clc from "cli-color";
|
||||
import App, { AppCommand } from "#app";
|
||||
import ConfigurationDefinition, { DefinitionType } from "#configuration/configurationDefinition";
|
||||
import definitionValidators from "#configuration/configurationValidatior";
|
||||
import Server from "#framework/server/server";
|
||||
import getlogger, { defaultLogger } from "#logger";
|
||||
|
||||
const app: App = new (class extends App {
|
||||
public options (setDefinitions: (...definitions: ConfigurationDefinition[]) => void): void {
|
||||
setDefinitions(new ConfigurationDefinition()
|
||||
.withName("port")
|
||||
.withType(DefinitionType.Number)
|
||||
.withArgName("port", "p")
|
||||
.withEnvName("port")
|
||||
.withDescription("Port of the server.")
|
||||
.isRequired()
|
||||
.relevantToCommands("start")
|
||||
.withValidator(definitionValidators.validatePort()),
|
||||
new ConfigurationDefinition()
|
||||
.withName("host")
|
||||
.withType(DefinitionType.String)
|
||||
.withArgName("host", "h")
|
||||
.withEnvName("host")
|
||||
.withDefault("127.0.0.1")
|
||||
.withDescription("Host name under which the server should listen for requests.")
|
||||
.relevantToCommands("start")
|
||||
.withValidator(definitionValidators.validateHost()));
|
||||
}
|
||||
|
||||
public commmands (setCommands: (...commands: AppCommand[]) => void): void {
|
||||
setCommands(new (class implements AppCommand {
|
||||
public getName (): string {
|
||||
return "start";
|
||||
}
|
||||
|
||||
public getDescriptions (): string {
|
||||
return "Start the server.";
|
||||
}
|
||||
|
||||
public run (): void {
|
||||
const server = new Server();
|
||||
const logger = getlogger("server");
|
||||
server.setup().then(() => {
|
||||
return server.start();
|
||||
}).catch((err) => logger.error(err));
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public printBanner (): void {
|
||||
const crot = clc.xterm(9);
|
||||
const cyellow = clc.xterm(11);
|
||||
const cwhite = clc.xterm(15);
|
||||
const cpurple = clc.xterm(13);
|
||||
console.log(crot(" ") + cwhite("_ _ _ _ _ _ ") + cpurple(" ____ _ "));
|
||||
console.log(crot(" \\ \\ ") + cwhite(" / \\ _ __ | |_| |__ (_) | | ") + cpurple("| __ ) __ _ ___| | ___ _ _ __ "));
|
||||
console.log(crot(" |__/ _ .-. ") + cwhite(" / _ \\ | '_ \\| __| '_ \\| | | | ") + cpurple("| _ \\ / _` |/ __| |/ / | | | '_ \\ "));
|
||||
console.log(crot("(") + cyellow("o_o") + crot(")(_`>( ) ") + cwhite(" / ___ \\| | | | |_| | | | | | |") + cpurple(" | |_) | (_| | (__| <| |_| | |_) |"));
|
||||
console.log(crot(" { }//||\\\\`-' ") + cwhite(" /_/ \\_\\_| |_|\\__|_| |_|_|_|_| ") + cpurple("|____/ \\__,_|\\___|_|\\_\\\\__,_| .__/ "));
|
||||
console.log(cpurple(" |_| "));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
app.execute();
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
defaultLogger.error(error.message);
|
||||
} else {
|
||||
defaultLogger.error("ssss" + error);
|
||||
}
|
||||
}
|
||||
|
||||
65
src/application/domain/repo/repo.entity.ts
Normal file
65
src/application/domain/repo/repo.entity.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { Aggregate, Identifier, IdentifierGenerator } from "#ddd";
|
||||
import { randomUUID } from "crypto";
|
||||
import { UserAssignment } from "./userAssignment.entity";
|
||||
|
||||
export class RepoId extends Identifier<string> {
|
||||
private readonly _value: string;
|
||||
|
||||
public constructor (value: string) {
|
||||
super();
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
public get value (): string {
|
||||
return this._value;
|
||||
}
|
||||
}
|
||||
export default class Repo extends Aggregate<RepoId>{
|
||||
|
||||
private readonly _id?: RepoId;
|
||||
private readonly _name: string;
|
||||
|
||||
private readonly _userAssignment: UserAssignment[] = [];
|
||||
|
||||
public constructor (id: RepoId| undefined, name: string, userAssignment: UserAssignment[]) {
|
||||
super();
|
||||
this._id = id;
|
||||
this._name = name;
|
||||
this._userAssignment = userAssignment;
|
||||
}
|
||||
|
||||
public get id (): RepoId | undefined {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get name (): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get path (): string {
|
||||
return "/" + this._name;
|
||||
}
|
||||
|
||||
public get userAssignment (): UserAssignment[] {
|
||||
return this._userAssignment;
|
||||
}
|
||||
|
||||
public addUserAssignment (userAssignment: UserAssignment): void {
|
||||
this._userAssignment.push(userAssignment);
|
||||
}
|
||||
|
||||
public removeUserAssignment (userAssignment: UserAssignment): void {
|
||||
this._userAssignment.filter(item => item !== userAssignment);
|
||||
}
|
||||
|
||||
// public static builder (): IBuilder<Repo> {
|
||||
// return <IBuilder<Repo>> <unknown>Builder(Repo);
|
||||
// }
|
||||
}
|
||||
|
||||
export class RepoIdGenerator implements IdentifierGenerator<RepoId> {
|
||||
public generate (): RepoId {
|
||||
return new RepoId(randomUUID());
|
||||
}
|
||||
}
|
||||
|
||||
6
src/application/domain/repo/repo.repo.ts
Normal file
6
src/application/domain/repo/repo.repo.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {Repository} from "#ddd";
|
||||
import Repo, { RepoId } from "./repo.entity";
|
||||
|
||||
export const RepoRepository = "RepoRepository";
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export default interface RepoRepository extends Repository<RepoId, Repo> {}
|
||||
30
src/application/domain/repo/userAssignment.entity.ts
Normal file
30
src/application/domain/repo/userAssignment.entity.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { ValueObject } from "#ddd";
|
||||
import { UserId } from "../user/user.entity";
|
||||
// import { Builder, IBuilder } from "builder-pattern";
|
||||
|
||||
enum UserRight {
|
||||
Read = "read",
|
||||
AppendOnly = "append",
|
||||
Write = "write"
|
||||
}
|
||||
|
||||
export class UserAssignment extends ValueObject {
|
||||
private readonly _right: UserRight;
|
||||
|
||||
private readonly _userId: UserId;
|
||||
|
||||
private constructor (userId: UserId, right: UserRight) {
|
||||
super();
|
||||
this._userId = userId;
|
||||
this._right = right;
|
||||
}
|
||||
|
||||
public get id (): UserId {
|
||||
return this._userId;
|
||||
}
|
||||
|
||||
public get right (): UserRight {
|
||||
return this._right;
|
||||
}
|
||||
|
||||
}
|
||||
42
src/application/domain/user/user.entity.ts
Normal file
42
src/application/domain/user/user.entity.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { Identifier, Aggregate} from "#ddd";
|
||||
|
||||
export default class User extends Aggregate<UserId>{
|
||||
private readonly _id: UserId | undefined;
|
||||
|
||||
private readonly _name: string;
|
||||
|
||||
private readonly _password: string;
|
||||
|
||||
public constructor (id: UserId | undefined, name: string, password: string) {
|
||||
super();
|
||||
this._id = id;
|
||||
this._name = name;
|
||||
this._password = password;
|
||||
}
|
||||
|
||||
public get id (): UserId | undefined {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get name (): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get password (): string {
|
||||
return this._password;
|
||||
}
|
||||
|
||||
}
|
||||
export class UserId extends Identifier<string> {
|
||||
private readonly _value: string;
|
||||
|
||||
public constructor (value: string) {
|
||||
super();
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
public get value (): string {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
5
src/application/domain/user/user.repo.ts
Normal file
5
src/application/domain/user/user.repo.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import User, { UserId } from "./user.entity";
|
||||
import {Repository} from "#ddd";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export default interface UserRepository extends Repository<UserId, User> {}
|
||||
@ -0,0 +1,46 @@
|
||||
import Repo, { RepoId } from "#domain/repo/repo.entity";
|
||||
import RepoRepository from "#domain/repo/repo.repo";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
@Injectable()
|
||||
export default class RepoFilePersistence implements RepoRepository {
|
||||
|
||||
private readonly values: Map<RepoId, Repo> = new Map();
|
||||
|
||||
public constructor () {
|
||||
const x = new RepoId("xxxx");
|
||||
const y = Repo.instantiate(x, "/xxx");
|
||||
this.values.set(x, y);
|
||||
}
|
||||
|
||||
public deleteAggregate (aggregate: Repo): void {
|
||||
if(aggregate.id)
|
||||
this.values.delete(aggregate.id);
|
||||
}
|
||||
|
||||
public deleteAggregateById (id: RepoId): void{
|
||||
this.values.delete(id);
|
||||
}
|
||||
|
||||
public getAggregate (identitiy: RepoId): Repo {
|
||||
const v: Repo | undefined = this.values.get(identitiy);
|
||||
if(v !== undefined)
|
||||
return v;
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
public getAggregates (): Repo[] {
|
||||
return Array.from(this.values.values());
|
||||
}
|
||||
|
||||
public storeAggregate (aggregate: Repo): Repo {
|
||||
let id = aggregate.id;
|
||||
if(id) {
|
||||
id = new RepoId("");
|
||||
aggregate = Repo.instantiate(id, aggregate.path);
|
||||
}
|
||||
this.values.set(id as RepoId, aggregate);
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import { constructUsing, createMap, forMember, mapFrom } from "@automapper/core";
|
||||
import Repo, { RepoId } from "#domain/repo/repo.entity";
|
||||
import { mapper } from "#framework/mapper/mapper";
|
||||
|
||||
export class RepoDto {
|
||||
public id!: string;
|
||||
public path!: string;
|
||||
}
|
||||
|
||||
createMap(mapper, Repo, RepoDto, forMember(
|
||||
(destination: RepoDto) => destination.id, mapFrom(s => s.id?.value)
|
||||
));
|
||||
|
||||
createMap(mapper, RepoDto, Repo,
|
||||
constructUsing((sourceObject, _destinationIdentifier): Repo => {
|
||||
return new Repo(new RepoId(sourceObject.id), sourceObject.path);
|
||||
})
|
||||
);
|
||||
@ -0,0 +1,30 @@
|
||||
|
||||
import Repo from "#domain/repo/repo.entity";
|
||||
import Repository, { RepoRepository } from "#domain/repo/repo.repo";
|
||||
import { mapper } from "#framework/mapper/mapper";
|
||||
import { Body, Controller, Get, Inject, Post, UseGuards } from "@nestjs/common";
|
||||
import { AuthGuard } from "@nestjs/passport";
|
||||
import { RepoDto } from "./dtos/repo.dto";
|
||||
|
||||
@Controller("api/management")
|
||||
export class ManagementController {
|
||||
@Inject(RepoRepository)
|
||||
private readonly repos!: Repository;
|
||||
|
||||
@Post()
|
||||
@UseGuards(AuthGuard("basic"))
|
||||
public create (@Body() repo: RepoDto): RepoDto {
|
||||
console.log(repo);
|
||||
console.log(mapper.map(repo, RepoDto, Repo));
|
||||
return new RepoDto();
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UseGuards(AuthGuard("basic"))
|
||||
public findAll (): RepoDto[] {
|
||||
console.log(this.repos.getAggregates());
|
||||
console.log(mapper.mapArray<Repo, RepoDto>(this.repos.getAggregates(), Repo, RepoDto));
|
||||
return mapper.mapArray<Repo, RepoDto>(this.repos.getAggregates(), Repo, RepoDto);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
|
||||
import { Controller, Get } from "@nestjs/common";
|
||||
|
||||
@Controller("repo")
|
||||
export class RepoController {
|
||||
|
||||
@Get()
|
||||
public findAll (): string {
|
||||
return "This action returns all cats";
|
||||
}
|
||||
}
|
||||
16
src/application/persistance.module.ts
Normal file
16
src/application/persistance.module.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-extraneous-class */
|
||||
|
||||
import {RepoRepository} from "#domain/repo/repo.repo";
|
||||
import { Module } from "@nestjs/common";
|
||||
import RepoFilePersistence from "./infrastructure/persistence/file/repo.persistence";
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
useClass: RepoFilePersistence,
|
||||
provide: RepoRepository
|
||||
}
|
||||
],
|
||||
exports: [RepoRepository]
|
||||
})
|
||||
export class PersistenceModule {}
|
||||
12
src/application/rest.module.ts
Normal file
12
src/application/rest.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-extraneous-class */
|
||||
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ManagementController } from "./infrastructure/presentation/rest/management.controller";
|
||||
import { RepoController } from "./infrastructure/presentation/rest/repo.controller";
|
||||
import { PersistenceModule } from "./persistance.module";
|
||||
|
||||
@Module({
|
||||
controllers: [ManagementController, RepoController],
|
||||
imports: [PersistenceModule]
|
||||
})
|
||||
export class RestModule {}
|
||||
77
src/framework/app/app.ts
Normal file
77
src/framework/app/app.ts
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
||||
/* eslint-disable no-console */
|
||||
import configuration, {initConfiguration} from "#configuration";
|
||||
import ConfigurationDefinition from "#configuration/configurationDefinition";
|
||||
import getLogger from "#logger";
|
||||
import { Logger } from "winston";
|
||||
import AppHelp from "./appHelp";
|
||||
|
||||
export abstract class AppCommand {
|
||||
public abstract getDescriptions (): string;
|
||||
public abstract run (): void;
|
||||
|
||||
public abstract getName (): string;
|
||||
}
|
||||
|
||||
export default abstract class App {
|
||||
|
||||
private readonly commands: Map<string, AppCommand> = new Map();
|
||||
private readonly appHelp: AppHelp;
|
||||
private readonly logger: Logger;
|
||||
|
||||
public constructor () {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const that = this;
|
||||
this.appHelp = new AppHelp();
|
||||
this.logger = getLogger("app");
|
||||
this.addCommmand(new (class implements AppCommand {
|
||||
public getName (): string {
|
||||
return "help";
|
||||
}
|
||||
|
||||
public getDescriptions (): string {
|
||||
return "Show help for this application.";
|
||||
}
|
||||
|
||||
public run (): void {
|
||||
that.appHelp.printHelp(that.commands);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* It executes the commands and options.
|
||||
*/
|
||||
public execute (): void {
|
||||
this.commmands((...definitions): void => {
|
||||
for (const definition of definitions) {
|
||||
this.addCommmand(definition);
|
||||
}
|
||||
console.log();
|
||||
});
|
||||
this.options((...defs): void => {
|
||||
initConfiguration("XXX", ...defs);
|
||||
});
|
||||
this.printBanner();
|
||||
configuration().validate();
|
||||
const name = configuration().getCommand();
|
||||
const command = this.commands.get(name);
|
||||
this.logger.info("Starting App with command '" + name + "'");
|
||||
if(command) {
|
||||
command.run();
|
||||
} else {
|
||||
throw new Error(name + " is not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private addCommmand (command: AppCommand): void {
|
||||
this.commands.set(command.getName(), command);
|
||||
}
|
||||
|
||||
public abstract options (setDefinitions: (...definitions: ConfigurationDefinition[]) => void): void;
|
||||
|
||||
public abstract commmands (setCommands: (...commands: AppCommand[]) => void): void;
|
||||
public abstract printBanner (): void;
|
||||
|
||||
}
|
||||
110
src/framework/app/appHelp.ts
Normal file
110
src/framework/app/appHelp.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import configuration from "#configuration";
|
||||
import clc from "cli-color";
|
||||
import ConfigurationDefinition from "#configuration/configurationDefinition";
|
||||
import { AppCommand } from "#app";
|
||||
|
||||
/* The `AppHelp` class is responsible for printing out the help message */
|
||||
export default class AppHelp {
|
||||
|
||||
/**
|
||||
* Prints the help message
|
||||
* @param commands - A map of all the commands that the program can run.
|
||||
*/
|
||||
public printHelp (commands: Map<string, AppCommand>): void {
|
||||
const programm = process.argv.slice(0, 2).join(" ");
|
||||
const underline = clc.underline;
|
||||
console.log(underline("Usage")+"\n");
|
||||
console.log("\t"+ programm + " [COMMAND] [OPTIONS]"+"\n");
|
||||
console.log(underline("Commands")+"\n");
|
||||
this.printCommands(commands);
|
||||
console.log();
|
||||
console.log(underline("Options"));
|
||||
console.log();
|
||||
this.printOptions((def) => def.getCommands().length >= 1);
|
||||
console.log();
|
||||
console.log(underline("Rules & Behavior"));
|
||||
console.log();
|
||||
this.printOptions((def) => def.getCommands().length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints all commands and their descriptions
|
||||
* @param commands - Map<string, AppCommand>
|
||||
*/
|
||||
private printCommands (commands: Map<string, AppCommand>): void {
|
||||
commands.forEach((v, k, _m) => {
|
||||
console.log("\t" + k + " - " + v.getDescriptions());
|
||||
const paramaters: string[] = [];
|
||||
// Find parameters for current command
|
||||
for (const def of this.getConfigurationDefinitions()) {
|
||||
if (def.getCommands().includes(k)) {
|
||||
paramaters.push(this.generateParameters(def));
|
||||
}
|
||||
}
|
||||
console.log("\t↳ " + k + " " + paramaters.join(" "));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out all the options that are available to the user
|
||||
* @param condition - Select witch options are printed.
|
||||
*/
|
||||
private printOptions (condition: (a: ConfigurationDefinition) => boolean): void {
|
||||
let maxLength = 0;
|
||||
/* Find longest String */
|
||||
for (const def of this.getConfigurationDefinitions()) {
|
||||
if (condition(def)) {
|
||||
const option = " -" + def.getArgName().sort((a, b) => a.length - b.length).join(" -");
|
||||
maxLength = Math.max(maxLength, option.length);
|
||||
}
|
||||
}
|
||||
for (const def of this.getConfigurationDefinitions()) {
|
||||
if (condition(def)) {
|
||||
const option = " -" + def.getArgName().sort((a, b) => a.length - b.length).join(" -");
|
||||
console.log("\t↳ " + (option.padEnd(maxLength, " ")) + " - " + def.getDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the parameters for the command line help
|
||||
* @param {ConfigurationDefinition} def - ConfigurationDefinition
|
||||
* @returns The `generateParameters` function returns a string that represents the parameters that are
|
||||
* being passed to the `CommandLine` object.
|
||||
*/
|
||||
private generateParameters (def: ConfigurationDefinition): string {
|
||||
const args: string[] = [];
|
||||
for (const argParam of def.getArgName()) {
|
||||
if (argParam.length === 1)
|
||||
args.push("-" + argParam + " <" + def.getName() + ">");
|
||||
else
|
||||
args.push("--" + argParam + " <" + def.getName() + ">");
|
||||
}
|
||||
let paramater = args.join(" | ");
|
||||
if (def.getArgName().length > 1)
|
||||
paramater = "(" + paramater + ")";
|
||||
if (!def.getRequired())
|
||||
paramater = "[" + paramater + "]";
|
||||
return paramater;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration definitions from the configuration object
|
||||
* @returns The configuration definitions are being returned.
|
||||
*/
|
||||
private getConfigurationDefinitions (): ConfigurationDefinition[] {
|
||||
return configuration().getDefinitions().sort((a, b) => {
|
||||
if (a.getRequired() && !b.getRequired())
|
||||
return -1;
|
||||
else if (!a.getRequired() && b.getRequired())
|
||||
return 1;
|
||||
|
||||
else
|
||||
return a.getName() < b.getName() ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
23
src/framework/auth/auth-basic.strategy.ts
Normal file
23
src/framework/auth/auth-basic.strategy.ts
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
import { BasicStrategy as Strategy } from "passport-http";
|
||||
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { FastifyRequest } from "fastify";
|
||||
|
||||
@Injectable()
|
||||
export class BasicStrategy extends PassportStrategy(Strategy) {
|
||||
public constructor () {
|
||||
super({
|
||||
passReqToCallback: true,
|
||||
realm: "XXXXX"
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async validate (req: FastifyRequest, username: string, password: string): Promise<boolean> {
|
||||
if (username === "test" && password === "abc") {
|
||||
return true;
|
||||
}
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
}
|
||||
116
src/framework/configuration/ConfigurationDefinition.ts
Normal file
116
src/framework/configuration/ConfigurationDefinition.ts
Normal file
@ -0,0 +1,116 @@
|
||||
import { DefinitionValidator } from "./configurationValidatior";
|
||||
|
||||
export enum DefinitionType {
|
||||
String = "string",
|
||||
Boolean = "boolean",
|
||||
File = "file",
|
||||
Number = "number"
|
||||
}
|
||||
|
||||
class ConfigurationDefinition implements ConfigurationDefinition {
|
||||
private name!: string;
|
||||
private envName!: string;
|
||||
private argName!: string[];
|
||||
private validator!: DefinitionValidator;
|
||||
private description!: string;
|
||||
private type!: DefinitionType;
|
||||
private multiple = false;
|
||||
private required = false;
|
||||
private commands: string[] = [];
|
||||
private defaultValue: string | undefined = undefined;
|
||||
|
||||
public getName (): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public getEnvName (): string {
|
||||
return this.envName;
|
||||
}
|
||||
|
||||
public getArgName (): string[] {
|
||||
return this.argName;
|
||||
}
|
||||
|
||||
public getValidator (): DefinitionValidator {
|
||||
return this.validator;
|
||||
}
|
||||
|
||||
public getDescription (): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public getType (): DefinitionType {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public getRequired (): boolean {
|
||||
return this.required;
|
||||
}
|
||||
|
||||
public getMultiple (): boolean {
|
||||
return this.multiple;
|
||||
}
|
||||
|
||||
public getCommands (): string[] {
|
||||
return this.commands;
|
||||
}
|
||||
|
||||
public getDefaultValue (): string[] | undefined {
|
||||
if(this.defaultValue) {
|
||||
return [this.defaultValue];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public withName (name: string): ConfigurationDefinition {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withEnvName (envName: string): ConfigurationDefinition {
|
||||
this.envName = envName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withArgName (...argName: string[]): ConfigurationDefinition {
|
||||
this.argName = argName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withValidator (validator: DefinitionValidator): ConfigurationDefinition {
|
||||
this.validator = validator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withDescription (description: string): ConfigurationDefinition {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withDefault (defaultValue: string): ConfigurationDefinition {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withType (type: DefinitionType): ConfigurationDefinition {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public relevantToCommands (...commands: string[]): ConfigurationDefinition {
|
||||
this.commands = commands;
|
||||
return this;
|
||||
}
|
||||
|
||||
public isRequired (): ConfigurationDefinition {
|
||||
this.required = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public isMultiple (): ConfigurationDefinition {
|
||||
this.multiple = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigurationDefinition;
|
||||
190
src/framework/configuration/configuration.ts
Normal file
190
src/framework/configuration/configuration.ts
Normal file
@ -0,0 +1,190 @@
|
||||
import ConfigurationDefinition, {DefinitionType} from "./configurationDefinition";
|
||||
import {parser, ArgCollection} from "args-command-parser";
|
||||
|
||||
class Configuration {
|
||||
|
||||
private readonly definitions: Map<string, ConfigurationDefinition> = new Map();
|
||||
private readonly values: Map<string, string[] | undefined> = new Map();
|
||||
private readonly defaultCommand: string;
|
||||
private readonly envPrefix: string;
|
||||
private readonly arguments: ArgCollection;
|
||||
public constructor (defaultCommand: string, envPrefix: string, ...definitions: ConfigurationDefinition[]) {
|
||||
this.arguments = parser();
|
||||
for (const definition of definitions) {
|
||||
this.definitions.set(definition.getName(), definition);
|
||||
this.values.set(definition.getName(), this.getInternalValue(definition));
|
||||
}
|
||||
this.defaultCommand = defaultCommand;
|
||||
this.envPrefix = envPrefix;
|
||||
}
|
||||
|
||||
public validate (): void {
|
||||
this.definitions.forEach((v, _k, _m) => {
|
||||
const def = v;
|
||||
const values = this.values.get(def.getName()) ?? [];
|
||||
if(!def.getMultiple() && values.length > 1)
|
||||
throw new Error("No more than one value may be assigned to parameter '" + def.getName() + "'.");
|
||||
if(def.getRequired() && (def.getCommands().length === 0 || def.getCommands().includes(this.getCommand())) && values.length === 0)
|
||||
throw new Error("Parameter '" + def.getName() + "' is required.");
|
||||
for (const value of values) {
|
||||
if(!def.getValidator().validate(value))
|
||||
throw new Error("Parameter '" + def.getName() + "' does not meet the requirements: " + def.getValidator().description());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getDefinitions (): ConfigurationDefinition[] {
|
||||
return Array.from(this.definitions.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command from the arguments
|
||||
* @returns The command that was passed in the arguments.
|
||||
*/
|
||||
public getCommand (): string {
|
||||
if(this.arguments.data.commands.length >= 1)
|
||||
return this.arguments.data.commands[0];
|
||||
else
|
||||
return this.defaultCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a parameter
|
||||
* @param {string} name - The name of the parameter.
|
||||
* @returns The value of the parameter.
|
||||
*/
|
||||
public getValue (name: string): string | string[] | boolean | boolean[] | number | number[] | undefined {
|
||||
const def = this.definitions.get(name);
|
||||
const value = this.values.get(name) ?? [];
|
||||
switch ( def?.getType() ) {
|
||||
case DefinitionType.Boolean: {
|
||||
const result = this.toBoolean(value);
|
||||
if(def.getMultiple()) {
|
||||
return result;
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
case DefinitionType.Number: {
|
||||
const result: number[] = this.toInteger(value);
|
||||
if(def.getMultiple()) {
|
||||
return result;
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
case DefinitionType.String: {
|
||||
if(def.getMultiple()) {
|
||||
return value;
|
||||
}
|
||||
return value[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a boolean parameter
|
||||
* @param {string} name - The name of the parameter.
|
||||
* @returns The value of the parameter.
|
||||
*/
|
||||
public getBooleanValue (name: string): boolean | boolean[] | undefined {
|
||||
const def = this.definitions.get(name);
|
||||
if(def?.getType() == DefinitionType.Boolean) {
|
||||
const value = this.values.get(name) ?? [];
|
||||
const result: boolean[] = this.toBoolean(value);
|
||||
if(def.getMultiple()) {
|
||||
return result;
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getNumberValue (name: string): number | number[] | undefined {
|
||||
const def = this.definitions.get(name);
|
||||
if(def?.getType() == DefinitionType.Number) {
|
||||
const value = this.values.get(name) ?? [];
|
||||
const result: number[] = this.toInteger(value);
|
||||
if(def.getMultiple()) {
|
||||
return result;
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public getStringValue (name: string): string | string[] | undefined {
|
||||
const def = this.definitions.get(name);
|
||||
if(def?.getType() == DefinitionType.String) {
|
||||
const value = this.values.get(name) ?? [];
|
||||
if(def.getMultiple()) {
|
||||
return value;
|
||||
}
|
||||
return value[0];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Convert a string array to a boolean array
|
||||
* @param {string[]} value - The value of the parameter.
|
||||
* @returns The `toBoolean` function returns an array of booleans.
|
||||
*/
|
||||
|
||||
private toBoolean (value: string[]): boolean[] {
|
||||
const result: boolean[] = [];
|
||||
for (const v of value) {
|
||||
result.push(v === "true" || v === "1" || v === "y");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes an array of strings and returns an array of numbers.
|
||||
* @param {string[]} value - The value of the parameter.
|
||||
* @returns The `toInteger` function returns an array of numbers.
|
||||
*/
|
||||
private toInteger (value: string[]): number[] {
|
||||
const result: number[] = [];
|
||||
for (const v of value) {
|
||||
const f = parseFloat(v); const i = parseInt(v);
|
||||
result.push((f == i) ? i : f);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private getInternalValue (def: ConfigurationDefinition): string[] | undefined {
|
||||
let value = this.getArgValue(def);
|
||||
value = (value === undefined)?this.getEnvValue(def):value;
|
||||
value = (value === undefined)?def.getDefaultValue():value;
|
||||
if(value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getEnvValue (def: ConfigurationDefinition): string[] | undefined {
|
||||
const value = process.env[this.envPrefix + "_" + def.getEnvName().toUpperCase()];
|
||||
if(value !== undefined)
|
||||
return [value];
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getArgValue (def: ConfigurationDefinition): string[] | undefined{
|
||||
for (const arg of def.getArgName()) {
|
||||
if(arg.length >= 2 && this.arguments.hasLongSwitch(arg)) {
|
||||
return this.arguments.getLongSwitch(arg).values;
|
||||
} else if (this.arguments.hasShortSwitch(arg)) {
|
||||
return this.arguments.getShortSwitch(arg).values;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
let configInstance: Configuration;
|
||||
|
||||
export function initConfiguration (envPrefix: string, ...definitions: ConfigurationDefinition[]): void {
|
||||
configInstance = new Configuration("help", envPrefix, ...definitions);
|
||||
}
|
||||
export default function config (): Configuration {
|
||||
return configInstance;
|
||||
}
|
||||
56
src/framework/configuration/configurationValidatior.ts
Normal file
56
src/framework/configuration/configurationValidatior.ts
Normal file
@ -0,0 +1,56 @@
|
||||
export abstract class DefinitionValidator {
|
||||
public abstract validate (value: string): boolean;
|
||||
public abstract description (): string;
|
||||
}
|
||||
|
||||
const definitionValidators = {
|
||||
validateBoolean (): DefinitionValidator {
|
||||
return new (class implements DefinitionValidator {
|
||||
public description (): string {
|
||||
return "Parameter must be a boolean value.";
|
||||
}
|
||||
|
||||
public validate (value: string): boolean {
|
||||
return (value === "true" || value === "1" || value === "y" || value === "false" || value === "0" || value === "n");
|
||||
}
|
||||
});
|
||||
},
|
||||
validateNumber (): DefinitionValidator {
|
||||
return new (class implements DefinitionValidator {
|
||||
public description (): string {
|
||||
return "Parameter must be a numeric value.";
|
||||
}
|
||||
|
||||
public validate (value: string): boolean {
|
||||
return /^[+-]?([0-9]*[.])?[0-9]+$/.exec(value) !== null;
|
||||
}
|
||||
});
|
||||
},
|
||||
validatePort (): DefinitionValidator {
|
||||
return new (class implements DefinitionValidator {
|
||||
public description (): string {
|
||||
return "Parameter must be valid port (1-65535).";
|
||||
}
|
||||
|
||||
public validate (value: string): boolean {
|
||||
const maxPort = 65535;
|
||||
return (/^[0-9]*$/.exec(value) !== null) && parseInt(value) >= 1 && parseInt(value) <= maxPort;
|
||||
}
|
||||
});
|
||||
},
|
||||
validateHost (): DefinitionValidator {
|
||||
return new (class implements DefinitionValidator {
|
||||
public description (): string {
|
||||
return "Hostname musst be a IP-Adress or a valid Hostname.";
|
||||
}
|
||||
|
||||
public validate (value: string): boolean {
|
||||
const regexIPv6 = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/gi;
|
||||
const regexIPv4 = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
const regexHost = /^(([a-zA-Z0-9]*)|([a-zA-Z0-9]*\.)*([a-zA-Z]+))$/;
|
||||
return (regexHost.exec(value) !== null) || (regexIPv4.exec(value) !== null) || (regexIPv6.exec(value) !== null);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
export default definitionValidators;
|
||||
41
src/framework/ddd/ddd.types.ts
Normal file
41
src/framework/ddd/ddd.types.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export abstract class Entity<I extends Identifier<unknown>> {
|
||||
public abstract get id (): I | undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export abstract class ValueObject {}
|
||||
|
||||
export abstract class Identifier<Type> {
|
||||
public abstract get value (): Type;
|
||||
|
||||
}
|
||||
|
||||
export abstract class IdentifierGenerator<I extends Identifier<unknown>> {
|
||||
|
||||
public abstract generate (): I;
|
||||
|
||||
}
|
||||
|
||||
export abstract class Aggregate<I extends Identifier<unknown>> extends Entity<I> {}
|
||||
|
||||
export interface ReadRepository<I extends Identifier<unknown>, A extends Aggregate<I>> {
|
||||
|
||||
getAggregate(identitiy: I): A;
|
||||
getAggregates(): Array<A>;
|
||||
|
||||
}
|
||||
|
||||
export interface WriteRepository<I extends Identifier<unknown>, A extends Aggregate<I>> {
|
||||
|
||||
storeAggregate(aggregate: A): A;
|
||||
|
||||
}
|
||||
|
||||
export interface DeleteRepository<I extends Identifier<unknown>, A extends Aggregate<I>> {
|
||||
|
||||
deleteAggregate(aggregate: A): void;
|
||||
deleteAggregateById(identitiy: I): void;
|
||||
|
||||
}
|
||||
|
||||
export interface Repository<I extends Identifier<unknown>, A extends Aggregate<I>> extends ReadRepository<I, A>, WriteRepository<I, A>, DeleteRepository<I, A>{}
|
||||
10
src/framework/framework.module.ts
Normal file
10
src/framework/framework.module.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { BasicStrategy } from "./auth/auth-basic.strategy";
|
||||
import { PassportModule } from "@nestjs/passport";
|
||||
|
||||
@Module({
|
||||
imports: [PassportModule],
|
||||
providers: [BasicStrategy],
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
export class FrameworkModule {}
|
||||
@ -70,22 +70,34 @@ export const plugin: FastifyPluginAsync<FastifyRequestLoggerOptions> = async (fa
|
||||
return false;
|
||||
};
|
||||
|
||||
const ifExists = (prefix: string, value: string): string => {
|
||||
if(value.length >= 1) {
|
||||
if(prefix.length >= 1) {
|
||||
return prefix + " " + value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
fastify.addHook("onRequest", async (request) => {
|
||||
if (isIgnoredRequest(request)) {
|
||||
return;
|
||||
}
|
||||
const id = request.id;
|
||||
const method = request.method;
|
||||
const referrer = (request.headers["Referer"] ?? (request.headers["referer"]) ?? request.headers["Refferer"]) ?? request.headers["refferer"];
|
||||
const referrer = (request.headers["Referer"] ?? (request.headers["referer"]) ?? request.headers["Refferer"]) ?? request.headers["refferer"] ?? "none";
|
||||
const remoteAddr = request.ip;
|
||||
const remoteUser = "???";
|
||||
const url = request.url;
|
||||
const httpVersion = request.raw.httpVersion;
|
||||
const userAgent = request.headers["User-Agents"];
|
||||
const contentLength = request.headers["content-length"];
|
||||
const contentLength = request.headers["content-length"] ?? "";
|
||||
|
||||
request.log.info(`${chalk.bold.yellow("→")} ${chalk.yellow(method)} ${chalk.green(url)} HTTP/${httpVersion} - ${chalk.blue(remoteAddr)} ${remoteUser} - ${referrer} - ${contentLength} bytes [${id}]`);
|
||||
request.log.trace(`${chalk.bold.yellow("→")} ${chalk.yellow(method)} ${chalk.green(url)} ${userAgent} [${id}]`);
|
||||
console.log(JSON.stringify(request.headers));
|
||||
|
||||
request.log.info(`${chalk.bold.yellow("→")} ${chalk.yellow(method)} ${chalk.green(url)} HTTP/${httpVersion} - ${chalk.blue(remoteAddr)} ${ifExists("", remoteUser)} - ${referrer} ${ifExists("-", contentLength+" bytes")} [${chalk.greenBright(id)}]`);
|
||||
request.log.trace(`${chalk.bold.yellow("→")} ${chalk.yellow(method)} ${chalk.green(url)} ${userAgent} [${chalk.greenBright(id)}]`);
|
||||
request.log.info(
|
||||
`${chalk.bold.yellow("← ")}${chalk.yellow(request.method)}:${chalk.green(
|
||||
request.url
|
||||
|
||||
@ -5,7 +5,7 @@ const { combine, timestamp, label, printf, errors, splat } = format;
|
||||
|
||||
// Custom logging format
|
||||
const customFormat = printf(({ level, message, label, timestamp, stack }) => {
|
||||
return `${level}\t ${timestamp} · ${label || "-"}: ${message} ${stack || ""}`;
|
||||
return `${level}\t ${timestamp} · ${label || "-"} \t· ${message} ${stack || ""}`;
|
||||
});
|
||||
|
||||
const myCustomLevels = {
|
||||
|
||||
28
src/framework/logger/nestLogger.ts
Normal file
28
src/framework/logger/nestLogger.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { LoggerService } from "@nestjs/common";
|
||||
import getlogger from "./logger";
|
||||
|
||||
const logger = getlogger("nest");
|
||||
export default class NestLogger implements LoggerService {
|
||||
|
||||
public log (message: any, ...optionalParams: any[]): void {
|
||||
logger.info(message, optionalParams);
|
||||
}
|
||||
|
||||
public error (message: any, ...optionalParams: any[]): void {
|
||||
logger.error(message, optionalParams);
|
||||
}
|
||||
|
||||
public warn (message: any, ...optionalParams: any[]): void {
|
||||
logger.warning(message, optionalParams);
|
||||
}
|
||||
|
||||
public debug (message: any, ...optionalParams: any[]): void {
|
||||
logger.debug(message, optionalParams);
|
||||
}
|
||||
|
||||
public verbose (message: any, ...optionalParams: any[]): void {
|
||||
logger.verbose(message, optionalParams);
|
||||
}
|
||||
}
|
||||
7
src/framework/mapper/mapper.ts
Normal file
7
src/framework/mapper/mapper.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { CamelCaseNamingConvention, createMapper } from "@automapper/core";
|
||||
import { classes } from "@automapper/classes";
|
||||
|
||||
export const mapper = createMapper({
|
||||
strategyInitializer: classes(),
|
||||
namingConventions: new CamelCaseNamingConvention()
|
||||
});
|
||||
67
src/framework/server/server.ts
Normal file
67
src/framework/server/server.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import fastifySensible from "fastify-sensible";
|
||||
import GracefulServer from "@gquittet/graceful-server";
|
||||
import getlogger from "#logger";
|
||||
import {fastifyLogger, fastifyRequestLogger} from "#logger/fastifyLogger";
|
||||
import { Logger } from "winston";
|
||||
import IGracefulServer from "@gquittet/graceful-server/lib/types/interface/gracefulServer";
|
||||
import configuration from "#configuration";
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify";
|
||||
import { AppModule } from "../../app.module";
|
||||
import NestLogger from "#framework/logger/nestLogger";
|
||||
|
||||
export default class Server {
|
||||
private readonly logger: Logger;
|
||||
private gracefulServer: IGracefulServer | undefined;
|
||||
private nest: NestFastifyApplication | undefined;
|
||||
public constructor () {
|
||||
this.logger = getlogger("fastify");
|
||||
}
|
||||
|
||||
public async start (): Promise<void> {
|
||||
const start = async (): Promise<void> => {
|
||||
try {
|
||||
const port = <number>configuration().getNumberValue("port");
|
||||
const host = <string>configuration().getStringValue("host");
|
||||
this.logger.info("Start server on port " + host + ":" + port);
|
||||
await this.nest?.listen(port, host);
|
||||
this.gracefulServer?.setReady();
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
await start();
|
||||
}
|
||||
|
||||
public async setup (): Promise<void> {
|
||||
this.nest = await NestFactory.create<NestFastifyApplication>(
|
||||
AppModule,
|
||||
new FastifyAdapter({
|
||||
logger: fastifyLogger,
|
||||
disableRequestLogging: true
|
||||
}), {
|
||||
logger: new NestLogger(),
|
||||
}
|
||||
);
|
||||
|
||||
await this.nest.register(fastifySensible);
|
||||
await this.nest.register(fastifyRequestLogger);
|
||||
|
||||
this.gracefulServer = GracefulServer(this.nest.getHttpServer());
|
||||
|
||||
this.gracefulServer.on(GracefulServer.READY, () => {
|
||||
this.logger.info("Server is ready");
|
||||
});
|
||||
|
||||
this.gracefulServer.on(GracefulServer.SHUTTING_DOWN, () => {
|
||||
this.logger.info("Server is shutting down");
|
||||
});
|
||||
|
||||
this.gracefulServer.on(GracefulServer.SHUTDOWN, () => {
|
||||
this.logger.info("Server is down");
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
import Fastify from "fastify";
|
||||
import fastifySensible from "fastify-sensible";
|
||||
import GracefulServer from "@gquittet/graceful-server";
|
||||
import getlogger from "@logger";
|
||||
import {fastifyLogger, fastifyRequestLogger} from "@logger/fastifyLogger";
|
||||
|
||||
const fastify = Fastify({
|
||||
logger: fastifyLogger,
|
||||
disableRequestLogging: true
|
||||
});
|
||||
await fastify.register(fastifyRequestLogger, {logBody: true});
|
||||
await fastify.register(fastifySensible);
|
||||
|
||||
const gracefulServer = GracefulServer(fastify.server);
|
||||
|
||||
gracefulServer.on(GracefulServer.READY, () => {
|
||||
getlogger("server").info("Server is ready");
|
||||
});
|
||||
|
||||
gracefulServer.on(GracefulServer.SHUTTING_DOWN, () => {
|
||||
getlogger("server").info("Server is shutting down");
|
||||
});
|
||||
|
||||
gracefulServer.on(GracefulServer.SHUTDOWN, (error: {message: string}) => {
|
||||
getlogger("server").info("Server is down because of", error.message);
|
||||
process.exit();
|
||||
});
|
||||
|
||||
// Declare a route
|
||||
fastify.get("/", function (request, reply) {
|
||||
void reply.send({ hello: "world" });
|
||||
});
|
||||
|
||||
// Run the server!
|
||||
const start = async (): Promise<void> => {
|
||||
try {
|
||||
const port = 3000;
|
||||
await fastify.listen(port);
|
||||
gracefulServer.setReady();
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
await start();
|
||||
@ -8,11 +8,28 @@
|
||||
"esModuleInterop": true,
|
||||
"outDir": "./dist/",
|
||||
"baseUrl": "./src/",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"paths": {
|
||||
"@abc/*": ["abc/*"],
|
||||
"@logger": ["framework/logger/logger"],
|
||||
"@logger/*": ["framework/logger/*"]
|
||||
"#ddd": ["framework/ddd/ddd.types"],
|
||||
"#framework/*": ["framework/*"],
|
||||
"#logger": ["framework/logger/logger"],
|
||||
"#logger/*": ["framework/logger/*"],
|
||||
"#configuration": ["framework/configuration/configuration"],
|
||||
"#configuration/*": ["framework/configuration/*"],
|
||||
"#app": ["framework/app/app"],
|
||||
"#domain/*": ["application/domain/*"]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"transform": "@automapper/classes/transformer-plugin",
|
||||
"modelFileNameSuffix": [".types.js", ".entity.ts", ".dto.ts"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"tsc-alias": {
|
||||
"verbose": true,
|
||||
"resolveFullPaths": true
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
Reference in New Issue
Block a user