appinit-templates 1.0.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/base/common/,gitignore.ejs +33 -0
- package/base/common/.prettierrc.ejs +5 -0
- package/base/common/db.ejs +33 -0
- package/base/common/env.ejs +4 -0
- package/base/common/src/middleware/asyncHandler.ejs +5 -0
- package/base/common/src/routes/health.routes.ejs +12 -0
- package/base/common/src/routes/index.ejs +12 -0
- package/base/common/src/utils/ApiError.ejs +9 -0
- package/base/common/src/utils/logger.ejs +25 -0
- package/base/javascript/.prettierrc.ejs +1 -0
- package/base/javascript/Readme.md.ejs +135 -0
- package/base/javascript/eslint.config.js.ejs +26 -0
- package/base/javascript/src/config/db.js.ejs +34 -0
- package/base/javascript/src/config/env.js.ejs +37 -0
- package/base/javascript/src/controllers/health.controller.js.ejs +40 -0
- package/base/javascript/src/index.js.ejs +54 -0
- package/base/javascript/src/middleware/asyncHandler.js.ejs +1 -0
- package/base/javascript/src/middleware/errorHandler.js.ejs +11 -0
- package/base/javascript/src/routes/health.routes.js.ejs +1 -0
- package/base/javascript/src/routes/index.js.ejs +7 -0
- package/base/javascript/src/utils/ApiError.js.ejs +1 -0
- package/base/javascript/src/utils/logger.js.ejs +1 -0
- package/base/typescript/.prettierrc.ejs +1 -0
- package/base/typescript/Readme.md.ejs +141 -0
- package/base/typescript/eslint.config.js.ejs +29 -0
- package/base/typescript/src/config/db.ts.ejs +38 -0
- package/base/typescript/src/config/env.ts.ejs +37 -0
- package/base/typescript/src/controllers/health.controller.ts.ejs +50 -0
- package/base/typescript/src/index.ts.ejs +54 -0
- package/base/typescript/src/middleware/errorHandler.ts.ejs +20 -0
- package/base/typescript/src/routes/health.routes.ts.ejs +1 -0
- package/base/typescript/src/routes/index.ts.ejs +7 -0
- package/base/typescript/src/utils/ApiError.ts.ejs +12 -0
- package/base/typescript/src/utils/logger.ts.ejs +1 -0
- package/base/websockets/socket.io/javascript/index.js.ejs +87 -0
- package/base/websockets/socket.io/typescript/index.ts.ejs +81 -0
- package/base/websockets/ws/javascript/index.js.ejs +86 -0
- package/base/websockets/ws/typescript/index.ts.ejs +88 -0
- package/features/docker/base/javascript/Dockerfile.ejs +19 -0
- package/features/docker/base/typescript/Dockerfile.ejs +41 -0
- package/package.json +9 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
|
|
4
|
+
export default tseslint.config(
|
|
5
|
+
js.configs.recommended,
|
|
6
|
+
...tseslint.configs.recommended,
|
|
7
|
+
{
|
|
8
|
+
files: ["**/*.ts"],
|
|
9
|
+
languageOptions: {
|
|
10
|
+
ecmaVersion: 2022,
|
|
11
|
+
sourceType: "module",
|
|
12
|
+
parser: tseslint.parser,
|
|
13
|
+
parserOptions: {
|
|
14
|
+
project: "./tsconfig.json",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
rules: {
|
|
18
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
19
|
+
"@typescript-eslint/no-unused-vars": [
|
|
20
|
+
"warn",
|
|
21
|
+
{
|
|
22
|
+
argsIgnorePattern: "^_",
|
|
23
|
+
varsIgnorePattern: "^_",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
"no-console": "off",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<%_ if (database === 'mongo') { _%>
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
|
|
4
|
+
const connectDb = async (): Promise<void> => {
|
|
5
|
+
try {
|
|
6
|
+
if (mongoose.connection.readyState >= 1) return;
|
|
7
|
+
const conn = await mongoose.connect(process.env.MONGODB_URI);
|
|
8
|
+
console.log(`MongoDB Connected: ${conn.connection.host}`);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.error(
|
|
11
|
+
`MongoDB Connection Error: ${error instanceof Error ? error.message : error}`
|
|
12
|
+
);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default connectDb;
|
|
18
|
+
<%_ } else if (database === 'postgresql_prisma') { _%>
|
|
19
|
+
import { PrismaClient } from "../../generated/prisma/client";
|
|
20
|
+
|
|
21
|
+
declare global {
|
|
22
|
+
var prisma: PrismaClient | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const prisma = global.prisma ?? new PrismaClient();
|
|
26
|
+
|
|
27
|
+
if (process.env.NODE_ENV !== "production") {
|
|
28
|
+
global.prisma = prisma;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const connectDb = async (): Promise<void> => {
|
|
32
|
+
await prisma.$connect();
|
|
33
|
+
console.log("PostgreSQL Connected");
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { prisma, connectDb };
|
|
37
|
+
export default prisma;
|
|
38
|
+
<%_ } _%>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
dotenv.config();
|
|
5
|
+
|
|
6
|
+
const envSchema = z.object({
|
|
7
|
+
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
|
8
|
+
PORT: z.coerce.number().default(3000),
|
|
9
|
+
<%_ if (database === 'mongo') { _%>
|
|
10
|
+
MONGODB_URI: z.string().min(1, "MongoDB URI is required"),
|
|
11
|
+
<%_ } else if (database === 'postgresql_prisma') { _%>
|
|
12
|
+
DATABASE_URL: z.string().min(1, "Database URL is required"),
|
|
13
|
+
<%_ } _%>
|
|
14
|
+
JWT_SECRET: z.string().min(1, "JWT secret is required"),
|
|
15
|
+
JWT_EXPIRES_IN: z.string().default("7d"),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const parsed = envSchema.safeParse(process.env);
|
|
19
|
+
|
|
20
|
+
if (!parsed.success) {
|
|
21
|
+
console.error("Invalid environment variables:", parsed.error.format());
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const config = {
|
|
26
|
+
nodeEnv: parsed.data.NODE_ENV,
|
|
27
|
+
port: parsed.data.PORT,
|
|
28
|
+
<%_ if (database === 'mongo') { _%>
|
|
29
|
+
mongoUri: parsed.data.MONGODB_URI,
|
|
30
|
+
<%_ } else if (database === 'postgresql_prisma') { _%>
|
|
31
|
+
databaseUrl: parsed.data.DATABASE_URL,
|
|
32
|
+
<%_ } _%>
|
|
33
|
+
jwt: {
|
|
34
|
+
secret: parsed.data.JWT_SECRET,
|
|
35
|
+
expiresIn: parsed.data.JWT_EXPIRES_IN,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from "express";
|
|
2
|
+
<%_ if (database === 'mongo') { _%>
|
|
3
|
+
import mongoose from "mongoose";
|
|
4
|
+
<%_ } else if (database === 'postgresql_prisma') { _%>
|
|
5
|
+
import prisma from "../config/db";
|
|
6
|
+
<%_ } _%>
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Health Check Controller
|
|
10
|
+
* Checks the status of the server and the <%= database %> connection
|
|
11
|
+
*/
|
|
12
|
+
export const getHealth = async (_req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
13
|
+
try {
|
|
14
|
+
const uptime = process.uptime();
|
|
15
|
+
<%_ if (database === 'none') { _%>
|
|
16
|
+
const dbStatus: "disconnected" = "disconnected";
|
|
17
|
+
const isHealthy = true;
|
|
18
|
+
<%_ } else { _%>
|
|
19
|
+
let dbStatus: "connected" | "disconnected" | "error" = "disconnected";
|
|
20
|
+
|
|
21
|
+
<%_ if (database === 'mongo') { _%>
|
|
22
|
+
// Check Mongoose connection state (1 = connected)
|
|
23
|
+
dbStatus = mongoose.connection.readyState === 1 ? "connected" : "disconnected";
|
|
24
|
+
|
|
25
|
+
<%_ } else if (database === 'postgresql_prisma') { _%>
|
|
26
|
+
try {
|
|
27
|
+
await prisma.$queryRaw`SELECT 1`;
|
|
28
|
+
dbStatus = "connected";
|
|
29
|
+
} catch (error) {
|
|
30
|
+
dbStatus = "error";
|
|
31
|
+
}
|
|
32
|
+
<%_ } _%>
|
|
33
|
+
|
|
34
|
+
const isHealthy = dbStatus === "connected";
|
|
35
|
+
<%_ } _%>
|
|
36
|
+
|
|
37
|
+
res.status(isHealthy ? 200 : 503).json({
|
|
38
|
+
success: true,
|
|
39
|
+
data: {
|
|
40
|
+
status: "ok",
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
uptime: `${Math.floor(uptime)}s`,
|
|
43
|
+
database: dbStatus,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Since we removed asyncHandler, we manually pass the error to the global handler
|
|
48
|
+
next(error);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// typescript index file
|
|
2
|
+
import express, { type Request, type Response, type NextFunction } from "express";
|
|
3
|
+
import helmet from "helmet";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
import routes from "./routes/index";
|
|
6
|
+
import errorHandler from "./middleware/errorHandler";
|
|
7
|
+
import { logger } from "./utils/logger";
|
|
8
|
+
import { config } from "./config/env";
|
|
9
|
+
|
|
10
|
+
<%_ if (database === 'postgresql_prisma') { _%>
|
|
11
|
+
// Prisma note: in prisma/schema.prisma use:
|
|
12
|
+
// provider = "prisma-client-js"
|
|
13
|
+
// output = "../../generated/prisma"
|
|
14
|
+
<%_ } _%>
|
|
15
|
+
|
|
16
|
+
const app = express();
|
|
17
|
+
const port = config.port;
|
|
18
|
+
|
|
19
|
+
app.use(express.json());
|
|
20
|
+
app.use(helmet());
|
|
21
|
+
app.use(cors());
|
|
22
|
+
app.use(express.urlencoded({ extended: true }));
|
|
23
|
+
|
|
24
|
+
// Call connectDb() from ./config/db to connect to your database.
|
|
25
|
+
|
|
26
|
+
// logger middleware
|
|
27
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
28
|
+
logger.info({
|
|
29
|
+
ip: req.ip,
|
|
30
|
+
method: req.method,
|
|
31
|
+
path: req.path,
|
|
32
|
+
});
|
|
33
|
+
next();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
app.use("/api", routes);
|
|
37
|
+
|
|
38
|
+
app.use(errorHandler);
|
|
39
|
+
|
|
40
|
+
app.listen(port, () => {
|
|
41
|
+
logger.info(`Server running on port ${port}`);
|
|
42
|
+
logger.info(`Environment: ${config.nodeEnv}`);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
process.on("SIGINT", () => {
|
|
46
|
+
console.log("SIGINT received, forcefully stopping the server");
|
|
47
|
+
process.exit(0);
|
|
48
|
+
});
|
|
49
|
+
process.on("SIGTERM", () => {
|
|
50
|
+
console.log("SIGTERM received, gracefully stopping the server");
|
|
51
|
+
process.exit(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export default app;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from "express";
|
|
2
|
+
import ApiError from "../utils/ApiError";
|
|
3
|
+
|
|
4
|
+
type ErrorWithStatus = ApiError | (Error & { statusCode?: number });
|
|
5
|
+
|
|
6
|
+
const errorHandler = (
|
|
7
|
+
err: ErrorWithStatus,
|
|
8
|
+
_req: Request,
|
|
9
|
+
res: Response,
|
|
10
|
+
_next: NextFunction,
|
|
11
|
+
) => {
|
|
12
|
+
const statusCode = err.statusCode || 500;
|
|
13
|
+
const message = err.message || "Internal Server Error";
|
|
14
|
+
|
|
15
|
+
res.status(statusCode).json({
|
|
16
|
+
success: false,
|
|
17
|
+
message: message,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
export default errorHandler;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%- await include("../../../common/src/routes/health.routes.ejs") %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%- await include("../../../common/src/utils/logger.ejs") %>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import helmet from "helmet";
|
|
3
|
+
import cors from "cors";
|
|
4
|
+
import { createServer } from "http";
|
|
5
|
+
import { Server } from "socket.io";
|
|
6
|
+
import routes from "./src/routes/index.js";
|
|
7
|
+
import errorHandler from "./src/middleware/errorHandler.js";
|
|
8
|
+
import { logger } from "./src/utils/logger.js";
|
|
9
|
+
import { config } from "./src/config/env.js";
|
|
10
|
+
|
|
11
|
+
<%_ if (database === 'postgresql_prisma') { _%>
|
|
12
|
+
// Prisma note: in prisma/schema.prisma use:
|
|
13
|
+
// provider = "prisma-client-js"
|
|
14
|
+
// output = "../generated/prisma"
|
|
15
|
+
<%_ } _%>
|
|
16
|
+
|
|
17
|
+
const app = express();
|
|
18
|
+
const port = config.port;
|
|
19
|
+
|
|
20
|
+
// 1. Create the HTTP Server
|
|
21
|
+
const http_server = createServer(app);
|
|
22
|
+
|
|
23
|
+
// 2. Initialize Socket.io on the HTTP Server
|
|
24
|
+
const socket_io_server = new Server(http_server, {
|
|
25
|
+
cors: {
|
|
26
|
+
origin: "*",
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Middleware
|
|
31
|
+
app.use(express.json());
|
|
32
|
+
app.use(helmet());
|
|
33
|
+
app.use(cors());
|
|
34
|
+
app.use(express.urlencoded({ extended: true }));
|
|
35
|
+
|
|
36
|
+
app.use((req, res, next) => {
|
|
37
|
+
logger.info({
|
|
38
|
+
method: req.method,
|
|
39
|
+
path: req.path,
|
|
40
|
+
ip: req.ip,
|
|
41
|
+
});
|
|
42
|
+
next();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Routes
|
|
46
|
+
app.use("/api", routes);
|
|
47
|
+
app.use(errorHandler);
|
|
48
|
+
|
|
49
|
+
// 3. Socket.io Event Handling
|
|
50
|
+
socket_io_server.on("connection", (socket) => {
|
|
51
|
+
console.log(`Client connected: ${socket.id}`);
|
|
52
|
+
|
|
53
|
+
// 'data' is already an object here—no JSON.parse needed
|
|
54
|
+
socket.on("message", (data) => {
|
|
55
|
+
if (data.type === "ping") {
|
|
56
|
+
// No JSON.stringify needed
|
|
57
|
+
socket.send({ type: "pong" });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
socket.on("disconnect", () => {
|
|
62
|
+
console.log(`Client disconnected: ${socket.id}`);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
socket.on("error", (err) => {
|
|
66
|
+
console.error("Socket Error:", err);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 4. Start the server using http_server
|
|
71
|
+
http_server.listen(port, () => {
|
|
72
|
+
logger.info(`Server running on port ${port}`);
|
|
73
|
+
logger.info(`Environment: ${config.nodeEnv}`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Graceful Shutdown
|
|
77
|
+
const shutdown = () => {
|
|
78
|
+
console.log("Stopping server...");
|
|
79
|
+
http_server.close(() => {
|
|
80
|
+
process.exit(0);
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
process.on("SIGINT", shutdown);
|
|
85
|
+
process.on("SIGTERM", shutdown);
|
|
86
|
+
|
|
87
|
+
export default http_server;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// typescript index file with a socket.io server
|
|
2
|
+
import express, { type Request, type Response, type NextFunction } from "express";
|
|
3
|
+
import helmet from "helmet";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
import routes from "./routes/index";
|
|
6
|
+
import errorHandler from "./middleware/errorHandler";
|
|
7
|
+
import { logger } from "./utils/logger";
|
|
8
|
+
import { config } from "./config/env";
|
|
9
|
+
import { createServer } from "http";
|
|
10
|
+
import { Server } from "socket.io";
|
|
11
|
+
|
|
12
|
+
<%_ if (database === 'postgresql_prisma') { _%>
|
|
13
|
+
// Prisma note: in prisma/schema.prisma use:
|
|
14
|
+
// provider = "prisma-client-js"
|
|
15
|
+
// output = "../generated/prisma"
|
|
16
|
+
<%_ } _%>
|
|
17
|
+
|
|
18
|
+
const app = express();
|
|
19
|
+
const port = config.port;
|
|
20
|
+
const http_server = createServer(app);
|
|
21
|
+
const socket_io_server = new Server(http_server, {
|
|
22
|
+
cors: {
|
|
23
|
+
origin: "*",
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
app.use(express.json());
|
|
28
|
+
app.use(helmet());
|
|
29
|
+
app.use(cors());
|
|
30
|
+
app.use(express.urlencoded({ extended: true }));
|
|
31
|
+
|
|
32
|
+
// logger middleware
|
|
33
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
34
|
+
logger.info({
|
|
35
|
+
ip: req.ip,
|
|
36
|
+
method: req.method,
|
|
37
|
+
path: req.path,
|
|
38
|
+
});
|
|
39
|
+
next();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
app.use("/api", routes);
|
|
43
|
+
|
|
44
|
+
app.use(errorHandler);
|
|
45
|
+
|
|
46
|
+
http_server.listen(port, () => {
|
|
47
|
+
logger.info(`Server running on port ${port}`);
|
|
48
|
+
logger.info(`Environment: ${config.nodeEnv}`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
socket_io_server.on("connection", (socket) => {
|
|
52
|
+
logger.info(`Client connected: ${socket.id}`);
|
|
53
|
+
|
|
54
|
+
// Socket.io auto-parses incoming JSON into an object
|
|
55
|
+
socket.on("message", (data) => {
|
|
56
|
+
if (data.type === "ping") {
|
|
57
|
+
// Socket.io auto-stringifies outgoing objects
|
|
58
|
+
socket.emit("message", { type: "pong" });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
socket.on("error", (err) => {
|
|
63
|
+
logger.error(`Socket error: ${err.message}`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
socket.on("disconnect", (reason) => {
|
|
67
|
+
logger.info(`Client disconnected: ${socket.id} (Reason: ${reason})`);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
process.on("SIGINT", () => {
|
|
72
|
+
console.log("SIGINT received, forcefully stopping the server");
|
|
73
|
+
process.exit(0);
|
|
74
|
+
});
|
|
75
|
+
process.on("SIGTERM", () => {
|
|
76
|
+
console.log("SIGTERM received, gracefully stopping the server");
|
|
77
|
+
process.exit(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
export default http_server;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// javascript index file with WebSocketServer
|
|
2
|
+
import express from "express";
|
|
3
|
+
import helmet from "helmet";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
import router from "./src/routes/index.js";
|
|
6
|
+
import errorHandler from "./src/middleware/errorHandler.js";
|
|
7
|
+
import { logger } from "./src/utils/logger.js";
|
|
8
|
+
import { config } from "./src/config/env.js";
|
|
9
|
+
import { createServer } from "http";
|
|
10
|
+
import { WebSocketServer } from "ws";
|
|
11
|
+
|
|
12
|
+
<%_ if (database === 'postgresql_prisma') { _%>
|
|
13
|
+
// Prisma note: in prisma/schema.prisma use:
|
|
14
|
+
// provider = "prisma-client-js"
|
|
15
|
+
// output = "../generated/prisma"
|
|
16
|
+
<%_ } _%>
|
|
17
|
+
|
|
18
|
+
const app = express();
|
|
19
|
+
const port = config.port;
|
|
20
|
+
const http_server = createServer(app);
|
|
21
|
+
const wss = new WebSocketServer({ server: http_server });
|
|
22
|
+
|
|
23
|
+
app.use(express.json());
|
|
24
|
+
app.use(helmet());
|
|
25
|
+
app.use(cors());
|
|
26
|
+
app.use(express.urlencoded({ extended: true }));
|
|
27
|
+
// Call connectDb() from ./src/config/db.js to connect to your database and also update the env vars with database url
|
|
28
|
+
|
|
29
|
+
// logger middleware
|
|
30
|
+
app.use((req, res, next) => {
|
|
31
|
+
logger.info({
|
|
32
|
+
method: req.method,
|
|
33
|
+
path: req.path,
|
|
34
|
+
ip: req.ip,
|
|
35
|
+
});
|
|
36
|
+
next();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
app.use("/api", router);
|
|
40
|
+
|
|
41
|
+
app.use(errorHandler);
|
|
42
|
+
|
|
43
|
+
http_server.listen(port, () => {
|
|
44
|
+
logger.info(`Server running on port ${port}`);
|
|
45
|
+
logger.info(`Environment: ${config.nodeEnv}`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
process.on("SIGINT", () => {
|
|
49
|
+
console.log("SIGINT received, forcefully stopping the server");
|
|
50
|
+
process.exit(0);
|
|
51
|
+
});
|
|
52
|
+
process.on("SIGTERM", () => {
|
|
53
|
+
console.log("SIGTERM received, gracefully stopping the server");
|
|
54
|
+
process.exit(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
wss.on("connection", (socket) => {
|
|
58
|
+
socket.on("message", (data) => {
|
|
59
|
+
const message = data.toString();
|
|
60
|
+
try {
|
|
61
|
+
const msg = JSON.parse(message);
|
|
62
|
+
if (msg.type === "ping") {
|
|
63
|
+
socket.send(
|
|
64
|
+
JSON.stringify({
|
|
65
|
+
type: "Pong",
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
socket.send(
|
|
72
|
+
JSON.stringify({
|
|
73
|
+
type: "error",
|
|
74
|
+
message:
|
|
75
|
+
"incorrect format for sending message, Only use Json for messages",
|
|
76
|
+
}),
|
|
77
|
+
);
|
|
78
|
+
socket.close();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
socket.on("error", console.error);
|
|
83
|
+
socket.on("close", () => console.log(`client disconnected`));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export default http_server;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// typescript index file with a websocket server
|
|
2
|
+
import express, { type Request, type Response, type NextFunction } from "express";
|
|
3
|
+
import helmet from "helmet";
|
|
4
|
+
import cors from "cors";
|
|
5
|
+
import routes from "./routes/index";
|
|
6
|
+
import errorHandler from "./middleware/errorHandler";
|
|
7
|
+
import { logger } from "./utils/logger";
|
|
8
|
+
import { config } from "./config/env";
|
|
9
|
+
import { createServer } from "http";
|
|
10
|
+
import { WebSocketServer } from "ws";
|
|
11
|
+
|
|
12
|
+
<%_ if (database === 'postgresql_prisma') { _%>
|
|
13
|
+
// Prisma note: in prisma/schema.prisma use:
|
|
14
|
+
// provider = "prisma-client-js"
|
|
15
|
+
// output = "../generated/prisma"
|
|
16
|
+
<%_ } _%>
|
|
17
|
+
|
|
18
|
+
const app = express();
|
|
19
|
+
const port = config.port;
|
|
20
|
+
const http_server = createServer(app);
|
|
21
|
+
const wss = new WebSocketServer({ server: http_server });
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
app.use(helmet());
|
|
24
|
+
app.use(cors());
|
|
25
|
+
app.use(express.urlencoded({ extended: true }));
|
|
26
|
+
|
|
27
|
+
// logger middleware
|
|
28
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
29
|
+
logger.info({
|
|
30
|
+
ip: req.ip,
|
|
31
|
+
method: req.method,
|
|
32
|
+
path: req.path,
|
|
33
|
+
});
|
|
34
|
+
next();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
app.use("/api", routes);
|
|
38
|
+
|
|
39
|
+
app.use(errorHandler);
|
|
40
|
+
|
|
41
|
+
http_server.listen(port, () => {
|
|
42
|
+
logger.info(`Server running on port ${port}`);
|
|
43
|
+
logger.info(`Environment: ${config.nodeEnv}`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
wss.on("connection", (socket) => {
|
|
47
|
+
console.log("Client connected");
|
|
48
|
+
// Basic Error Handling for the socket
|
|
49
|
+
socket.on("error", console.error);
|
|
50
|
+
socket.on("message", (message: string) => {
|
|
51
|
+
try {
|
|
52
|
+
const msg = JSON.parse(message);
|
|
53
|
+
if (msg.type === "ping") {
|
|
54
|
+
socket.send(
|
|
55
|
+
JSON.stringify({
|
|
56
|
+
type: "pong",
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
socket.send(
|
|
63
|
+
JSON.stringify({
|
|
64
|
+
type: "error",
|
|
65
|
+
message:
|
|
66
|
+
"Incorrect format for sending message, Only use Json_Format for sending messages",
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
socket.close();
|
|
70
|
+
console.log(error);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
socket.on("close", () => {
|
|
74
|
+
console.log("Client Disconnected");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
process.on("SIGINT", () => {
|
|
79
|
+
console.log("SIGINT received, forcefully stopping the server");
|
|
80
|
+
process.exit(0);
|
|
81
|
+
});
|
|
82
|
+
process.on("SIGTERM", () => {
|
|
83
|
+
console.log("SIGTERM received, gracefully stopping the server");
|
|
84
|
+
process.exit(0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
export default http_server;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
FROM node:22-alpine AS builder
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
COPY ./package*.json ./
|
|
4
|
+
RUN npm install
|
|
5
|
+
COPY . .
|
|
6
|
+
|
|
7
|
+
RUN npm prune --omit=dev
|
|
8
|
+
|
|
9
|
+
FROM node:22-alpine AS runner
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
ENV NODE_ENV=production
|
|
12
|
+
|
|
13
|
+
COPY --from=builder /app/package*.json ./
|
|
14
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
15
|
+
COPY --from=builder /app ./
|
|
16
|
+
|
|
17
|
+
EXPOSE 3000
|
|
18
|
+
|
|
19
|
+
CMD [ "node","./index.js" ]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# --- Stage 1: Build Stage ---
|
|
2
|
+
FROM node:22-alpine AS builder
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# 1. Install dependencies first (for better caching)
|
|
6
|
+
COPY package*.json ./
|
|
7
|
+
RUN npm install
|
|
8
|
+
|
|
9
|
+
# 2. Copy source and tsconfig
|
|
10
|
+
COPY . .
|
|
11
|
+
|
|
12
|
+
# 3. Build the TypeScript code (assumes 'build' script runs 'tsc')
|
|
13
|
+
RUN npm run build
|
|
14
|
+
|
|
15
|
+
# 4. Prune devDependencies (removes tsc and @types)
|
|
16
|
+
RUN npm prune --production
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# --- Stage 2: Production Runner ---
|
|
20
|
+
FROM node:22-alpine AS runner
|
|
21
|
+
WORKDIR /app
|
|
22
|
+
|
|
23
|
+
# Set production environment
|
|
24
|
+
ENV NODE_ENV=production
|
|
25
|
+
|
|
26
|
+
# 1. Copy only the production node_modules from builder
|
|
27
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
28
|
+
|
|
29
|
+
# 2. Copy the compiled JS files (usually from a 'dist' or 'build' folder)
|
|
30
|
+
COPY --from=builder /app/dist ./dist
|
|
31
|
+
|
|
32
|
+
# 3. Copy package.json (needed for "type": "module" and start scripts)
|
|
33
|
+
COPY --from=builder /app/package*.json ./
|
|
34
|
+
|
|
35
|
+
# 4. Security: Run as non-root user
|
|
36
|
+
USER node
|
|
37
|
+
|
|
38
|
+
EXPOSE 4000
|
|
39
|
+
|
|
40
|
+
# 5. Run the compiled code
|
|
41
|
+
CMD ["node", "dist/index.js"]
|