diff --git a/Dockerfiles/Dockerfile.api b/Dockerfiles/Dockerfile.api index 6977373a..9cc4eadd 100644 --- a/Dockerfiles/Dockerfile.api +++ b/Dockerfiles/Dockerfile.api @@ -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 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 \ No newline at end of file diff --git a/Dockerfiles/Dockerfile.full b/Dockerfiles/Dockerfile.full deleted file mode 100644 index 77865e96..00000000 --- a/Dockerfiles/Dockerfile.full +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 94051fe6..d7f1c3d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ volumes: services: postgres: - image: postgres:11 + image: lecoffrelocal restart: always environment: - POSTGRES_USER diff --git a/id_ed25519 b/id_ed25519 new file mode 100755 index 00000000..597cc0ac --- /dev/null +++ b/id_ed25519 @@ -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----- diff --git a/package-lock.json b/package-lock.json index deca5734..ec4504bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" } diff --git a/package.json b/package.json index cb101758..151b6856 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/api/controllers/index.ts b/src/api/controllers/index.ts deleted file mode 100644 index c0dc0129..00000000 --- a/src/api/controllers/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Container } from "typedi"; -import ProjectController from "./ProjectController"; - -export default { - start: () => { - Container.get(ProjectController); - } -} diff --git a/src/app/HomeController.ts b/src/app/HomeController.ts new file mode 100644 index 00000000..ae366a0f --- /dev/null +++ b/src/app/HomeController.ts @@ -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!"); + } +} + diff --git a/src/api/controllers/ProjectController.ts b/src/app/api/ProjectController.ts similarity index 57% rename from src/api/controllers/ProjectController.ts rename to src/app/api/ProjectController.ts index fe7c2928..b865b2b1 100644 --- a/src/api/controllers/ProjectController.ts +++ b/src/app/api/ProjectController.ts @@ -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; 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)); - } } diff --git a/src/app/api/project/UserController.ts b/src/app/api/project/UserController.ts new file mode 100644 index 00000000..b865b2b1 --- /dev/null +++ b/src/app/api/project/UserController.ts @@ -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; + 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); + } +} + diff --git a/src/app/index.ts b/src/app/index.ts new file mode 100644 index 00000000..84bb36c2 --- /dev/null +++ b/src/app/index.ts @@ -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); + } +} diff --git a/src/api/middlewares/ErrorHandler.ts b/src/app/middlewares/ErrorHandler.ts similarity index 100% rename from src/api/middlewares/ErrorHandler.ts rename to src/app/middlewares/ErrorHandler.ts diff --git a/src/common/config/variables/Variables.ts b/src/common/config/variables/Variables.ts index 5e41cb57..66d13ad0 100644 --- a/src/common/config/variables/Variables.ts +++ b/src/common/config/variables/Variables.ts @@ -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); diff --git a/src/common/services/BaseService.ts b/src/common/services/BaseService.ts index 4613dc92..91aae64f 100644 --- a/src/common/services/BaseService.ts +++ b/src/common/services/BaseService.ts @@ -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; } diff --git a/src/entries/Api.ts b/src/entries/Site.ts similarity index 76% rename from src/entries/Api.ts rename to src/entries/Site.ts index e062259d..49c08318 100644 --- a/src/entries/Api.ts +++ b/src/entries/Site.ts @@ -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({ diff --git a/tsconfig.json b/tsconfig.json index 73fe76f0..e22f3657 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -38,8 +38,11 @@ "moduleResolution": "node", "baseUrl": ".", "paths": { + "@Site/*": [ + "src/app/*" + ], "@Api/*": [ - "src/api/*" + "src/app/api/*" ], "@Services/*": [ "src/common/services/*"