init
This commit is contained in:
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
dist
|
||||||
7
compose.yaml
Normal file
7
compose.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
api:
|
||||||
|
container_name: api
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
build:
|
||||||
|
context: ./services/api
|
||||||
14
services/api/Dockerfile
Normal file
14
services/api/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM node:slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm i
|
||||||
|
|
||||||
|
# Generate kysely types
|
||||||
|
RUN npx kysely-codegen
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
CMD ["npm", "run", "start"]
|
||||||
2283
services/api/package-lock.json
generated
Normal file
2283
services/api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
services/api/package.json
Normal file
34
services/api/package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "honong",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Honong web app",
|
||||||
|
"license": "ISC",
|
||||||
|
"author": "Adam Vo",
|
||||||
|
"type": "module",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsx --watch src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/index.js",
|
||||||
|
"gen-t": "kysely-codegen"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/cors": "^2.8.19",
|
||||||
|
"@types/express": "^5.0.6",
|
||||||
|
"@types/morgan": "^1.9.10",
|
||||||
|
"@types/node": "^25.3.0",
|
||||||
|
"@types/pg": "^8.16.0",
|
||||||
|
"tsx": "^4.21.0",
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.6",
|
||||||
|
"dotenv": "^17.3.1",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"kysely": "^0.28.11",
|
||||||
|
"kysely-codegen": "^0.20.0",
|
||||||
|
"morgan": "^1.10.1",
|
||||||
|
"pg": "^8.18.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
services/api/src/app.ts
Normal file
35
services/api/src/app.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import "dotenv/config";
|
||||||
|
import express from "express";
|
||||||
|
import db from "./lib/db.js";
|
||||||
|
import crypto from "crypto";
|
||||||
|
import morgan from "morgan";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(morgan("dev"));
|
||||||
|
|
||||||
|
app.get("/", async (req, res, next) => {
|
||||||
|
const notes = await db.selectFrom("notes").selectAll().execute();
|
||||||
|
|
||||||
|
res.json({ notes });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/", async (req, res, next) => {
|
||||||
|
const created = await db
|
||||||
|
.insertInto("notes")
|
||||||
|
.values({
|
||||||
|
title: "test",
|
||||||
|
content: "text",
|
||||||
|
completed: true,
|
||||||
|
uuid: crypto.randomUUID(),
|
||||||
|
created_at: new Date(),
|
||||||
|
updated_at: new Date(),
|
||||||
|
completed_at: new Date(),
|
||||||
|
})
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow();
|
||||||
|
|
||||||
|
res.json({ created });
|
||||||
|
});
|
||||||
|
|
||||||
|
export { app };
|
||||||
10
services/api/src/index.ts
Normal file
10
services/api/src/index.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import "dotenv/config";
|
||||||
|
import { app } from "./app.js";
|
||||||
|
import { shutdown } from "./lib/shutdown.js";
|
||||||
|
|
||||||
|
const server = app.listen(process.env.PORT, () => {
|
||||||
|
console.log(`App running on port ${process.env.PORT} !`);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("SIGTERM", () => shutdown("SIGTERM", server));
|
||||||
|
process.on("SIGINT", () => shutdown("SIGINT", server));
|
||||||
13
services/api/src/lib/db.ts
Normal file
13
services/api/src/lib/db.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Kysely, PostgresDialect } from "kysely";
|
||||||
|
import type { DB } from "kysely-codegen";
|
||||||
|
import { Pool } from "pg";
|
||||||
|
|
||||||
|
const db = new Kysely<DB>({
|
||||||
|
dialect: new PostgresDialect({
|
||||||
|
pool: new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default db;
|
||||||
26
services/api/src/lib/shutdown.ts
Normal file
26
services/api/src/lib/shutdown.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import type { SignalConstants } from "node:os";
|
||||||
|
import db from "./db.js";
|
||||||
|
import type { Server } from "node:http";
|
||||||
|
|
||||||
|
export const shutdown = async (
|
||||||
|
signal: keyof SignalConstants,
|
||||||
|
server: Server,
|
||||||
|
) => {
|
||||||
|
console.log(`${signal} received. Shutting down gracefully...`);
|
||||||
|
|
||||||
|
server.close(async () => {
|
||||||
|
try {
|
||||||
|
await db.destroy();
|
||||||
|
console.log("Database connection closed.");
|
||||||
|
process.exit(0);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error during shutdown:", err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.error("Forcing shutdown after timeout");
|
||||||
|
process.exit(1);
|
||||||
|
}, 10000);
|
||||||
|
};
|
||||||
42
services/api/tsconfig.json
Normal file
42
services/api/tsconfig.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
// Visit https://aka.ms/tsconfig to read more about this file
|
||||||
|
"compilerOptions": {
|
||||||
|
// File Layout
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
|
||||||
|
// Environment Settings
|
||||||
|
// See also https://aka.ms/tsconfig/module
|
||||||
|
"module": "nodenext",
|
||||||
|
"target": "esnext",
|
||||||
|
// For nodejs:
|
||||||
|
"lib": ["esnext"],
|
||||||
|
"types": ["node"],
|
||||||
|
|
||||||
|
// Other Outputs
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
|
||||||
|
// Stricter Typechecking Options
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
|
||||||
|
// Style Options
|
||||||
|
// "noImplicitReturns": true,
|
||||||
|
// "noImplicitOverride": true,
|
||||||
|
// "noUnusedLocals": true,
|
||||||
|
// "noUnusedParameters": true,
|
||||||
|
// "noFallthroughCasesInSwitch": true,
|
||||||
|
// "noPropertyAccessFromIndexSignature": true,
|
||||||
|
|
||||||
|
// Recommended Options
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user