Add API monorepo
Change-Id: I39aa1707744bb86c4bc9113157bbf815bb3fe33a
This commit is contained in:
parent
b87cff043a
commit
5246efb027
56 changed files with 251 additions and 22 deletions
|
@ -6,7 +6,7 @@ Seedling is a project generator for @prokyon libraries.
|
||||||
|
|
||||||
1. Create file `.npmrc`
|
1. Create file `.npmrc`
|
||||||
2. Paste content to file `@toolkit:registry=https://npm.romanjaros.dev`
|
2. Paste content to file `@toolkit:registry=https://npm.romanjaros.dev`
|
||||||
3. Use `pnpm dlx @toolkit/seedling --name hello-world --port 90 --monorepo T ./app`
|
3. Use `pnpm dlx @toolkit/seedling --name hello-world --port 90 ./app`
|
||||||
- instead of `./app` use can use curent folder, `.` or `./`
|
- instead of `./app` use can use curent folder, `.` or `./`
|
||||||
4. Use `pnpm dev` to start
|
4. Use `pnpm dev` to start
|
||||||
|
|
||||||
|
@ -20,10 +20,6 @@ Seedling is a project generator for @prokyon libraries.
|
||||||
- define port for application when is deployed
|
- define port for application when is deployed
|
||||||
- number
|
- number
|
||||||
|
|
||||||
### monorepo
|
|
||||||
- define if generated structure will be for monorepo
|
|
||||||
- value `T` = `true` or `F` = `false`
|
|
||||||
|
|
||||||
### context
|
### context
|
||||||
- last parameter is folder as destination of generated code
|
- last parameter is folder as destination of generated code
|
||||||
- string
|
- string
|
25
create.js
25
create.js
|
@ -6,7 +6,6 @@ const argv = require('minimist')(process.argv.slice(2));
|
||||||
// questions
|
// questions
|
||||||
const appName = argv.name ?? 'app';
|
const appName = argv.name ?? 'app';
|
||||||
const appPort = argv.port ?? '0';
|
const appPort = argv.port ?? '0';
|
||||||
const isMonorepo = argv.monorepo === 'T';
|
|
||||||
|
|
||||||
const defaultContextDir = './';
|
const defaultContextDir = './';
|
||||||
const rootDir = __dirname;
|
const rootDir = __dirname;
|
||||||
|
@ -14,18 +13,19 @@ const contextDir = `${argv._[0]}/` ?? defaultContextDir;
|
||||||
|
|
||||||
// create app folder
|
// create app folder
|
||||||
const appsDir = `${contextDir}/apps`;
|
const appsDir = `${contextDir}/apps`;
|
||||||
const appDir = isMonorepo ? `${contextDir}/apps/${appName}-fe` : `${contextDir}/`;
|
const uiDir = `${contextDir}/apps/${appName}-ui`;
|
||||||
|
const apiDir = `${contextDir}/apps/${appName}-api`;
|
||||||
|
|
||||||
// prepare structure folders
|
// prepare structure folders
|
||||||
if (!fs.existsSync(appDir)) {
|
if (!fs.existsSync(uiDir)) {
|
||||||
if (!fs.existsSync(contextDir)) {
|
if (!fs.existsSync(contextDir)) {
|
||||||
fs.mkdirSync(contextDir);
|
fs.mkdirSync(contextDir);
|
||||||
}
|
}
|
||||||
if (isMonorepo) {
|
fs.mkdirSync(appsDir);
|
||||||
fs.mkdirSync(appsDir);
|
fs.mkdirSync(uiDir);
|
||||||
}
|
fs.mkdirSync(`${uiDir}/src`);
|
||||||
fs.mkdirSync(appDir);
|
fs.mkdirSync(apiDir);
|
||||||
fs.mkdirSync(`${appDir}/src`);
|
fs.mkdirSync(`${apiDir}/src`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy folder content
|
// copy folder content
|
||||||
|
@ -34,7 +34,11 @@ try {
|
||||||
force: true,
|
force: true,
|
||||||
recursive: true,
|
recursive: true,
|
||||||
});
|
});
|
||||||
fs.cpSync(`${rootDir}/source/app/`, appDir, {
|
fs.cpSync(`${rootDir}/source/ui/`, uiDir, {
|
||||||
|
force: true,
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
fs.cpSync(`${rootDir}/source/api/`, apiDir, {
|
||||||
force: true,
|
force: true,
|
||||||
recursive: true,
|
recursive: true,
|
||||||
});
|
});
|
||||||
|
@ -50,7 +54,8 @@ fs.renameSync(`${contextDir}/npmrc`, `${contextDir}/.npmrc`);
|
||||||
fs.renameSync(`${contextDir}/gitignore`, `${contextDir}/.gitignore`);
|
fs.renameSync(`${contextDir}/gitignore`, `${contextDir}/.gitignore`);
|
||||||
fs.renameSync(`${contextDir}/prettierrc`, `${contextDir}/.prettierrc`);
|
fs.renameSync(`${contextDir}/prettierrc`, `${contextDir}/.prettierrc`);
|
||||||
fs.renameSync(`${contextDir}/.tsconfig.json`, `${contextDir}/tsconfig.json`);
|
fs.renameSync(`${contextDir}/.tsconfig.json`, `${contextDir}/tsconfig.json`);
|
||||||
fs.renameSync(`${appDir}/.tsconfig.json`, `${appDir}/tsconfig.json`);
|
fs.renameSync(`${uiDir}/.tsconfig.json`, `${uiDir}/tsconfig.json`);
|
||||||
|
fs.renameSync(`${apiDir}/.tsconfig.json`, `${apiDir}/tsconfig.json`);
|
||||||
|
|
||||||
// replace in files
|
// replace in files
|
||||||
replaceInFiles.sync({
|
replaceInFiles.sync({
|
||||||
|
|
42
source/api/.tsconfig.json
Normal file
42
source/api/.tsconfig.json
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strict": false,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "./",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"paths": {
|
||||||
|
"config/*": [
|
||||||
|
"./src/config/*"
|
||||||
|
],
|
||||||
|
"database/*": [
|
||||||
|
"./src/database/*"
|
||||||
|
],
|
||||||
|
"model/*": [
|
||||||
|
"./src/model/*"
|
||||||
|
],
|
||||||
|
"controllers/*": [
|
||||||
|
"./src/controllers/*"
|
||||||
|
],
|
||||||
|
"utils/*": [
|
||||||
|
"./src/utils/*"
|
||||||
|
],
|
||||||
|
"types/*": [
|
||||||
|
"./src/types/*"
|
||||||
|
],
|
||||||
|
"plugins/*": [
|
||||||
|
"./src/plugins/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./**/*.ts",
|
||||||
|
"./**/*.tsx",
|
||||||
|
"./src/**/*.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
5
source/api/nodemon.json
Normal file
5
source/api/nodemon.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"ignore": [
|
||||||
|
"README"
|
||||||
|
]
|
||||||
|
}
|
38
source/api/package.json
Normal file
38
source/api/package.json
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"name": "$(appName)-api",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "",
|
||||||
|
"author": "Roman Jaroš <hello@romanjaros.dev>",
|
||||||
|
"license": "ISC",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon --exec ts-node -r tsconfig-paths/register src/index.ts",
|
||||||
|
"build": "rimraf dist && tsc -p tsconfig.json && tsc-alias -p tsconfig.json"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/hapi": "18.0.10",
|
||||||
|
"@types/hapi__nes": "11.0.7",
|
||||||
|
"@types/ramda": "0.28.0",
|
||||||
|
"@types/validator": "13.11.1",
|
||||||
|
"copyfiles": "2.4.1",
|
||||||
|
"nodemon": "3.0.1",
|
||||||
|
"rimraf": "5.0.1",
|
||||||
|
"ts-node": "10.9.1",
|
||||||
|
"tsc-alias": "1.8.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hapi/boom": "10.0.1",
|
||||||
|
"@hapi/nes": "13.0.1",
|
||||||
|
"@hapi/hapi": "21.3.2",
|
||||||
|
"@hapi/inert": "7.1.0",
|
||||||
|
"@hapi/vision": "7.0.3",
|
||||||
|
"@prokyon/utils": "1.0.31",
|
||||||
|
"hapi-swagger": "17.1.0",
|
||||||
|
"joi": "17.10.1",
|
||||||
|
"typeorm": "0.3.17",
|
||||||
|
"sqlite3": "5.1.6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"ramda": "0.28.0",
|
||||||
|
"typescript": "5.2.2"
|
||||||
|
}
|
||||||
|
}
|
4
source/api/src/config/server.json
Normal file
4
source/api/src/config/server.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 3000
|
||||||
|
}
|
18
source/api/src/controllers/hey.ts
Normal file
18
source/api/src/controllers/hey.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { Request, ResponseToolkit, ServerRoute } from '@hapi/hapi';
|
||||||
|
|
||||||
|
const routers: ServerRoute[] = [
|
||||||
|
{
|
||||||
|
path: '/hey',
|
||||||
|
method: 'GET',
|
||||||
|
options: {
|
||||||
|
description: 'Are u alive?',
|
||||||
|
tags: ['api'],
|
||||||
|
},
|
||||||
|
handler: (request: Request, h: ResponseToolkit) => {
|
||||||
|
return h.response('I am here !').type('html/text').code(200);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routers;
|
13
source/api/src/database/db.ts
Normal file
13
source/api/src/database/db.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
export const dbConnect = async () => {
|
||||||
|
const AppDataSource = new DataSource({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: `database.sqlite`,
|
||||||
|
entities: [],
|
||||||
|
synchronize: true,
|
||||||
|
logging: ['query'],
|
||||||
|
});
|
||||||
|
return await AppDataSource.initialize();
|
||||||
|
};
|
51
source/api/src/index.ts
Normal file
51
source/api/src/index.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import hapi from '@hapi/hapi';
|
||||||
|
import nes from '@hapi/nes';
|
||||||
|
|
||||||
|
import { host, port } from 'config/server.json';
|
||||||
|
import { dbConnect } from 'database/db';
|
||||||
|
|
||||||
|
import swagger from 'plugins/swagger';
|
||||||
|
|
||||||
|
const server: hapi.Server = new hapi.Server({
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
debug: {
|
||||||
|
log: ['error'],
|
||||||
|
request: ['error'],
|
||||||
|
},
|
||||||
|
routes: {
|
||||||
|
validate: {
|
||||||
|
failAction: (request, h, err) => {
|
||||||
|
throw err;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const start = async (): Promise<void> => {
|
||||||
|
await swagger(server);
|
||||||
|
await server.register(nes);
|
||||||
|
|
||||||
|
// v1 endpoints
|
||||||
|
await server.register(require('./plugins/controllers'), { routes: { prefix: '/api/v1' } });
|
||||||
|
|
||||||
|
// connect to database
|
||||||
|
await dbConnect()
|
||||||
|
.then(() => {
|
||||||
|
console.log('Data Source has been initialized!');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Error during Data Source initialization', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.start();
|
||||||
|
console.info(`Server running: ${server.info.uri}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on('unhandledRejection', (err) => {
|
||||||
|
console.info(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
start();
|
11
source/api/src/plugins/controllers.ts
Normal file
11
source/api/src/plugins/controllers.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { Server } from '@hapi/hapi';
|
||||||
|
|
||||||
|
import hey from 'controllers/hey';
|
||||||
|
|
||||||
|
export const plugin = {
|
||||||
|
name: 'Controllers',
|
||||||
|
register: async (server: Server) => {
|
||||||
|
server.route(hey);
|
||||||
|
},
|
||||||
|
};
|
26
source/api/src/plugins/swagger.ts
Normal file
26
source/api/src/plugins/swagger.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { Server } from '@hapi/hapi';
|
||||||
|
|
||||||
|
const version = require('../../package.json').version;
|
||||||
|
|
||||||
|
export default async (server: Server) => {
|
||||||
|
return server.register([
|
||||||
|
require('@hapi/inert'),
|
||||||
|
require('@hapi/vision'),
|
||||||
|
{
|
||||||
|
plugin: require('hapi-swagger'),
|
||||||
|
options: {
|
||||||
|
info: {
|
||||||
|
title: 'Many API',
|
||||||
|
version,
|
||||||
|
},
|
||||||
|
swaggerUI: true,
|
||||||
|
documentationPage: true,
|
||||||
|
documentationPath: '/swagger',
|
||||||
|
pathPrefixSize: 3,
|
||||||
|
basePath: '/api/v1',
|
||||||
|
reuseDefinitions: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
23
source/api/src/utils/params.ts
Normal file
23
source/api/src/utils/params.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { Request } from '@hapi/hapi';
|
||||||
|
|
||||||
|
export type RequestParams = Record<string, string | null>;
|
||||||
|
|
||||||
|
export const getParam =
|
||||||
|
(request: Request) =>
|
||||||
|
(name: string): string | null => {
|
||||||
|
const params: RequestParams = {
|
||||||
|
...(request.params as object),
|
||||||
|
...(request.payload as object),
|
||||||
|
...(request.query as object),
|
||||||
|
};
|
||||||
|
const filteredParams: RequestParams = {} as any;
|
||||||
|
Object.keys(params).map((key) => {
|
||||||
|
const value = params[key];
|
||||||
|
if (['undefined', 'null'].includes(value as string)) {
|
||||||
|
filteredParams[key] = null;
|
||||||
|
}
|
||||||
|
filteredParams[key] = value;
|
||||||
|
});
|
||||||
|
return filteredParams[name] ?? null;
|
||||||
|
};
|
|
@ -16,10 +16,6 @@
|
||||||
"es6",
|
"es6",
|
||||||
"dom"
|
"dom"
|
||||||
],
|
],
|
||||||
"jsx": "react",
|
|
||||||
"types": [
|
|
||||||
"jest",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./node_modules",
|
"./node_modules",
|
||||||
|
|
|
@ -36,9 +36,10 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"types": [
|
"types": [
|
||||||
"./src/types/yup",
|
"node",
|
||||||
"node"
|
"jest",
|
||||||
],
|
"./src/types/yup"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src/**/*.ts",
|
"./src/**/*.ts",
|
0
source/ui/src/types/global.ts
Normal file
0
source/ui/src/types/global.ts
Normal file
Loading…
Add table
Reference in a new issue