This commit is contained in:
Vincent Alamelle 2023-03-16 18:03:58 +01:00
parent 9a884f1154
commit efc7ad1efb
16 changed files with 151 additions and 101 deletions

View File

@ -1,39 +1,47 @@
# Install dependencies only when needed
FROM node:19-alpine AS deps
WORKDIR tezosLink
WORKDIR leCoffre
RUN npm install -D prisma@4.11.0
COPY package.json ./
COPY src/common/databases/schema.prisma ./src/common/databases/schema.prisma
RUN npx prisma generate
RUN npm install --frozen-lockfile
# Rebuild the source code only when needed
FROM node:19-alpine AS builder
WORKDIR tezosLink
WORKDIR leCoffre
COPY . .
COPY --from=deps tezosLink/node_modules ./node_modules
RUN apk update && apk add openssh-client git
COPY id_ed25519 /root/.ssh/id_ed25519
RUN chmod 600 ~/.ssh/id_ed25519
RUN eval "$(ssh-agent -s)" && ssh-add /root/.ssh/id_ed25519
RUN ssh-keyscan github.com smart-chain-fr/leCoffre-resources.git >> /root/.ssh/known_hosts
COPY node_modules ./node_modules
RUN npx prisma generate
RUN npm run build
# Production image, copy all the files and run next
FROM node:19-alpine AS production
WORKDIR tezosLink
WORKDIR leCoffre
RUN adduser -D tezoslinkuser --uid 10000 && chown -R tezoslinkuser .
RUN adduser -D lecoffreuser --uid 10000 && chown -R lecoffreuser .
COPY --from=builder --chown=tezoslinkuser tezosLink/node_modules ./node_modules
COPY --from=builder --chown=tezoslinkuser tezosLink/dist/api ./dist/api
COPY --from=builder --chown=tezoslinkuser tezosLink/dist/entries ./dist/entries
COPY --from=builder --chown=tezoslinkuser tezosLink/dist/common ./dist/common
#COPY --from=builder --chown=tezoslinkuser tezosLink/src/common/databases/ ./src/common/databases/
COPY --from=builder --chown=tezoslinkuser tezosLink/package.json ./package.json
COPY --from=builder --chown=lecoffreuser leCoffre/node_modules ./node_modules
COPY --from=builder --chown=lecoffreuser leCoffre/dist/api ./dist/api
COPY --from=builder --chown=lecoffreuser leCoffre/dist/entries ./dist/entries
COPY --from=builder --chown=lecoffreuser leCoffre/dist/common ./dist/common
COPY --from=builder --chown=lecoffreuser leCoffre/src/common/databases/ ./src/common/databases/
COPY --from=builder --chown=lecoffreuser leCoffre/package.json ./package.json
USER tezoslinkuser
USER lecoffreuser
CMD ["npm", "run", "api:start"]
EXPOSE 3001

View File

@ -1,10 +0,0 @@
# Install dependencies only when needed
FROM node:19-alpine AS deps
WORKDIR tezosLink
COPY . .
RUN npm i
RUN npm run build
CMD ["npm", "run", "web:start"]
EXPOSE 3000

View File

@ -5,7 +5,7 @@ volumes:
services:
postgres:
image: postgres:11
image: lecoffrelocal
restart: always
environment:
- POSTGRES_USER

8
id_ed25519 Executable file
View File

@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDyIUEbyS+lqxsmtJ/nKJ0M11tZ0fA1/MxpoU5//r5kHwAAAKh4md+XeJnf
lwAAAAtzc2gtZWQyNTUxOQAAACDyIUEbyS+lqxsmtJ/nKJ0M11tZ0fA1/MxpoU5//r5kHw
AAAEAlhxwZbtCgVmJ1olIGIaHdD9QBtGHJndiB2LsaNfJefPIhQRvJL6WrGya0n+conQzX
W1nR8DX8zGmhTn/+vmQfAAAAH3ZpbmNlbnQuYWxhbWVsbGVAc21hcnQtY2hhaW4uZnIBAg
MEBQY=
-----END OPENSSH PRIVATE KEY-----

19
package-lock.json generated
View File

@ -17,10 +17,10 @@
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"le-coffre-ressources": "github.com:smart-chain-fr/leCoffre-resources.git",
"module-alias": "^2.2.2",
"next": "^13.1.5",
"node-schedule": "^2.1.1",
"prisma": "^4.9.0",
"prisma-query": "^2.0.0",
"reflect-metadata": "^0.1.13",
"ts-node": "^10.9.1",
@ -36,9 +36,11 @@
"@types/node-schedule": "^2.1.0",
"@types/uuid": "^9.0.0",
"nodemon": "^2.0.20",
"prettier": "2.8.4"
"prettier": "2.8.4",
"prisma": "^4.11.0"
}
},
"github.com:smart-chain-fr/leCoffre-resources.git": {},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@ -296,6 +298,7 @@
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.11.0.tgz",
"integrity": "sha512-0AEBi2HXGV02cf6ASsBPhfsVIbVSDC9nbQed4iiY5eHttW9ZtMxHThuKZE1pnESbr8HRdgmFSa/Kn4OSNYuibg==",
"devOptional": true,
"hasInstallScript": true
},
"node_modules/@prisma/engines-version": {
@ -1095,6 +1098,10 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"peer": true
},
"node_modules/le-coffre-ressources": {
"resolved": "github.com:smart-chain-fr/leCoffre-resources.git",
"link": true
},
"node_modules/libphonenumber-js": {
"version": "1.10.24",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz",
@ -1456,6 +1463,7 @@
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.11.0.tgz",
"integrity": "sha512-4zZmBXssPUEiX+GeL0MUq/Yyie4ltiKmGu7jCJFnYMamNrrulTBc+D+QwAQSJ01tyzeGHlD13kOnqPwRipnlNw==",
"devOptional": true,
"hasInstallScript": true,
"dependencies": {
"@prisma/engines": "4.11.0"
@ -2042,7 +2050,8 @@
"@prisma/engines": {
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.11.0.tgz",
"integrity": "sha512-0AEBi2HXGV02cf6ASsBPhfsVIbVSDC9nbQed4iiY5eHttW9ZtMxHThuKZE1pnESbr8HRdgmFSa/Kn4OSNYuibg=="
"integrity": "sha512-0AEBi2HXGV02cf6ASsBPhfsVIbVSDC9nbQed4iiY5eHttW9ZtMxHThuKZE1pnESbr8HRdgmFSa/Kn4OSNYuibg==",
"devOptional": true
},
"@prisma/engines-version": {
"version": "4.11.0-57.8fde8fef4033376662cad983758335009d522acb",
@ -2671,6 +2680,9 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"peer": true
},
"le-coffre-ressources": {
"version": "file:github.com:smart-chain-fr/leCoffre-resources.git"
},
"libphonenumber-js": {
"version": "1.10.24",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.24.tgz",
@ -2906,6 +2918,7 @@
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.11.0.tgz",
"integrity": "sha512-4zZmBXssPUEiX+GeL0MUq/Yyie4ltiKmGu7jCJFnYMamNrrulTBc+D+QwAQSJ01tyzeGHlD13kOnqPwRipnlNw==",
"devOptional": true,
"requires": {
"@prisma/engines": "4.11.0"
}

View File

@ -3,7 +3,8 @@
"version": "1.0.0",
"description": "tezosLink project",
"_moduleAliases": {
"@Api": "./dist/api",
"@Site": "./dist/site",
"@Api": "./dist/site/api",
"@Pages": "./dist/pages",
"@Common": "./dist/common",
"@Services": "./dist/common/services",
@ -16,7 +17,7 @@
},
"scripts": {
"build": "tsc",
"api:start": "node ./dist/entries/Api.js",
"api:start": "node ./dist/entries/Site.js",
"dev": "nodemon -V",
"api:dev": "nodemon -V --exec 'tsc && npm run api:start'",
"build:test": "tsc && mocha ./dist/entries/Test.js",
@ -42,11 +43,10 @@
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"le-coffre-ressources": "github:smart-chain-fr/leCoffre-resources#v0.2",
"le-coffre-ressources": "github.com:smart-chain-fr/leCoffre-resources.git",
"module-alias": "^2.2.2",
"next": "^13.1.5",
"node-schedule": "^2.1.1",
"prisma": "^4.9.0",
"prisma-query": "^2.0.0",
"reflect-metadata": "^0.1.13",
"ts-node": "^10.9.1",
@ -62,7 +62,8 @@
"@types/node-schedule": "^2.1.0",
"@types/uuid": "^9.0.0",
"nodemon": "^2.0.20",
"prettier": "2.8.4"
"prettier": "2.8.4",
"prisma": "^4.11.0"
},
"prisma": {
"schema": "src/common/databases/schema.prisma"

View File

@ -1,8 +0,0 @@
import { Container } from "typedi";
import ProjectController from "./ProjectController";
export default {
start: () => {
Container.get(ProjectController);
}
}

17
src/app/HomeController.ts Normal file
View File

@ -0,0 +1,17 @@
import { type Response, type Request } from "express";
import { Controller, Get } from "@ControllerPattern/index";
import { Service } from "typedi";
import ApiController from "@Common/system/controller-pattern/ApiController";
@Controller()
@Service()
export default class HomeController extends ApiController {
@Get("/")
protected async get(req: Request, res: Response) {
// const query = processFindManyQuery(req.query);
this.httpSuccess(res, "Welcome to the home page!");
}
}

View File

@ -1,10 +1,8 @@
import { type Response, type Request } from "express";
import { Controller, Get, Post } from "@ControllerPattern/index";
import { Controller, Get } from "@ControllerPattern/index";
import { Service } from "typedi";
import { ProjectEntity } from "@Common/ressources";
import { IsNotEmpty, IsString, IsUUID, validateOrReject } from "class-validator";
import ProjectService from "@Services/_TemplateService/_TemplateService";
import ObjectHydrate from "@Common/helpers/ObjectHydrate";
import ApiController from "@Common/system/controller-pattern/ApiController";
class Params {
@ -21,13 +19,14 @@ export default class ProjectController extends ApiController {
super();
}
@Get("/projects")
@Get("/api/projects")
protected async get(req: Request, res: Response) {
// const query = processFindManyQuery(req.query);
// this.httpSuccess(res, await this.projectService.getByCrite@rias(query));
this.httpSuccess(res, { message: "get" });
}
@Get("/projects/:uuid")
@Get("/api/projects/:uuid")
protected async getByUUID(req: Request, res: Response) {
const { uuid } = req.params as Partial<Params>;
const params = new Params();
@ -39,25 +38,12 @@ export default class ProjectController extends ApiController {
return;
}
const project = await this.projectService.getByUUID(params);
const project = await this.projectService.getByUUID();
// if (!project) {
// this.httpNotFoundRequest(res);
// return;
// }
this.httpSuccess(res, project);
}
@Post("/projects")
protected async post(req: Request, res: Response) {
const projectEntity = new ProjectEntity();
ObjectHydrate.hydrate(projectEntity, req.body);
try {
await validateOrReject(projectEntity, { whitelist: true, forbidNonWhitelisted: true, groups: ["create"] });
} catch (error) {
this.httpBadRequest(res, error);
return;
}
this.httpCreated(res, await this.projectService.create(projectEntity));
}
}

View File

@ -0,0 +1,49 @@
import { type Response, type Request } from "express";
import { Controller, Get } from "@ControllerPattern/index";
import { Service } from "typedi";
import { IsNotEmpty, IsString, IsUUID, validateOrReject } from "class-validator";
import ProjectService from "@Services/_TemplateService/_TemplateService";
import ApiController from "@Common/system/controller-pattern/ApiController";
class Params {
@IsString()
@IsNotEmpty()
@IsUUID()
public uuid!: string;
}
@Controller()
@Service()
export default class ProjectController extends ApiController {
constructor(private projectService: ProjectService) {
super();
}
@Get("/api/projects")
protected async get(req: Request, res: Response) {
// const query = processFindManyQuery(req.query);
this.httpSuccess(res, { message: "get" });
}
@Get("/api/projects/:uuid")
protected async getByUUID(req: Request, res: Response) {
const { uuid } = req.params as Partial<Params>;
const params = new Params();
params.uuid = uuid!;
try {
await validateOrReject(params, { forbidUnknownValues: true });
} catch (error) {
this.httpBadRequest(res, error);
return;
}
const project = await this.projectService.getByUUID();
// if (!project) {
// this.httpNotFoundRequest(res);
// return;
// }
this.httpSuccess(res, project);
}
}

11
src/app/index.ts Normal file
View File

@ -0,0 +1,11 @@
import { Container } from "typedi";
import HomeController from "./HomeController";
import ProjectController from "./api/ProjectController";
export default {
start: () => {
Container.get(HomeController);
Container.get(ProjectController);
}
}

View File

@ -19,9 +19,6 @@ export class BackendVariables {
@IsNotEmpty()
public readonly DATABASE_NAME!: string;
@IsNotEmpty()
public readonly API_HOSTNAME!: string;
@IsOptional()
public readonly API_LABEL!: string;
@ -32,28 +29,13 @@ export class BackendVariables {
public readonly API_ROOT_URL!: string;
@IsOptional()
public readonly RPC_GATEWAY_LABEL!: string;
public readonly SITE_LABEL!: string;
@IsNotEmpty()
public readonly RPC_GATEWAY_PORT!: string;
public readonly SITE_PORT!: string;
@IsNotEmpty()
public readonly RPC_GATEWAY_ROOT_URL!: string;
@IsNotEmpty()
public readonly ARCHIVE_NODES_URL!: string;
@IsNotEmpty()
public readonly ARCHIVE_NODES_PORT!: string;
@IsNotEmpty()
public readonly ROLLING_NODES_URL!: string;
@IsNotEmpty()
public readonly ROLLING_NODES_PORT!: string;
@IsNotEmpty()
public readonly TEZOS_NETWORK!: string;
public readonly SITE_ROOT_URL!: string;
public readonly NODE_ENV = process.env.NODE_ENV;
@ -64,18 +46,11 @@ export class BackendVariables {
this.DATABASE_USER = process.env["DATABASE_USER"]!;
this.DATABASE_PASSWORD = process.env["DATABASE_PASSWORD"]!;
this.DATABASE_NAME = process.env["DATABASE_NAME"]!;
this.API_HOSTNAME = process.env["API_HOSTNAME"]!;
this.API_LABEL = process.env["API_LABEL"]!;
this.API_PORT = process.env["API_PORT"]!;
this.API_ROOT_URL = process.env["API_ROOT_URL"]!;
this.RPC_GATEWAY_LABEL = process.env["RPC_GATEWAY_LABEL"]!;
this.RPC_GATEWAY_PORT = process.env["RPC_GATEWAY_PORT"]!;
this.RPC_GATEWAY_ROOT_URL = process.env["RPC_GATEWAY_ROOT_URL"]!;
this.ARCHIVE_NODES_URL = process.env["ARCHIVE_NODES_URL"]!;
this.ARCHIVE_NODES_PORT = process.env["ARCHIVE_NODES_PORT"]!;
this.ROLLING_NODES_URL = process.env["ROLLING_NODES_URL"]!;
this.ROLLING_NODES_PORT = process.env["ROLLING_NODES_PORT"]!;
this.TEZOS_NETWORK = process.env["TEZOS_NETWORK"]!;
this.SITE_PORT = process.env["SITE_PORT"]!;
this.SITE_ROOT_URL = process.env["SITE_ROOT_URL"]!;
this.SITE_LABEL = process.env["SITE_LABEL"]!;
}
public async validate() {
await validateOrReject(this);

View File

@ -1,11 +1,8 @@
import { BackendVariables } from "@Common/config/variables/Variables";
import Container from "typedi";
export default abstract class BaseService {
/** @TODO place methods in a config file */
public static readonly whitelisted: string[] = ["/chains/main/blocks"];
public static readonly blacklisted: string[] = ["/context/contracts", "/monitor", "/network"];
public static readonly rollingPatterns: string[] = ["/head", "/injection/operation"];
public static readonly network: string = Container.get(BackendVariables).TEZOS_NETWORK;
}

View File

@ -2,20 +2,20 @@ import "module-alias/register";
import "reflect-metadata";
import { Container } from "typedi";
import ExpressServer from "@Common/system/ExpressServer";
import routes from "@Api/controllers/index";
import routes from "@Site/index";
import cors from "cors";
import bodyParser from "body-parser";
// import TezosLink from "@Common/databases/TezosLink";
import errorHandler from "@Api/middlewares/ErrorHandler";
import errorHandler from "@Site/middlewares/ErrorHandler";
import { BackendVariables } from "@Common/config/variables/Variables";
(async () => {
try {
const variables = await Container.get(BackendVariables).validate();
const port = variables.API_PORT;
const rootUrl = variables.API_ROOT_URL;
const label = variables.API_LABEL ?? "Unknown Service";
const port = variables.SITE_PORT;
const rootUrl = variables.SITE_ROOT_URL;
const label = variables.SITE_LABEL ?? "Unknown Service";
// Container.get(TezosLink).connect();
Container.get(ExpressServer).init({

View File

@ -38,8 +38,11 @@
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"@Site/*": [
"src/app/*"
],
"@Api/*": [
"src/api/*"
"src/app/api/*"
],
"@Services/*": [
"src/common/services/*"