@appinit/cli 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +68 -0
- package/README.md +27 -95
- package/dist/index.d.ts +371 -2
- package/dist/index.js +3795 -24
- package/dist/templates/backend-express/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/backend-express/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/backend-express/.appinit-generate/project/.env.example +4 -0
- package/dist/templates/backend-express/.appinit-generate/project/.husky/pre-commit +1 -0
- package/dist/templates/backend-express/.appinit-generate/project/.prettierignore +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/.prettierrc +28 -0
- package/dist/templates/backend-express/.appinit-generate/project/.vscode/settings.json +6 -0
- package/dist/templates/backend-express/.appinit-generate/project/Dockerfile +14 -0
- package/dist/templates/backend-express/.appinit-generate/project/README.md +26 -0
- package/dist/templates/backend-express/.appinit-generate/project/eslint.config.js +76 -0
- package/dist/templates/backend-express/.appinit-generate/project/nodemon.json +8 -0
- package/dist/templates/backend-express/.appinit-generate/project/package.json +69 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/__tests__/health.test.ts +19 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/__tests__/validation.test.ts +36 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/app.ts +72 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/defaults.ts +5 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/dev.ts +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/env.ts +62 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/prod.ts +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/test.ts +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/errors/app-error.ts +20 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/errors/not-found.ts +16 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/middleware/error-handler.ts +73 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/middleware/http-logger.ts +5 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/middleware/validate.ts +17 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/routes/health.ts +13 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/routes/users.ts +12 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/schema/create-user.schema.ts +8 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/server.ts +54 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/utils/logger.ts +21 -0
- package/dist/templates/backend-express/.appinit-generate/project/tsconfig.json +30 -0
- package/dist/templates/backend-express/.appinit-generate/project/vitest.config.ts +15 -0
- package/dist/templates/backend-express/.appinit-preview/project/.appinit/project.json +6 -0
- package/dist/templates/backend-express/.appinit-preview/project/.appinit/template.json +6 -0
- package/dist/templates/backend-express/.appinit-preview/project/.env.example +4 -0
- package/dist/templates/backend-express/.appinit-preview/project/.husky/pre-commit +1 -0
- package/dist/templates/backend-express/.appinit-preview/project/.prettierignore +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/.prettierrc +28 -0
- package/dist/templates/backend-express/.appinit-preview/project/.vscode/settings.json +6 -0
- package/dist/templates/backend-express/.appinit-preview/project/Dockerfile +14 -0
- package/dist/templates/backend-express/.appinit-preview/project/README.md +26 -0
- package/dist/templates/backend-express/.appinit-preview/project/eslint.config.js +76 -0
- package/dist/templates/backend-express/.appinit-preview/project/nodemon.json +8 -0
- package/dist/templates/backend-express/.appinit-preview/project/package.json +69 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/__tests__/health.test.ts +19 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/__tests__/validation.test.ts +36 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/app.ts +72 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/defaults.ts +5 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/dev.ts +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/env.ts +62 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/prod.ts +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/test.ts +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/errors/app-error.ts +20 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/errors/not-found.ts +16 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/middleware/error-handler.ts +73 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/middleware/http-logger.ts +5 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/middleware/validate.ts +17 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/routes/health.ts +13 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/routes/users.ts +12 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/schema/create-user.schema.ts +8 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/server.ts +54 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/utils/logger.ts +21 -0
- package/dist/templates/backend-express/.appinit-preview/project/tsconfig.json +30 -0
- package/dist/templates/backend-express/.appinit-preview/project/vitest.config.ts +15 -0
- package/dist/templates/backend-express/README.md +107 -0
- package/dist/templates/backend-express/appinit.template.json +25 -0
- package/dist/templates/backend-express/docs/README.md +11 -0
- package/dist/templates/backend-express/package.json +5 -0
- package/dist/templates/backend-express/template/.env.example +4 -0
- package/dist/templates/backend-express/template/.husky/pre-commit +1 -0
- package/dist/templates/backend-express/template/.prettierignore +3 -0
- package/dist/templates/backend-express/template/.prettierrc +28 -0
- package/dist/templates/backend-express/template/.vscode/settings.json +6 -0
- package/dist/templates/backend-express/template/Dockerfile +14 -0
- package/dist/templates/backend-express/template/README.md__tmpl +26 -0
- package/dist/templates/backend-express/template/eslint.config.js +76 -0
- package/dist/templates/backend-express/template/nodemon.json +8 -0
- package/dist/templates/backend-express/template/package.json__tmpl +69 -0
- package/dist/templates/backend-express/template/src/__tests__/health.test.ts +19 -0
- package/dist/templates/backend-express/template/src/__tests__/validation.test.ts +36 -0
- package/dist/templates/backend-express/template/src/app.ts +72 -0
- package/dist/templates/backend-express/template/src/config/defaults.ts +5 -0
- package/dist/templates/backend-express/template/src/config/dev.ts +3 -0
- package/dist/templates/backend-express/template/src/config/env.ts +62 -0
- package/dist/templates/backend-express/template/src/config/prod.ts +3 -0
- package/dist/templates/backend-express/template/src/config/test.ts +3 -0
- package/dist/templates/backend-express/template/src/errors/app-error.ts +20 -0
- package/dist/templates/backend-express/template/src/errors/not-found.ts +16 -0
- package/dist/templates/backend-express/template/src/middleware/error-handler.ts +73 -0
- package/dist/templates/backend-express/template/src/middleware/http-logger.ts +5 -0
- package/dist/templates/backend-express/template/src/middleware/validate.ts +17 -0
- package/dist/templates/backend-express/template/src/routes/health.ts +13 -0
- package/dist/templates/backend-express/template/src/routes/users.ts +12 -0
- package/dist/templates/backend-express/template/src/schema/create-user.schema.ts +8 -0
- package/dist/templates/backend-express/template/src/server.ts +54 -0
- package/dist/templates/backend-express/template/src/utils/logger.ts +21 -0
- package/dist/templates/backend-express/template/tsconfig.json +30 -0
- package/dist/templates/backend-express/template/vitest.config.ts +15 -0
- package/dist/templates/backend-express/template.config.ts +36 -0
- package/dist/templates/backend-express/variables/defaults.json +7 -0
- package/dist/templates/backend-express/variables/defaults.ts +4 -0
- package/dist/templates/backend-express/variables/schema.json +22 -0
- package/dist/templates/backend-express/variables/schema.ts +11 -0
- package/dist/templates/backend-express/variables/transform.ts +6 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/README.md +26 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/nodemon.json +8 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/package-lock.json +3245 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/package.json +24 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/src/app.ts +13 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/src/server.ts +15 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/tsconfig.json +30 -0
- package/dist/templates/backend-fastify/README.md +27 -0
- package/dist/templates/backend-fastify/appinit.template.json +25 -0
- package/dist/templates/backend-fastify/docs/README.md +12 -0
- package/dist/templates/backend-fastify/package.json +5 -0
- package/dist/templates/backend-fastify/template/README.md__tmpl +26 -0
- package/dist/templates/backend-fastify/template/nodemon.json +8 -0
- package/dist/templates/backend-fastify/template/package.json__tmpl +24 -0
- package/dist/templates/backend-fastify/template/src/app.ts +13 -0
- package/dist/templates/backend-fastify/template/src/server.ts +15 -0
- package/dist/templates/backend-fastify/template/tsconfig.json +30 -0
- package/dist/templates/backend-fastify/template.config.ts +32 -0
- package/dist/templates/backend-fastify/variables/defaults.json +4 -0
- package/dist/templates/backend-fastify/variables/defaults.ts +3 -0
- package/dist/templates/backend-fastify/variables/schema.json +7 -0
- package/dist/templates/backend-fastify/variables/schema.ts +1 -0
- package/dist/templates/backend-fastify/variables/transform.ts +3 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/README.md +33 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/favicon.ico +0 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/globals.css +42 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/layout.tsx +33 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/page.module.css +141 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/page.tsx +67 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/next-env.d.ts +6 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/next.config.ts +8 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/package.json +25 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/file.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/globe.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/next.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/vercel.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/window.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/tsconfig.json +34 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/.appinit/project.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/.appinit/template.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/README.md +33 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/favicon.ico +0 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/globals.css +42 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/layout.tsx +33 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/page.module.css +141 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/page.tsx +67 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/next-env.d.ts +6 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/next.config.ts +8 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/package.json +25 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/file.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/globe.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/next.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/vercel.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/window.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/tsconfig.json +34 -0
- package/dist/templates/frontend-nextjs/README.md +27 -0
- package/dist/templates/frontend-nextjs/appinit.template.json +25 -0
- package/dist/templates/frontend-nextjs/docs/README.md +12 -0
- package/dist/templates/frontend-nextjs/package.json +5 -0
- package/dist/templates/frontend-nextjs/template/README.md__tmpl +33 -0
- package/dist/templates/frontend-nextjs/template/app/favicon.ico +0 -0
- package/dist/templates/frontend-nextjs/template/app/globals.css +42 -0
- package/dist/templates/frontend-nextjs/template/app/layout.tsx +33 -0
- package/dist/templates/frontend-nextjs/template/app/page.module.css +141 -0
- package/dist/templates/frontend-nextjs/template/app/page.tsx +67 -0
- package/dist/templates/frontend-nextjs/template/next-env.d.ts +6 -0
- package/dist/templates/frontend-nextjs/template/next.config.ts +8 -0
- package/dist/templates/frontend-nextjs/template/package.json__tmpl +25 -0
- package/dist/templates/frontend-nextjs/template/public/file.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/globe.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/next.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/vercel.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/window.svg +1 -0
- package/dist/templates/frontend-nextjs/template/tsconfig.json +34 -0
- package/dist/templates/frontend-nextjs/template.config.ts +28 -0
- package/dist/templates/frontend-nextjs/variables/defaults.json +6 -0
- package/dist/templates/frontend-nextjs/variables/defaults.ts +4 -0
- package/dist/templates/frontend-nextjs/variables/schema.json +22 -0
- package/dist/templates/frontend-nextjs/variables/schema.ts +7 -0
- package/dist/templates/frontend-nextjs/variables/transform.ts +3 -0
- package/dist/templates/frontend-react/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/frontend-react/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/frontend-react/.appinit-generate/project/README.md +14 -0
- package/dist/templates/frontend-react/.appinit-generate/project/index.html +12 -0
- package/dist/templates/frontend-react/.appinit-generate/project/package.json +26 -0
- package/dist/templates/frontend-react/.appinit-generate/project/public/vite.svg +30 -0
- package/dist/templates/frontend-react/.appinit-generate/project/src/App.tsx +16 -0
- package/dist/templates/frontend-react/.appinit-generate/project/src/assets/react.svg +1 -0
- package/dist/templates/frontend-react/.appinit-generate/project/src/index.css +9 -0
- package/dist/templates/frontend-react/.appinit-generate/project/src/main.tsx +10 -0
- package/dist/templates/frontend-react/.appinit-generate/project/tsconfig.app.json +31 -0
- package/dist/templates/frontend-react/.appinit-generate/project/tsconfig.json +7 -0
- package/dist/templates/frontend-react/.appinit-generate/project/tsconfig.node.json +26 -0
- package/dist/templates/frontend-react/.appinit-generate/project/vite.config.ts +16 -0
- package/dist/templates/frontend-react/appinit.template.json +26 -0
- package/dist/templates/frontend-react/docs/README.md +12 -0
- package/dist/templates/frontend-react/hooks/afterRender.ts +3 -0
- package/dist/templates/frontend-react/hooks/afterWrite.ts +3 -0
- package/dist/templates/frontend-react/hooks/beforeRender.ts +3 -0
- package/dist/templates/frontend-react/hooks/beforeWrite.ts +3 -0
- package/dist/templates/frontend-react/package.json +16 -0
- package/dist/templates/frontend-react/template/.env.example__tmpl +1 -0
- package/dist/templates/frontend-react/template/README.md__tmpl +14 -0
- package/dist/templates/frontend-react/template/_gitignore +41 -0
- package/dist/templates/frontend-react/template/index.html__tmpl +12 -0
- package/dist/templates/frontend-react/template/package.json__tmpl +26 -0
- package/dist/templates/frontend-react/template/public/vite.svg +30 -0
- package/dist/templates/frontend-react/template/src/App.tsx__tmpl +16 -0
- package/dist/templates/frontend-react/template/src/assets/react.svg +1 -0
- package/dist/templates/frontend-react/template/src/index.css__tmpl +9 -0
- package/dist/templates/frontend-react/template/src/main.tsx__tmpl +10 -0
- package/dist/templates/frontend-react/template/tsconfig.app.json__tmpl +31 -0
- package/dist/templates/frontend-react/template/tsconfig.json__tmpl +7 -0
- package/dist/templates/frontend-react/template/tsconfig.node.json__tmpl +26 -0
- package/dist/templates/frontend-react/template/vite.config.ts__tmpl +16 -0
- package/dist/templates/frontend-react/template.config.ts +26 -0
- package/dist/templates/frontend-react/template.meta.json +10 -0
- package/dist/templates/frontend-react/variables/defaults.json +6 -0
- package/dist/templates/frontend-react/variables/defaults.ts +6 -0
- package/dist/templates/frontend-react/variables/schema.json +15 -0
- package/dist/templates/frontend-react/variables/schema.ts +17 -0
- package/dist/templates/frontend-react/variables/transform.ts +10 -0
- package/dist/templates/frontend-vue/README.md +27 -0
- package/dist/templates/frontend-vue/appinit.template.json +25 -0
- package/dist/templates/frontend-vue/docs/README.md +12 -0
- package/dist/templates/frontend-vue/package.json +5 -0
- package/dist/templates/frontend-vue/template/README.md__tmpl +31 -0
- package/dist/templates/frontend-vue/template/index.html +13 -0
- package/dist/templates/frontend-vue/template/package.json__tmpl +25 -0
- package/dist/templates/frontend-vue/template/public/vite.svg +1 -0
- package/dist/templates/frontend-vue/template/src/App.vue +30 -0
- package/dist/templates/frontend-vue/template/src/assets/vue.svg +1 -0
- package/dist/templates/frontend-vue/template/src/components/HelloWorld.vue +42 -0
- package/dist/templates/frontend-vue/template/src/main.ts +6 -0
- package/dist/templates/frontend-vue/template/src/style.css +79 -0
- package/dist/templates/frontend-vue/template/tsconfig.app.json +16 -0
- package/dist/templates/frontend-vue/template/tsconfig.json +7 -0
- package/dist/templates/frontend-vue/template/tsconfig.node.json +26 -0
- package/dist/templates/frontend-vue/template/vite.config.ts +7 -0
- package/dist/templates/frontend-vue/template.config.ts +32 -0
- package/dist/templates/frontend-vue/variables/defaults.json +6 -0
- package/dist/templates/frontend-vue/variables/defaults.ts +3 -0
- package/dist/templates/frontend-vue/variables/schema.json +22 -0
- package/dist/templates/frontend-vue/variables/schema.ts +1 -0
- package/dist/templates/frontend-vue/variables/transform.ts +3 -0
- package/package.json +40 -29
- package/bin/appinit.js +0 -15
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import request from "supertest";
|
|
2
|
+
|
|
3
|
+
import { app } from "../app.js";
|
|
4
|
+
|
|
5
|
+
describe("Health endpoints", () => {
|
|
6
|
+
it("GET /health should return ok", async () => {
|
|
7
|
+
const res = await request(app).get("/api/health");
|
|
8
|
+
|
|
9
|
+
expect(res.status).toBe(200);
|
|
10
|
+
expect(res.body).toEqual({ status: "ok" });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("GET /ready should return ready", async () => {
|
|
14
|
+
const res = await request(app).get("/api/health/ready");
|
|
15
|
+
|
|
16
|
+
expect(res.status).toBe(200);
|
|
17
|
+
expect(res.body).toEqual({ ready: true });
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import request from "supertest";
|
|
2
|
+
|
|
3
|
+
import { app } from "../app.js";
|
|
4
|
+
|
|
5
|
+
describe("Zod validation errors", () => {
|
|
6
|
+
it("returns 400 with field errors", async () => {
|
|
7
|
+
const res = await request(app)
|
|
8
|
+
.post("/api/users")
|
|
9
|
+
.send({ email: "invalid", password: "123" });
|
|
10
|
+
|
|
11
|
+
expect(res.status).toBe(400);
|
|
12
|
+
expect(res.body).toEqual({
|
|
13
|
+
error: {
|
|
14
|
+
code: "VALIDATION_ERROR",
|
|
15
|
+
message: "Invalid request",
|
|
16
|
+
fields: {
|
|
17
|
+
"body.email": ["Invalid email address"],
|
|
18
|
+
"body.password": [
|
|
19
|
+
"Too small: expected string to have >=8 characters",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("returns 201 with register", async () => {
|
|
27
|
+
const res = await request(app)
|
|
28
|
+
.post("/api/users")
|
|
29
|
+
.send({ email: "abc@gmail.com", password: "12345678" });
|
|
30
|
+
|
|
31
|
+
expect(res.status).toBe(201);
|
|
32
|
+
expect(res.body).toEqual({
|
|
33
|
+
ok: true,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import compression from "compression";
|
|
2
|
+
import cors from "cors";
|
|
3
|
+
import express, { Express } from "express";
|
|
4
|
+
import type { Request, Response } from "express";
|
|
5
|
+
import helmet from "helmet";
|
|
6
|
+
import morgan from "morgan";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
|
|
10
|
+
import { env } from "./config/env";
|
|
11
|
+
import { notFoundHandler } from "./errors/not-found";
|
|
12
|
+
import { errorHandler } from "./middleware/error-handler";
|
|
13
|
+
import { httpLogger } from "./middleware/http-logger";
|
|
14
|
+
import { healthRouter } from "./routes/health";
|
|
15
|
+
import { userRouter } from "./routes/users";
|
|
16
|
+
|
|
17
|
+
// Define __dirname for ESM
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
|
|
21
|
+
const app: Express = express();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 1. SECURITY MIDDLEWARE
|
|
25
|
+
*/
|
|
26
|
+
app.use(helmet());
|
|
27
|
+
app.use(cors({ origin: env.corsOrigin }));
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 2. PERFORMANCE MIDDLEWARE
|
|
31
|
+
*/
|
|
32
|
+
app.use(compression());
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 3. REQUEST PARSING
|
|
36
|
+
*/
|
|
37
|
+
app.use(express.json({ limit: "10kb" }));
|
|
38
|
+
app.use(express.urlencoded({ extended: true, limit: "10kb" }));
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 4. LOGGING & STATIC FILES
|
|
42
|
+
*/
|
|
43
|
+
app.use(httpLogger);
|
|
44
|
+
if (process.env.NODE_ENV !== "production") {
|
|
45
|
+
app.use(morgan("dev"));
|
|
46
|
+
} else {
|
|
47
|
+
app.use(morgan("combined"));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fixed static path for ESM
|
|
51
|
+
app.use(express.static(path.join(__dirname, "../public")));
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 5. SETTINGS
|
|
55
|
+
*/
|
|
56
|
+
app.disable("x-powered-by");
|
|
57
|
+
|
|
58
|
+
// route
|
|
59
|
+
app.get("/", (_req: Request, res: Response) => {
|
|
60
|
+
res.status(200).json({
|
|
61
|
+
version: "1.0.0",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
app.use("/api/health", healthRouter);
|
|
66
|
+
app.use("/api/users", userRouter);
|
|
67
|
+
/* 404 */
|
|
68
|
+
app.use(notFoundHandler);
|
|
69
|
+
|
|
70
|
+
/* error handler (LAST) */
|
|
71
|
+
app.use(errorHandler);
|
|
72
|
+
export { app };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { defaults } from "./defaults";
|
|
4
|
+
import { developmentConfig } from "./dev";
|
|
5
|
+
import { productionConfig } from "./prod";
|
|
6
|
+
import { testConfig } from "./test";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Load dotenv ONLY outside production
|
|
10
|
+
*/
|
|
11
|
+
if (process.env.NODE_ENV !== "production") {
|
|
12
|
+
await import("dotenv/config");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Raw env schema (what the platform provides)
|
|
17
|
+
*/
|
|
18
|
+
const rawEnvSchema = z.object({
|
|
19
|
+
NODE_ENV: z
|
|
20
|
+
.enum(["development", "test", "production"])
|
|
21
|
+
.default("development"),
|
|
22
|
+
PORT: z.coerce.number().int().positive().optional(),
|
|
23
|
+
LOG_LEVEL: z
|
|
24
|
+
.enum(["fatal", "error", "warn", "info", "debug", "trace"])
|
|
25
|
+
.optional(),
|
|
26
|
+
CORS_ORIGIN: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validate process.env
|
|
31
|
+
*/
|
|
32
|
+
const parsed = rawEnvSchema.safeParse(process.env);
|
|
33
|
+
|
|
34
|
+
if (!parsed.success) {
|
|
35
|
+
console.error("❌ Invalid environment variables");
|
|
36
|
+
console.error(parsed.error.format());
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const nodeEnv = parsed.data.NODE_ENV;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Select environment config
|
|
44
|
+
*/
|
|
45
|
+
const envConfigMap = {
|
|
46
|
+
development: developmentConfig,
|
|
47
|
+
test: testConfig,
|
|
48
|
+
production: productionConfig,
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
51
|
+
const envOverrides = envConfigMap[nodeEnv];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Final resolved config (single export)
|
|
55
|
+
*/
|
|
56
|
+
export const env = {
|
|
57
|
+
nodeEnv,
|
|
58
|
+
|
|
59
|
+
port: parsed.data.PORT ?? defaults.port,
|
|
60
|
+
logLevel: parsed.data.LOG_LEVEL ?? envOverrides.logLevel ?? defaults.logLevel,
|
|
61
|
+
corsOrigin: parsed.data.CORS_ORIGIN ?? defaults.corsOrigin,
|
|
62
|
+
} as const;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class AppError extends Error {
|
|
2
|
+
public readonly statusCode: number;
|
|
3
|
+
public readonly code: string;
|
|
4
|
+
public readonly isOperational: boolean;
|
|
5
|
+
|
|
6
|
+
constructor(
|
|
7
|
+
message: string,
|
|
8
|
+
options: {
|
|
9
|
+
statusCode?: number;
|
|
10
|
+
code?: string;
|
|
11
|
+
isOperational?: boolean;
|
|
12
|
+
} = {},
|
|
13
|
+
) {
|
|
14
|
+
super(message);
|
|
15
|
+
|
|
16
|
+
this.statusCode = options.statusCode ?? 500;
|
|
17
|
+
this.code = options.code ?? "INTERNAL_SERVER_ERROR";
|
|
18
|
+
this.isOperational = options.isOperational ?? true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from "express";
|
|
2
|
+
|
|
3
|
+
import { AppError } from "../errors/app-error.js";
|
|
4
|
+
|
|
5
|
+
export function notFoundHandler(
|
|
6
|
+
_req: Request,
|
|
7
|
+
_res: Response,
|
|
8
|
+
next: NextFunction,
|
|
9
|
+
) {
|
|
10
|
+
next(
|
|
11
|
+
new AppError("Route not found", {
|
|
12
|
+
statusCode: 404,
|
|
13
|
+
code: "NOT_FOUND",
|
|
14
|
+
}),
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from "express";
|
|
2
|
+
import { ZodError } from "zod";
|
|
3
|
+
|
|
4
|
+
import { env } from "../config/env.js";
|
|
5
|
+
import { AppError } from "../errors/app-error.js";
|
|
6
|
+
import { logger } from "../utils/logger.js";
|
|
7
|
+
|
|
8
|
+
export function errorHandler(
|
|
9
|
+
err: unknown,
|
|
10
|
+
_req: Request,
|
|
11
|
+
res: Response,
|
|
12
|
+
_next: NextFunction,
|
|
13
|
+
) {
|
|
14
|
+
/**
|
|
15
|
+
* Zod validation errors (400)
|
|
16
|
+
*/
|
|
17
|
+
if (err instanceof ZodError) {
|
|
18
|
+
const fieldErrors: Record<string, string[]> = {};
|
|
19
|
+
|
|
20
|
+
for (const issue of err.issues) {
|
|
21
|
+
const field = issue.path.join(".") || "root";
|
|
22
|
+
fieldErrors[field] ??= [];
|
|
23
|
+
fieldErrors[field].push(issue.message);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
logger.warn({ fieldErrors }, "Request validation failed");
|
|
27
|
+
|
|
28
|
+
return res.status(400).json({
|
|
29
|
+
error: {
|
|
30
|
+
code: "VALIDATION_ERROR",
|
|
31
|
+
message: "Invalid request",
|
|
32
|
+
fields: fieldErrors,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Normalize error
|
|
37
|
+
const error =
|
|
38
|
+
err instanceof AppError
|
|
39
|
+
? err
|
|
40
|
+
: new AppError("Internal server error", {
|
|
41
|
+
statusCode: 500,
|
|
42
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
43
|
+
isOperational: false,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Log full error (always)
|
|
47
|
+
logger.error(
|
|
48
|
+
{
|
|
49
|
+
err,
|
|
50
|
+
statusCode: error.statusCode,
|
|
51
|
+
code: error.code,
|
|
52
|
+
},
|
|
53
|
+
"Unhandled application error",
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Response (safe for clients)
|
|
57
|
+
const response: Record<string, unknown> = {
|
|
58
|
+
error: {
|
|
59
|
+
code: error.code,
|
|
60
|
+
message: error.message,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Dev-only details
|
|
65
|
+
if (env.nodeEnv !== "production") {
|
|
66
|
+
response.error = {
|
|
67
|
+
...(response.error ?? {}),
|
|
68
|
+
details: err instanceof Error ? err.stack : err,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
res.status(error.statusCode).json(response);
|
|
73
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from "express";
|
|
2
|
+
import type { ZodSchema } from "zod";
|
|
3
|
+
|
|
4
|
+
export const validate =
|
|
5
|
+
<T>(schema: ZodSchema<T>) =>
|
|
6
|
+
(req: Request, _res: Response, next: NextFunction) => {
|
|
7
|
+
try {
|
|
8
|
+
schema.parse({
|
|
9
|
+
body: req.body,
|
|
10
|
+
query: req.query,
|
|
11
|
+
params: req.params,
|
|
12
|
+
});
|
|
13
|
+
next();
|
|
14
|
+
} catch (err) {
|
|
15
|
+
next(err);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
|
|
3
|
+
export const router = Router();
|
|
4
|
+
|
|
5
|
+
router.get("/", (_req, res) => {
|
|
6
|
+
res.status(200).json({ status: "ok" });
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
router.get("/ready", (_req, res) => {
|
|
10
|
+
res.status(200).json({ ready: true });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export { router as healthRouter };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
|
|
3
|
+
import { validate } from "../middleware/validate";
|
|
4
|
+
import { createUserSchema } from "../schema/create-user.schema";
|
|
5
|
+
|
|
6
|
+
const router = Router();
|
|
7
|
+
|
|
8
|
+
router.post("/", validate(createUserSchema), (_req, res) => {
|
|
9
|
+
res.status(201).json({ ok: true });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export { router as userRouter };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { app } from "./app";
|
|
2
|
+
import { logger } from "./utils/logger";
|
|
3
|
+
|
|
4
|
+
// Assuming the Pino logger from before
|
|
5
|
+
|
|
6
|
+
const PORT = process.env.PORT || 3001;
|
|
7
|
+
|
|
8
|
+
const server = app.listen(PORT, () => {
|
|
9
|
+
logger.info(`🚀 Server version 1.0.0 started`);
|
|
10
|
+
logger.info(`📡 Listening on http://localhost:${PORT}`);
|
|
11
|
+
logger.info(`🌍 Environment: ${process.env.NODE_ENV || "development"}`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* GRACEFUL SHUTDOWN LOGIC
|
|
16
|
+
* Ensures the server closes existing connections before exiting.
|
|
17
|
+
*/
|
|
18
|
+
const shutdown = (signal: string) => {
|
|
19
|
+
logger.info(`${signal} received. Starting graceful shutdown...`);
|
|
20
|
+
|
|
21
|
+
server.close(() => {
|
|
22
|
+
logger.info("⛔ HTTP server closed.");
|
|
23
|
+
|
|
24
|
+
// Close database connections here (e.g., prisma.$disconnect() or mongoose.connection.close())
|
|
25
|
+
|
|
26
|
+
process.exit(0);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Force close after 10 seconds if graceful shutdown fails
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
logger.error(
|
|
32
|
+
"Could not close connections in time, forcefully shutting down",
|
|
33
|
+
);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}, 10000);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Listen for termination signals
|
|
39
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
40
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* UNHANDLED REJECTION/EXCEPTION CATCHING
|
|
44
|
+
* Prevents the process from crashing silently.
|
|
45
|
+
*/
|
|
46
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
47
|
+
logger.fatal({ promise, reason }, "Unhandled Rejection at Promise");
|
|
48
|
+
throw reason; // Let uncaughtException handle it
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
process.on("uncaughtException", (error) => {
|
|
52
|
+
logger.fatal(error, "Uncaught Exception thrown");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import pino from "pino";
|
|
2
|
+
|
|
3
|
+
import { env } from "../config/env.js";
|
|
4
|
+
|
|
5
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
6
|
+
|
|
7
|
+
export const logger = pino({
|
|
8
|
+
level: env.logLevel,
|
|
9
|
+
// Redact sensitive data from logs for security
|
|
10
|
+
redact: ["req.headers.authorization", "password", "confirmPassword"],
|
|
11
|
+
transport: isProduction
|
|
12
|
+
? undefined // In production, output raw JSON for Datadog/ELK
|
|
13
|
+
: {
|
|
14
|
+
target: "pino-pretty",
|
|
15
|
+
options: {
|
|
16
|
+
colorize: true,
|
|
17
|
+
translateTime: "SYS:standard",
|
|
18
|
+
ignore: "pid,hostname",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
/* Language & runtime */
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
|
|
8
|
+
/* Project structure */
|
|
9
|
+
"rootDir": "src",
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
|
|
12
|
+
/* Interop & correctness */
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"allowSyntheticDefaultImports": true,
|
|
15
|
+
|
|
16
|
+
/* Safety */
|
|
17
|
+
"strict": true,
|
|
18
|
+
"forceConsistentCasingInFileNames": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noEmitOnError": true,
|
|
21
|
+
|
|
22
|
+
/* DX & production debugging */
|
|
23
|
+
"sourceMap": true,
|
|
24
|
+
|
|
25
|
+
/* Node environment */
|
|
26
|
+
"types": ["node", "vitest/globals"]
|
|
27
|
+
},
|
|
28
|
+
"include": ["src"],
|
|
29
|
+
"exclude": ["node_modules", "dist", "__tests__"]
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
environment: "node",
|
|
6
|
+
globals: true,
|
|
7
|
+
clearMocks: true,
|
|
8
|
+
restoreMocks: true,
|
|
9
|
+
coverage: {
|
|
10
|
+
provider: "v8",
|
|
11
|
+
reporter: ["text", "html"],
|
|
12
|
+
exclude: ["dist", "node_modules"],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const config = {
|
|
2
|
+
// Canonical template identity
|
|
3
|
+
id: "backend.backend-express.basic",
|
|
4
|
+
version: "0.1.0",
|
|
5
|
+
|
|
6
|
+
// ATS contract
|
|
7
|
+
appinitSpec: "1.0",
|
|
8
|
+
atsVersion: "1.0",
|
|
9
|
+
|
|
10
|
+
// Classification
|
|
11
|
+
category: "backend",
|
|
12
|
+
language: "ts",
|
|
13
|
+
// Rendering
|
|
14
|
+
engine: "none", // liquid | none, default is none
|
|
15
|
+
|
|
16
|
+
// Output
|
|
17
|
+
output: {
|
|
18
|
+
dir: ".",
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Metadata
|
|
22
|
+
name: "backend-express",
|
|
23
|
+
description: "Backend template (Express, Fastify, etc.)",
|
|
24
|
+
|
|
25
|
+
tags: ["backend", "api", "server"],
|
|
26
|
+
|
|
27
|
+
license: "MIT",
|
|
28
|
+
postInstall: [],
|
|
29
|
+
author: {
|
|
30
|
+
name: "AppInit",
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
paths: {
|
|
34
|
+
template: "./template",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"projectName": {
|
|
3
|
+
"type": "string",
|
|
4
|
+
"required": true,
|
|
5
|
+
"minLength": 1
|
|
6
|
+
},
|
|
7
|
+
"description": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"author": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"licenseType": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": [
|
|
16
|
+
"MIT",
|
|
17
|
+
"Apache-2.0",
|
|
18
|
+
"ISC",
|
|
19
|
+
"UNLICENSED"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ⚛️ backend-fastify
|
|
2
|
+
|
|
3
|
+
This project was generated using **AppInit 1.0**.
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
pnpm dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 🛠️ Stack Configuration
|
|
13
|
+
|
|
14
|
+
- **Framework**: React 18+ (Vite)
|
|
15
|
+
- **Engine Used**:
|
|
16
|
+
- **Generated At**:
|
|
17
|
+
|
|
18
|
+
## 📂 Project Structure
|
|
19
|
+
|
|
20
|
+
- `src/`: Your React components and logic.
|
|
21
|
+
- `public/`: Static assets (images, icons).
|
|
22
|
+
- `tailwind.config.js`: Styling configuration.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
_Built with speed using the AppInit Scaffolding Engine._
|