├── .gitignore ├── bun-and-hono ├── bun-hono-task-crud-api │ ├── .gitignore │ ├── README.md │ ├── bun.lock │ ├── bun_hono_task_crud.sqlite │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── controllers │ │ │ ├── auth.ts │ │ │ └── task.ts │ │ ├── database │ │ │ └── db.ts │ │ ├── index.ts │ │ └── types │ │ │ └── index.ts │ └── tsconfig.json └── concepts │ ├── .gitignore │ ├── README.md │ ├── bun.lock │ ├── bundb.sqlite │ ├── features │ ├── binary.ts │ ├── fetch.ts │ ├── fs.ts │ ├── hashing.ts │ ├── meta-env.ts │ ├── server.ts │ └── sqllite.ts │ ├── index.ts │ ├── output.txt │ ├── package.json │ ├── read.txt │ └── tsconfig.json ├── nest-js ├── concepts │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── eslint.config.mjs │ ├── nest-cli.json │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app.controller.spec.ts │ │ ├── app.controller.ts │ │ ├── app.module.ts │ │ ├── app.service.ts │ │ ├── blog │ │ │ ├── blog.controller.spec.ts │ │ │ ├── blog.controller.ts │ │ │ ├── blog.module.ts │ │ │ ├── blog.service.spec.ts │ │ │ └── blog.service.ts │ │ └── main.ts │ ├── test │ │ ├── app.e2e-spec.ts │ │ └── jest-e2e.json │ ├── tsconfig.build.json │ └── tsconfig.json ├── file-upload │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── eslint.config.mjs │ ├── nest-cli.json │ ├── package-lock.json │ ├── package.json │ ├── prisma │ │ ├── migrations │ │ │ ├── 20250302190131_init │ │ │ │ └── migration.sql │ │ │ └── migration_lock.toml │ │ └── schema.prisma │ ├── src │ │ ├── app.controller.spec.ts │ │ ├── app.controller.ts │ │ ├── app.module.ts │ │ ├── app.service.ts │ │ ├── file-upload │ │ │ ├── file-upload.controller.spec.ts │ │ │ ├── file-upload.controller.ts │ │ │ ├── file-upload.module.ts │ │ │ ├── file-upload.service.spec.ts │ │ │ └── file-upload.service.ts │ │ ├── main.ts │ │ └── prisma │ │ │ └── prisma.service.ts │ ├── test │ │ ├── app.e2e-spec.ts │ │ └── jest-e2e.json │ ├── tsconfig.build.json │ └── tsconfig.json └── travel-tracker │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── eslint.config.mjs │ ├── nest-cli.json │ ├── package-lock.json │ ├── package.json │ ├── prisma │ ├── migrations │ │ ├── 20250301182602_init │ │ │ └── migration.sql │ │ └── migration_lock.toml │ └── schema.prisma │ ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── auth │ │ ├── auth.controller.spec.ts │ │ ├── auth.controller.ts │ │ ├── auth.module.ts │ │ ├── auth.service.spec.ts │ │ ├── auth.service.ts │ │ ├── dto │ │ │ ├── login.dto.ts │ │ │ └── register.dto.ts │ │ ├── jwt-auth.guard.ts │ │ └── jwt.strategy.ts │ ├── destinations │ │ ├── destinations.controller.spec.ts │ │ ├── destinations.controller.ts │ │ ├── destinations.module.ts │ │ ├── destinations.service.spec.ts │ │ ├── destinations.service.ts │ │ └── dto │ │ │ ├── create-destination.dto.ts │ │ │ └── update-destination.dto.ts │ ├── main.ts │ └── prisma │ │ ├── prisma.module.ts │ │ ├── prisma.service.spec.ts │ │ └── prisma.service.ts │ ├── test │ ├── app.e2e-spec.ts │ └── jest-e2e.json │ ├── tsconfig.build.json │ └── tsconfig.json └── postgres ├── postgres-with-prisma ├── .dockerignore ├── .gitignore ├── Dockerfile ├── docker-compose.yml ├── package-lock.json ├── package.json ├── prisma │ ├── migrations │ │ ├── 20250223203943_init │ │ │ └── migration.sql │ │ ├── 20250223205502_author │ │ │ └── migration.sql │ │ ├── 20250223214459_delete │ │ │ └── migration.sql │ │ └── migration_lock.toml │ └── schema.prisma ├── prometheus.yml └── src │ ├── controllers │ ├── authorController.js │ └── bookController.js │ ├── routes │ ├── authorRoutes.js │ └── bookRoutes.js │ ├── server.js │ └── services │ ├── authorService.js │ └── bookService.js └── postgress-concepts ├── package-lock.json ├── package.json └── src ├── concepts ├── aggregation.js ├── basic-queries.js ├── filtering-sorting.js ├── joins.js └── relationships.js ├── db └── db.js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/README.md: -------------------------------------------------------------------------------- 1 | To install dependencies: 2 | ```sh 3 | bun install 4 | ``` 5 | 6 | To run: 7 | ```sh 8 | bun run dev 9 | ``` 10 | 11 | open http://localhost:3000 12 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "bun-hono-task-crud-api", 6 | "dependencies": { 7 | "hono": "^4.7.2", 8 | }, 9 | "devDependencies": { 10 | "@types/bun": "latest", 11 | }, 12 | }, 13 | }, 14 | "packages": { 15 | "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], 16 | 17 | "@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="], 18 | 19 | "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], 20 | 21 | "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], 22 | 23 | "hono": ["hono@4.7.2", "", {}, "sha512-8V5XxoOF6SI12jkHkzX/6aLBMU5GEF5g387EjVSQipS0DlxWgWGSMeEayY3CRBjtTUQYwLHx9JYouWqKzy2Vng=="], 24 | 25 | "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/bun_hono_task_crud.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sangammukherjee/nodejs-full-course-part-3/d96ce13d2a46197a843d28fadd4ec6ec5aa6ff0c/bun-and-hono/bun-hono-task-crud-api/bun_hono_task_crud.sqlite -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bun-hono-task-crud-api", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "bun-hono-task-crud-api", 8 | "dependencies": { 9 | "@hono/zod-validator": "^0.4.3", 10 | "hono": "^4.7.2" 11 | }, 12 | "devDependencies": { 13 | "@types/bun": "latest" 14 | } 15 | }, 16 | "node_modules/@hono/zod-validator": { 17 | "version": "0.4.3", 18 | "resolved": "https://registry.npmjs.org/@hono/zod-validator/-/zod-validator-0.4.3.tgz", 19 | "integrity": "sha512-xIgMYXDyJ4Hj6ekm9T9Y27s080Nl9NXHcJkOvkXPhubOLj8hZkOL8pDnnXfvCf5xEE8Q4oMFenQUZZREUY2gqQ==", 20 | "peerDependencies": { 21 | "hono": ">=3.9.0", 22 | "zod": "^3.19.1" 23 | } 24 | }, 25 | "node_modules/@types/bun": { 26 | "version": "1.2.2", 27 | "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.2.2.tgz", 28 | "integrity": "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w==", 29 | "dev": true, 30 | "dependencies": { 31 | "bun-types": "1.2.2" 32 | } 33 | }, 34 | "node_modules/@types/node": { 35 | "version": "22.13.4", 36 | "dev": true, 37 | "license": "MIT", 38 | "dependencies": { 39 | "undici-types": "~6.20.0" 40 | } 41 | }, 42 | "node_modules/@types/ws": { 43 | "version": "8.5.14", 44 | "dev": true, 45 | "license": "MIT", 46 | "dependencies": { 47 | "@types/node": "*" 48 | } 49 | }, 50 | "node_modules/bun-types": { 51 | "version": "1.2.2", 52 | "dev": true, 53 | "license": "MIT", 54 | "dependencies": { 55 | "@types/node": "*", 56 | "@types/ws": "~8.5.10" 57 | } 58 | }, 59 | "node_modules/hono": { 60 | "version": "4.7.2", 61 | "license": "MIT", 62 | "engines": { 63 | "node": ">=16.9.0" 64 | } 65 | }, 66 | "node_modules/undici-types": { 67 | "version": "6.20.0", 68 | "dev": true, 69 | "license": "MIT" 70 | }, 71 | "node_modules/zod": { 72 | "version": "3.24.2", 73 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", 74 | "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", 75 | "peer": true, 76 | "funding": { 77 | "url": "https://github.com/sponsors/colinhacks" 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bun-hono-task-crud-api", 3 | "scripts": { 4 | "dev": "bun run --hot src/index.ts" 5 | }, 6 | "dependencies": { 7 | "@hono/zod-validator": "^0.4.3", 8 | "hono": "^4.7.2" 9 | }, 10 | "devDependencies": { 11 | "@types/bun": "latest" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/src/controllers/auth.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from "hono"; 2 | import { Database } from "bun:sqlite"; 3 | import { User } from "../types"; 4 | import { password as bunPassword } from "bun"; 5 | import { sign } from "hono/jwt"; 6 | 7 | export async function registerUser(c: Context, db: Database) { 8 | const { username, password, role = "user" } = await c.req.json(); 9 | 10 | if (!username || !password) { 11 | return c.json( 12 | { 13 | error: "Username and password are required", 14 | }, 15 | 400 16 | ); 17 | } 18 | 19 | if (role !== "user" && role !== "admin") { 20 | return c.json({ error: "Invalid role" }, 400); 21 | } 22 | 23 | try { 24 | const existingUser = db 25 | .query("SELECT * FROM users WHERE username = ?") 26 | .get(username) as User | undefined; 27 | 28 | if (existingUser) { 29 | return c.json( 30 | { 31 | error: 32 | "User is already exists with same username! Please try with a diff username", 33 | }, 34 | 400 35 | ); 36 | } 37 | 38 | //hashing the password 39 | const hashedPassword = await bunPassword.hash(password); 40 | 41 | db.run("INSERT INTO users (username, password, role) VALUES (?,?,?)", [ 42 | username, 43 | hashedPassword, 44 | role, 45 | ]); 46 | 47 | return c.json( 48 | { 49 | message: "User registered successfully!", 50 | }, 51 | 201 52 | ); 53 | } catch (e) { 54 | console.error(e); 55 | return c.json({ error: "Internal server error" }, 500); 56 | } 57 | } 58 | 59 | export async function loginUser(c: Context, db: Database) { 60 | const { username, password } = await c.req.json(); 61 | if (!username || !password) { 62 | return c.json( 63 | { 64 | error: "Username and password are required", 65 | }, 66 | 400 67 | ); 68 | } 69 | 70 | try { 71 | const user = db 72 | .query("SELECT * FROM users WHERE username = ?") 73 | .get(username) as User | undefined; 74 | if (!user) { 75 | return c.json( 76 | { 77 | error: "Invalid credentials", 78 | }, 79 | 401 80 | ); 81 | } 82 | 83 | //verify the password user entered 84 | const isPasswordValid = await bunPassword.verify(password, user.password); 85 | if (!isPasswordValid) { 86 | return c.json( 87 | { 88 | error: "Invalid password", 89 | }, 90 | 401 91 | ); 92 | } 93 | 94 | const token = await sign( 95 | { 96 | userId: user.id, 97 | role: user.role, 98 | }, 99 | process.env.JWT_SECRET || "JWT_SECRET" 100 | ); 101 | 102 | return c.json({ token }); 103 | } catch (error) { 104 | console.error(error); 105 | return c.json({ error: "Internal server error" }, 500); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/src/controllers/task.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from "hono"; 2 | import { Database } from "bun:sqlite"; 3 | import { Task } from "../types"; 4 | 5 | export async function createTask(c: Context, db: Database) { 6 | const userId = c.get("jwtPayload").userId; 7 | const userRole = c.get("jwtPayload").role; 8 | const { title, description, user_id } = await c.req.json(); 9 | 10 | if (!userId) { 11 | return c.json( 12 | { 13 | error: "Unauthenticated! You need to login to create task", 14 | }, 15 | 401 16 | ); 17 | } 18 | 19 | if (userRole !== "admin") { 20 | return c.json( 21 | { 22 | error: "Unauthorized! Only admin users can create a post", 23 | }, 24 | 403 25 | ); 26 | } 27 | 28 | if (userId !== user_id) { 29 | return c.json( 30 | { 31 | error: "Invalid user with diff user id", 32 | }, 33 | 403 34 | ); 35 | } 36 | 37 | try { 38 | const result = db 39 | .query( 40 | ` 41 | INSERT INTO tasks (user_id, title, description) VALUES (?,?,?) RETURNING * 42 | ` 43 | ) 44 | .get(user_id, title, description) as Task; 45 | 46 | return c.json(result, 201); 47 | } catch (error) { 48 | console.error(error); 49 | return c.json({ error: "Internal server error" }, 500); 50 | } 51 | } 52 | 53 | export async function getAllTasks(c: Context, db: Database) { 54 | try { 55 | const extractAllTasks = db.query("SELECT * FROM tasks").all() as Task[]; 56 | return c.json(extractAllTasks, 200); 57 | } catch (error) { 58 | console.error(error); 59 | return c.json({ error: "Internal server error" }, 500); 60 | } 61 | } 62 | 63 | export async function getTask(c: Context, db: Database) { 64 | const taskId = c.req.param("id"); 65 | 66 | try { 67 | const extractSingleTask = db 68 | .query("SELECT * FROM tasks WHERE id =?") 69 | .get(taskId) as Task | undefined; 70 | 71 | if (!extractSingleTask) { 72 | return c.json( 73 | { 74 | error: "Task not found! Please try again", 75 | }, 76 | 404 77 | ); 78 | } 79 | 80 | return c.json(extractSingleTask, 200); 81 | } catch (error) { 82 | console.error(error); 83 | return c.json({ error: "Internal server error" }, 500); 84 | } 85 | } 86 | 87 | export async function updateATask(c: Context, db: Database) { 88 | const userId = c.get("jwtPayload").userId; 89 | const userRole = c.get("jwtPayload").role; 90 | const taskId = c.req.param("id"); 91 | const { title, description, user_id } = await c.req.json(); 92 | 93 | if (!userId) { 94 | return c.json( 95 | { 96 | error: "Unauthenticated! You need to login to create task", 97 | }, 98 | 401 99 | ); 100 | } 101 | 102 | if (userRole !== "admin") { 103 | return c.json( 104 | { 105 | error: "Unauthorized! Only admin users can update a post", 106 | }, 107 | 403 108 | ); 109 | } 110 | 111 | if (userId !== user_id) { 112 | return c.json( 113 | { 114 | error: "You are not owner of this post, you can't update this!", 115 | }, 116 | 403 117 | ); 118 | } 119 | 120 | try { 121 | const extractSingleTask = db 122 | .query("SELECT * FROM tasks WHERE id= ?") 123 | .get(taskId) as Task | undefined; 124 | 125 | if (!extractSingleTask) { 126 | return c.json( 127 | { 128 | error: "Task not found! Please try again", 129 | }, 130 | 404 131 | ); 132 | } 133 | 134 | const updatedTask = db 135 | .query( 136 | ` 137 | UPDATE tasks 138 | SET title = ?, description = ?, user_id = ? 139 | WHERE id = ? 140 | RETURNING * 141 | ` 142 | ) 143 | .get( 144 | title || extractSingleTask.title, 145 | description !== undefined ? description : extractSingleTask.description, 146 | user_id || extractSingleTask.user_id, 147 | taskId 148 | ) as Task; 149 | 150 | return c.json(updatedTask, 200); 151 | } catch (error) { 152 | console.error(error); 153 | return c.json({ error: "Internal server error" }, 500); 154 | } 155 | } 156 | 157 | export async function deleteTask(c: Context, db: Database) { 158 | const userId = c.get("jwtPayload").userId; 159 | const userRole = c.get("jwtPayload").role; 160 | const taskId = c.req.param("id"); 161 | const { user_id } = await c.req.json(); 162 | 163 | if (!userId) { 164 | return c.json( 165 | { 166 | error: "Unauthenticated! You need to login to create task", 167 | }, 168 | 401 169 | ); 170 | } 171 | 172 | if (userRole !== "admin") { 173 | return c.json( 174 | { 175 | error: "Unauthorized! Only admin users can delete a post", 176 | }, 177 | 403 178 | ); 179 | } 180 | 181 | if (userId !== user_id) { 182 | return c.json( 183 | { 184 | error: "You are not owner of this post, you can't delete this!", 185 | }, 186 | 403 187 | ); 188 | } 189 | 190 | try { 191 | const deletedTask = db.query("DELETE FROM tasks WHERE id= ?").run(taskId); 192 | 193 | if (deletedTask.changes === 0) { 194 | return c.json( 195 | { 196 | error: "Task not found! Please try again", 197 | }, 198 | 404 199 | ); 200 | } 201 | 202 | return c.json({ message: "Task deleted successfully!" }); 203 | } catch (error) { 204 | console.error(error); 205 | return c.json({ error: "Internal server error" }, 500); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/src/database/db.ts: -------------------------------------------------------------------------------- 1 | import { Database } from "bun:sqlite"; 2 | 3 | export function initDatabase(): Database { 4 | const db = new Database("bun_hono_task_crud.sqlite"); 5 | 6 | //users table 7 | db.run(` 8 | CREATE TABLE IF NOT EXISTS users ( 9 | id INTEGER PRIMARY KEY AUTOINCREMENT, 10 | username TEXT UNIQUE NOT NULL, 11 | password TEXT NOT NULL, 12 | role TEXT CHECK(role IN ('user', 'admin')) NOT NULL DEFAULT 'user' 13 | ) 14 | `); 15 | 16 | //tasks table 17 | db.run(` 18 | CREATE TABLE IF NOT EXISTS tasks ( 19 | id INTEGER PRIMARY KEY AUTOINCREMENT, 20 | user_id INTEGER NOT NULL, 21 | title TEXT NOT NULL, 22 | description TEXT, 23 | created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 24 | FOREIGN KEY (user_id) REFERENCES users(id) 25 | 26 | ) 27 | `); 28 | 29 | return db; 30 | } 31 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | import { initDatabase } from "./database/db"; 3 | import { cors } from "hono/cors"; 4 | import { logger } from "hono/logger"; 5 | import { z } from "zod"; 6 | import { zValidator } from "@hono/zod-validator"; 7 | import { loginUser, registerUser } from "./controllers/auth"; 8 | import { jwt } from "hono/jwt"; 9 | import { 10 | createTask, 11 | deleteTask, 12 | getAllTasks, 13 | getTask, 14 | updateATask, 15 | } from "./controllers/task"; 16 | 17 | const app = new Hono(); 18 | const db = initDatabase(); 19 | 20 | app.use("*", cors()); 21 | app.use("*", logger()); 22 | 23 | const auth = jwt({ 24 | secret: process.env.JWT_SECRET || "JWT_SECRET", 25 | }); 26 | 27 | //Input validation 28 | const registerSchema = z.object({ 29 | username: z.string().min(3).max(25), 30 | password: z.string().min(5), 31 | role: z.enum(["user", "admin"]).optional(), 32 | }); 33 | 34 | const loginSchema = z.object({ 35 | username: z.string(), 36 | password: z.string(), 37 | }); 38 | 39 | const taskSchema = z.object({ 40 | title: z.string().min(1).max(100), 41 | description: z.string().optional(), 42 | user_id: z.number().int().positive(), 43 | }); 44 | 45 | //auth related routes 46 | app.post("/register-user", zValidator("json", registerSchema), (c) => 47 | registerUser(c, db) 48 | ); 49 | app.post("/login", zValidator("json", loginSchema), (c) => loginUser(c, db)); 50 | 51 | //task routes 52 | app.post("/tasks", auth, zValidator("json", taskSchema), (c) => 53 | createTask(c, db) 54 | ); 55 | app.get("/tasks", auth, (c) => getAllTasks(c, db)); 56 | app.get("/tasks/:id", auth, (c) => getTask(c, db)); 57 | app.put("/tasks/:id", auth, zValidator("json", taskSchema), (c) => 58 | updateATask(c, db) 59 | ); 60 | 61 | app.delete("/tasks/:id", auth, (c) => deleteTask(c, db)); 62 | 63 | app.get("/", (c) => { 64 | return c.text("Hello, User & Task management using Bun & Hono"); 65 | }); 66 | 67 | app.get("/db-test", (c) => { 68 | const result = db.query("SELECT sqlite_version()").get(); 69 | 70 | return c.json({ 71 | message: "Database connected successfully", 72 | sqlite_version: result, 73 | }); 74 | }); 75 | 76 | export default app; 77 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type User = { 2 | id: number; 3 | username: string; 4 | password: string; 5 | role: "user" | "admin"; 6 | }; 7 | 8 | export type Task = { 9 | id: number; 10 | user_id: number; 11 | title: string; 12 | description: string; 13 | created_at: string; 14 | }; 15 | -------------------------------------------------------------------------------- /bun-and-hono/bun-hono-task-crud-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "hono/jsx" 6 | } 7 | } -------------------------------------------------------------------------------- /bun-and-hono/concepts/.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/README.md: -------------------------------------------------------------------------------- 1 | # concepts 2 | 3 | To install dependencies: 4 | 5 | ```bash 6 | bun install 7 | ``` 8 | 9 | To run: 10 | 11 | ```bash 12 | bun run index.ts 13 | ``` 14 | 15 | This project was created using `bun init` in bun v1.2.2. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. 16 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "concepts", 6 | "devDependencies": { 7 | "@types/bun": "latest", 8 | }, 9 | "peerDependencies": { 10 | "typescript": "^5.0.0", 11 | }, 12 | }, 13 | }, 14 | "packages": { 15 | "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], 16 | 17 | "@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="], 18 | 19 | "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], 20 | 21 | "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], 22 | 23 | "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], 24 | 25 | "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/bundb.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sangammukherjee/nodejs-full-course-part-3/d96ce13d2a46197a843d28fadd4ec6ec5aa6ff0c/bun-and-hono/concepts/bundb.sqlite -------------------------------------------------------------------------------- /bun-and-hono/concepts/features/binary.ts: -------------------------------------------------------------------------------- 1 | function binaryDataOperations() { 2 | const buf = new ArrayBuffer(8); 3 | console.log("Arraybuffer size", buf.byteLength); 4 | 5 | const dv = new DataView(buf); 6 | dv.setUint8(0, 3); 7 | dv.setUint16(1, 513); 8 | 9 | console.log(dv.getUint8(0)); 10 | console.log(dv.getUint16(1)); 11 | 12 | const unint8Array = new Uint8Array([0, 1, 2, 3, 4]); 13 | console.log(unint8Array); 14 | 15 | const nodeBuffer = Buffer.from("Hello Bun Js"); 16 | console.log(nodeBuffer, nodeBuffer.toString()); 17 | 18 | const blob = new Blob(["Hello"], { type: "text/html" }); 19 | console.log(blob.size, blob.type); 20 | 21 | const encoder = new TextEncoder(); 22 | const encodedVal = encoder.encode("Hello Bun!"); 23 | console.log(encodedVal, "encodedVal"); 24 | 25 | const decoder = new TextDecoder(); 26 | console.log(decoder.decode(encodedVal)); 27 | } 28 | 29 | binaryDataOperations(); 30 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/features/fetch.ts: -------------------------------------------------------------------------------- 1 | async function fetchDemo() { 2 | const response = await fetch("https://dummyjson.com/products/1"); 3 | const data = await response.json(); 4 | console.log(data); 5 | } 6 | 7 | fetchDemo(); 8 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/features/fs.ts: -------------------------------------------------------------------------------- 1 | import type { BunFile } from "bun"; 2 | 3 | async function fileSystemOperations() { 4 | //Read a file 5 | const file: BunFile = Bun.file("read.txt"); 6 | console.log(file.size); 7 | console.log(file.type, "type"); 8 | const extractTextContent = await file.text(); 9 | console.log(extractTextContent); 10 | const arrayBuffer = await file.arrayBuffer(); 11 | const unint8Array = await file.bytes(); 12 | 13 | console.log(arrayBuffer, unint8Array); 14 | 15 | const content = "Hello! I am learning Bun for the first time"; 16 | await Bun.write("output.txt", content); 17 | console.log("file created successfully"); 18 | 19 | const inputFile = Bun.file("read.txt"); 20 | await Bun.write("read_copy.txt", inputFile); 21 | console.log("file copied and created successfully"); 22 | 23 | const isFileExists = await Bun.file("read_copy.txt").exists(); 24 | console.log(isFileExists); 25 | 26 | await Bun.file("read_copy.txt").delete(); 27 | console.log("Read copy file is deleted successfully!!"); 28 | } 29 | 30 | fileSystemOperations(); 31 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/features/hashing.ts: -------------------------------------------------------------------------------- 1 | async function hashingOperations() { 2 | const password = "123456"; 3 | const hash = await Bun.password.hash(password); 4 | console.log(hash); 5 | 6 | const isMatch = await Bun.password.verify(password, hash); 7 | 8 | console.log(isMatch); 9 | 10 | const argonHashExample = await Bun.password.hash(password, { 11 | algorithm: "argon2id", 12 | memoryCost: 4, 13 | timeCost: 3, 14 | }); 15 | 16 | console.log(argonHashExample); 17 | 18 | const bcryptHashExample = await Bun.password.hash(password, { 19 | algorithm: "bcrypt", 20 | cost: 10, 21 | }); 22 | 23 | console.log(bcryptHashExample); 24 | } 25 | 26 | hashingOperations(); 27 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/features/meta-env.ts: -------------------------------------------------------------------------------- 1 | function importMetaAndEnv() { 2 | console.log(import.meta.url); 3 | 4 | console.log("Is this main module ?", import.meta.main); 5 | console.log(process.env.NODE_ENV); 6 | console.log(Bun.env.NODE_ENV); 7 | } 8 | 9 | importMetaAndEnv(); 10 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/features/server.ts: -------------------------------------------------------------------------------- 1 | import type { Server } from "bun"; 2 | 3 | interface User { 4 | id: number; 5 | name: string; 6 | } 7 | 8 | interface ApiResponse { 9 | message: string; 10 | method: string; 11 | route: string; 12 | data?: User | User[]; 13 | } 14 | 15 | const users: User[] = [ 16 | { 17 | id: 1, 18 | name: "John", 19 | }, 20 | { 21 | id: 2, 22 | name: "raj", 23 | }, 24 | { 25 | id: 3, 26 | name: "Sangam", 27 | }, 28 | { 29 | id: 4, 30 | name: "John4", 31 | }, 32 | { 33 | id: 5, 34 | name: "John5", 35 | }, 36 | ]; 37 | 38 | const server: Server = Bun.serve({ 39 | port: 3000, 40 | fetch(req: Request): Response { 41 | const url = new URL(req.url); 42 | const method = req.method; 43 | 44 | let response: ApiResponse = { 45 | message: "Hello from Bun server!", 46 | method: method, 47 | route: url.pathname, 48 | }; 49 | 50 | if (url.pathname === "/") { 51 | //root route 52 | if (method === "GET") { 53 | response.message = "Welcome to Bun API!"; 54 | } else { 55 | response.message = "method not allowed for this route"; 56 | } 57 | } else if (url.pathname === "/users") { 58 | switch (method) { 59 | case "GET": 60 | response.message = "fetching all users"; 61 | response.data = users; 62 | 63 | break; 64 | 65 | case "POST": 66 | response.message = "Creating a new user"; 67 | 68 | break; 69 | 70 | default: 71 | response.message = "method not allowed for this route"; 72 | break; 73 | } 74 | } 75 | 76 | return Response.json(response); 77 | }, 78 | }); 79 | 80 | console.log(`Bun server is running on http://localhost:${server.port}`); 81 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/features/sqllite.ts: -------------------------------------------------------------------------------- 1 | import { Database } from "bun:sqlite"; 2 | 3 | async function sqliteDemo() { 4 | const db = new Database("bundb.sqlite"); 5 | 6 | //create a table 7 | db.run(` 8 | CREATE TABLE IF NOT EXISTS users ( 9 | id INTEGER PRIMARY KEY AUTOINCREMENT, 10 | name TEXT NOT NULL, 11 | email TEXT UNIQUE NOT NULL, 12 | created_at DATETIME DEFAULT CURRENT_TIMESTAMP 13 | ) 14 | `); 15 | 16 | console.log("Table users created successfully!"); 17 | 18 | const insertUser = db.prepare( 19 | `INSERT INTO users (name, email) VALUES (?, ?)` 20 | ); // SQL Injection 21 | // insertUser.run("Sangam Mukherjee", "sangam@gmail.com"); 22 | // insertUser.run("John Doe", "johndoe@gmail.com"); 23 | // insertUser.run("James Corden", "james12345@gmail.com"); 24 | 25 | // const extractAllUsers = db.query("SELECT * FROM users").all(); 26 | // console.log(extractAllUsers); 27 | 28 | // db.run("UPDATE users SET name = ? WHERE email = ?", [ 29 | // "Raj Mukherjee", 30 | // "sangam@gmail.com", 31 | // ]); 32 | 33 | // const getUpdatedUserInfo = db 34 | // .query("SELECT * FROM users WHERE email = ?") 35 | // .get("sangam@gmail.com"); 36 | // console.log(getUpdatedUserInfo); 37 | 38 | db.run("DELETE FROM users WHERE email = ?", ["james12345@gmail.com"]); 39 | const extractRemainingUsers = db.query("SELECT * FROM users").all(); 40 | 41 | console.log("extractRemainingUsers", extractRemainingUsers); 42 | } 43 | 44 | sqliteDemo(); 45 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/index.ts: -------------------------------------------------------------------------------- 1 | // console.log("Hello via Bun!"); 2 | // console.log(3 + 4); 3 | 4 | // import "./features/fs"; 5 | // import "./features/binary"; 6 | // import "./features/meta-env"; 7 | // console.log("Is this main module ?", import.meta.main); 8 | // import "./features/fetch"; 9 | // import "./features/server"; 10 | // import "./features/hashing"; 11 | import "./features/sqllite"; 12 | -------------------------------------------------------------------------------- /bun-and-hono/concepts/output.txt: -------------------------------------------------------------------------------- 1 | Hello! I am learning Bun for the first time -------------------------------------------------------------------------------- /bun-and-hono/concepts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "concepts", 3 | "module": "index.ts", 4 | "type": "module", 5 | "devDependencies": { 6 | "@types/bun": "latest" 7 | }, 8 | "peerDependencies": { 9 | "typescript": "^5.0.0" 10 | } 11 | } -------------------------------------------------------------------------------- /bun-and-hono/concepts/read.txt: -------------------------------------------------------------------------------- 1 | Read the content from this file -------------------------------------------------------------------------------- /bun-and-hono/concepts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable latest features 4 | "lib": ["ESNext", "DOM"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | // Some stricter flags (disabled by default) 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": false, 25 | "noPropertyAccessFromIndexSignature": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /nest-js/concepts/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | /build 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | pnpm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | # OS 16 | .DS_Store 17 | 18 | # Tests 19 | /coverage 20 | /.nyc_output 21 | 22 | # IDEs and editors 23 | /.idea 24 | .project 25 | .classpath 26 | .c9/ 27 | *.launch 28 | .settings/ 29 | *.sublime-workspace 30 | 31 | # IDE - VSCode 32 | .vscode/* 33 | !.vscode/settings.json 34 | !.vscode/tasks.json 35 | !.vscode/launch.json 36 | !.vscode/extensions.json 37 | 38 | # dotenv environment variable files 39 | .env 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | .env.local 44 | 45 | # temp directory 46 | .temp 47 | .tmp 48 | 49 | # Runtime data 50 | pids 51 | *.pid 52 | *.seed 53 | *.pid.lock 54 | 55 | # Diagnostic reports (https://nodejs.org/api/report.html) 56 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 57 | -------------------------------------------------------------------------------- /nest-js/concepts/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /nest-js/concepts/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | Donate us 19 | Support us 20 | Follow us on Twitter 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Project setup 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Compile and run the project 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Run tests 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Deployment 62 | 63 | When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. 64 | 65 | If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: 66 | 67 | ```bash 68 | $ npm install -g mau 69 | $ mau deploy 70 | ``` 71 | 72 | With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. 73 | 74 | ## Resources 75 | 76 | Check out a few resources that may come in handy when working with NestJS: 77 | 78 | - Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. 79 | - For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). 80 | - To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). 81 | - Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. 82 | - Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). 83 | - Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). 84 | - To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). 85 | - Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). 86 | 87 | ## Support 88 | 89 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). 90 | 91 | ## Stay in touch 92 | 93 | - Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) 94 | - Website - [https://nestjs.com](https://nestjs.com/) 95 | - Twitter - [@nestframework](https://twitter.com/nestframework) 96 | 97 | ## License 98 | 99 | Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). 100 | -------------------------------------------------------------------------------- /nest-js/concepts/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import eslint from '@eslint/js'; 3 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 4 | import globals from 'globals'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { 9 | ignores: ['eslint.config.mjs'], 10 | }, 11 | eslint.configs.recommended, 12 | ...tseslint.configs.recommendedTypeChecked, 13 | eslintPluginPrettierRecommended, 14 | { 15 | languageOptions: { 16 | globals: { 17 | ...globals.node, 18 | ...globals.jest, 19 | }, 20 | ecmaVersion: 5, 21 | sourceType: 'module', 22 | parserOptions: { 23 | projectService: true, 24 | tsconfigRootDir: import.meta.dirname, 25 | }, 26 | }, 27 | }, 28 | { 29 | rules: { 30 | '@typescript-eslint/no-explicit-any': 'off', 31 | '@typescript-eslint/no-floating-promises': 'warn', 32 | '@typescript-eslint/no-unsafe-argument': 'warn' 33 | }, 34 | }, 35 | ); -------------------------------------------------------------------------------- /nest-js/concepts/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /nest-js/concepts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "concepts", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@nestjs/common": "^11.0.1", 24 | "@nestjs/core": "^11.0.1", 25 | "@nestjs/platform-express": "^11.0.1", 26 | "reflect-metadata": "^0.2.2", 27 | "rxjs": "^7.8.1" 28 | }, 29 | "devDependencies": { 30 | "@eslint/eslintrc": "^3.2.0", 31 | "@eslint/js": "^9.18.0", 32 | "@nestjs/cli": "^11.0.0", 33 | "@nestjs/schematics": "^11.0.0", 34 | "@nestjs/testing": "^11.0.1", 35 | "@swc/cli": "^0.6.0", 36 | "@swc/core": "^1.10.7", 37 | "@types/express": "^5.0.0", 38 | "@types/jest": "^29.5.14", 39 | "@types/node": "^22.10.7", 40 | "@types/supertest": "^6.0.2", 41 | "eslint": "^9.18.0", 42 | "eslint-config-prettier": "^10.0.1", 43 | "eslint-plugin-prettier": "^5.2.2", 44 | "globals": "^15.14.0", 45 | "jest": "^29.7.0", 46 | "prettier": "^3.4.2", 47 | "source-map-support": "^0.5.21", 48 | "supertest": "^7.0.0", 49 | "ts-jest": "^29.2.5", 50 | "ts-loader": "^9.5.2", 51 | "ts-node": "^10.9.2", 52 | "tsconfig-paths": "^4.2.0", 53 | "typescript": "^5.7.3", 54 | "typescript-eslint": "^8.20.0" 55 | }, 56 | "jest": { 57 | "moduleFileExtensions": [ 58 | "js", 59 | "json", 60 | "ts" 61 | ], 62 | "rootDir": "src", 63 | "testRegex": ".*\\.spec\\.ts$", 64 | "transform": { 65 | "^.+\\.(t|j)s$": "ts-jest" 66 | }, 67 | "collectCoverageFrom": [ 68 | "**/*.(t|j)s" 69 | ], 70 | "coverageDirectory": "../coverage", 71 | "testEnvironment": "node" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /nest-js/concepts/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /nest-js/concepts/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | //AppService is injected through the constructor 7 | //constructor based dependency injection 8 | constructor(private readonly appService: AppService) {} 9 | 10 | @Get() 11 | getHello(): string { 12 | //using the injected service to get the gethello method 13 | return this.appService.getHello(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /nest-js/concepts/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { BlogModule } from './blog/blog.module'; 5 | 6 | // -> Root module which imports all other modules 7 | 8 | //Modules -> group related providers and controllers together 9 | 10 | @Module({ 11 | imports: [BlogModule], 12 | controllers: [AppController], 13 | providers: [AppService], 14 | }) 15 | export class AppModule {} 16 | -------------------------------------------------------------------------------- /nest-js/concepts/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | //decorator 4 | //business logics of your application 5 | @Injectable() 6 | export class AppService { 7 | getHello(): string { 8 | return 'Hello Nest JS!'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /nest-js/concepts/src/blog/blog.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { BlogController } from './blog.controller'; 3 | 4 | describe('BlogController', () => { 5 | let controller: BlogController; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | controllers: [BlogController], 10 | }).compile(); 11 | 12 | controller = module.get(BlogController); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(controller).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/concepts/src/blog/blog.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Param } from '@nestjs/common'; 2 | import { BlogService } from './blog.service'; 3 | 4 | @Controller('blog') 5 | export class BlogController { 6 | constructor(private readonly blogService: BlogService) {} 7 | @Get() 8 | findAll() { 9 | return this.blogService.findAll(); 10 | } 11 | 12 | @Get(':id') 13 | findById(@Param('id') id: string) { 14 | return this.blogService.findById(+id); 15 | } 16 | 17 | @Get('by-unique-key/:key') 18 | findByBlogUniqueKey(@Param('key') key: string) { 19 | return this.blogService.findByUniqueKey(key); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /nest-js/concepts/src/blog/blog.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { BlogController } from './blog.controller'; 3 | import { BlogService } from './blog.service'; 4 | 5 | @Module({ 6 | controllers: [BlogController], 7 | providers: [BlogService] 8 | }) 9 | export class BlogModule {} 10 | -------------------------------------------------------------------------------- /nest-js/concepts/src/blog/blog.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { BlogService } from './blog.service'; 3 | 4 | describe('BlogService', () => { 5 | let service: BlogService; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | providers: [BlogService], 10 | }).compile(); 11 | 12 | service = module.get(BlogService); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/concepts/src/blog/blog.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class BlogService { 5 | private readonly blogs = [ 6 | { 7 | id: 1, 8 | blogTitle: 'Blog 1', 9 | blogUniqueKey: 'blogUniqueKey1', 10 | }, 11 | { 12 | id: 2, 13 | blogTitle: 'Blog 2', 14 | blogUniqueKey: 'blogUniqueKey2', 15 | }, 16 | { 17 | id: 3, 18 | blogTitle: 'Blog 3', 19 | blogUniqueKey: 'blogUniqueKey3', 20 | }, 21 | { 22 | id: 4, 23 | blogTitle: 'Blog 4', 24 | blogUniqueKey: 'blogUniqueKey4', 25 | }, 26 | { 27 | id: 5, 28 | blogTitle: 'Blog 5', 29 | blogUniqueKey: 'blogUniqueKey5', 30 | }, 31 | ]; 32 | 33 | findAll() { 34 | return this.blogs; 35 | } 36 | 37 | findById(id: number) { 38 | return this.blogs.find((blog) => blog.id === id); 39 | } 40 | 41 | findByUniqueKey(key: string) { 42 | return this.blogs.find((blog) => blog.blogUniqueKey === key); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /nest-js/concepts/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | //create a nest js application instance 6 | const app = await NestFactory.create(AppModule); 7 | 8 | //start the application anf listen to this port 9 | await app.listen(process.env.PORT ?? 3000); 10 | } 11 | bootstrap(); 12 | -------------------------------------------------------------------------------- /nest-js/concepts/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { App } from 'supertest/types'; 5 | import { AppModule } from './../src/app.module'; 6 | 7 | describe('AppController (e2e)', () => { 8 | let app: INestApplication; 9 | 10 | beforeEach(async () => { 11 | const moduleFixture: TestingModule = await Test.createTestingModule({ 12 | imports: [AppModule], 13 | }).compile(); 14 | 15 | app = moduleFixture.createNestApplication(); 16 | await app.init(); 17 | }); 18 | 19 | it('/ (GET)', () => { 20 | return request(app.getHttpServer()) 21 | .get('/') 22 | .expect(200) 23 | .expect('Hello World!'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /nest-js/concepts/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /nest-js/concepts/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /nest-js/concepts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2023", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "noImplicitAny": false, 18 | "strictBindCallApply": false, 19 | "noFallthroughCasesInSwitch": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /nest-js/file-upload/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | /build 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | pnpm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | # OS 16 | .DS_Store 17 | 18 | # Tests 19 | /coverage 20 | /.nyc_output 21 | 22 | # IDEs and editors 23 | /.idea 24 | .project 25 | .classpath 26 | .c9/ 27 | *.launch 28 | .settings/ 29 | *.sublime-workspace 30 | 31 | # IDE - VSCode 32 | .vscode/* 33 | !.vscode/settings.json 34 | !.vscode/tasks.json 35 | !.vscode/launch.json 36 | !.vscode/extensions.json 37 | 38 | # dotenv environment variable files 39 | .env 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | .env.local 44 | 45 | # temp directory 46 | .temp 47 | .tmp 48 | 49 | # Runtime data 50 | pids 51 | *.pid 52 | *.seed 53 | *.pid.lock 54 | 55 | # Diagnostic reports (https://nodejs.org/api/report.html) 56 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 57 | -------------------------------------------------------------------------------- /nest-js/file-upload/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /nest-js/file-upload/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | Donate us 19 | Support us 20 | Follow us on Twitter 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Project setup 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Compile and run the project 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Run tests 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Deployment 62 | 63 | When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. 64 | 65 | If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: 66 | 67 | ```bash 68 | $ npm install -g mau 69 | $ mau deploy 70 | ``` 71 | 72 | With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. 73 | 74 | ## Resources 75 | 76 | Check out a few resources that may come in handy when working with NestJS: 77 | 78 | - Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. 79 | - For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). 80 | - To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). 81 | - Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. 82 | - Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). 83 | - Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). 84 | - To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). 85 | - Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). 86 | 87 | ## Support 88 | 89 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). 90 | 91 | ## Stay in touch 92 | 93 | - Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) 94 | - Website - [https://nestjs.com](https://nestjs.com/) 95 | - Twitter - [@nestframework](https://twitter.com/nestframework) 96 | 97 | ## License 98 | 99 | Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). 100 | -------------------------------------------------------------------------------- /nest-js/file-upload/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import eslint from '@eslint/js'; 3 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 4 | import globals from 'globals'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { 9 | ignores: ['eslint.config.mjs'], 10 | }, 11 | eslint.configs.recommended, 12 | ...tseslint.configs.recommendedTypeChecked, 13 | eslintPluginPrettierRecommended, 14 | { 15 | languageOptions: { 16 | globals: { 17 | ...globals.node, 18 | ...globals.jest, 19 | }, 20 | ecmaVersion: 5, 21 | sourceType: 'module', 22 | parserOptions: { 23 | projectService: true, 24 | tsconfigRootDir: import.meta.dirname, 25 | }, 26 | }, 27 | }, 28 | { 29 | rules: { 30 | '@typescript-eslint/no-explicit-any': 'off', 31 | '@typescript-eslint/no-floating-promises': 'warn', 32 | '@typescript-eslint/no-unsafe-argument': 'warn' 33 | }, 34 | }, 35 | ); -------------------------------------------------------------------------------- /nest-js/file-upload/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /nest-js/file-upload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file-upload", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@nestjs/common": "^11.0.1", 24 | "@nestjs/config": "^4.0.0", 25 | "@nestjs/core": "^11.0.1", 26 | "@nestjs/platform-express": "^11.0.11", 27 | "@prisma/client": "^6.4.1", 28 | "@types/multer": "^1.4.12", 29 | "cloudinary": "^2.5.1", 30 | "multer": "^1.4.5-lts.1", 31 | "prisma": "^6.4.1", 32 | "reflect-metadata": "^0.2.2", 33 | "rxjs": "^7.8.1" 34 | }, 35 | "devDependencies": { 36 | "@eslint/eslintrc": "^3.2.0", 37 | "@eslint/js": "^9.18.0", 38 | "@nestjs/cli": "^11.0.0", 39 | "@nestjs/schematics": "^11.0.0", 40 | "@nestjs/testing": "^11.0.1", 41 | "@swc/cli": "^0.6.0", 42 | "@swc/core": "^1.10.7", 43 | "@types/express": "^5.0.0", 44 | "@types/jest": "^29.5.14", 45 | "@types/node": "^22.10.7", 46 | "@types/supertest": "^6.0.2", 47 | "eslint": "^9.18.0", 48 | "eslint-config-prettier": "^10.0.1", 49 | "eslint-plugin-prettier": "^5.2.2", 50 | "globals": "^15.14.0", 51 | "jest": "^29.7.0", 52 | "prettier": "^3.4.2", 53 | "source-map-support": "^0.5.21", 54 | "supertest": "^7.0.0", 55 | "ts-jest": "^29.2.5", 56 | "ts-loader": "^9.5.2", 57 | "ts-node": "^10.9.2", 58 | "tsconfig-paths": "^4.2.0", 59 | "typescript": "^5.7.3", 60 | "typescript-eslint": "^8.20.0" 61 | }, 62 | "jest": { 63 | "moduleFileExtensions": [ 64 | "js", 65 | "json", 66 | "ts" 67 | ], 68 | "rootDir": "src", 69 | "testRegex": ".*\\.spec\\.ts$", 70 | "transform": { 71 | "^.+\\.(t|j)s$": "ts-jest" 72 | }, 73 | "collectCoverageFrom": [ 74 | "**/*.(t|j)s" 75 | ], 76 | "coverageDirectory": "../coverage", 77 | "testEnvironment": "node" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /nest-js/file-upload/prisma/migrations/20250302190131_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "File" ( 3 | "id" TEXT NOT NULL, 4 | "filename" TEXT NOT NULL, 5 | "publicId" TEXT NOT NULL, 6 | "url" TEXT NOT NULL, 7 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 8 | "updatedAt" TIMESTAMP(3) NOT NULL, 9 | 10 | CONSTRAINT "File_pkey" PRIMARY KEY ("id") 11 | ); 12 | 13 | -- CreateIndex 14 | CREATE UNIQUE INDEX "File_publicId_key" ON "File"("publicId"); 15 | -------------------------------------------------------------------------------- /nest-js/file-upload/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /nest-js/file-upload/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? 5 | // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init 6 | 7 | generator client { 8 | provider = "prisma-client-js" 9 | } 10 | 11 | datasource db { 12 | provider = "postgresql" 13 | url = env("DATABASE_URL") 14 | } 15 | 16 | model File { 17 | id String @id @default(uuid()) 18 | filename String 19 | publicId String @unique 20 | url String 21 | createdAt DateTime @default(now()) 22 | updatedAt DateTime @updatedAt 23 | } 24 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { ConfigModule } from '@nestjs/config'; 5 | import { FileUploadModule } from './file-upload/file-upload.module'; 6 | 7 | @Module({ 8 | imports: [ 9 | ConfigModule.forRoot({ 10 | isGlobal: true, 11 | }), 12 | FileUploadModule, 13 | ], 14 | controllers: [AppController], 15 | providers: [AppService], 16 | }) 17 | export class AppModule {} 18 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/file-upload/file-upload.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { FileUploadController } from './file-upload.controller'; 3 | 4 | describe('FileUploadController', () => { 5 | let controller: FileUploadController; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | controllers: [FileUploadController], 10 | }).compile(); 11 | 12 | controller = module.get(FileUploadController); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(controller).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/file-upload/file-upload.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Delete, 4 | Param, 5 | Post, 6 | UploadedFile, 7 | UseInterceptors, 8 | } from '@nestjs/common'; 9 | import { FileUploadService } from './file-upload.service'; 10 | import { FileInterceptor } from '@nestjs/platform-express'; 11 | import { diskStorage } from 'multer'; 12 | import { extname } from 'path'; 13 | 14 | @Controller('file-upload') 15 | export class FileUploadController { 16 | constructor(private readonly fileUploaderService: FileUploadService) {} 17 | 18 | @Post() 19 | @UseInterceptors( 20 | FileInterceptor('file', { 21 | storage: diskStorage({ 22 | destination: './uploads', 23 | filename: (req, file, callback) => { 24 | const uniqueSuffix = 25 | Date.now() + '-' + Math.round(Math.random() * 1e9); 26 | const ext = extname(file.originalname); 27 | const filename = `${uniqueSuffix}${ext}`; 28 | callback(null, filename); 29 | }, 30 | }), 31 | }), 32 | ) 33 | async uploadFile(@UploadedFile() file: Express.Multer.File) { 34 | return this.fileUploaderService.uploadFile(file); 35 | } 36 | 37 | @Delete(':id') 38 | async deleteFile(@Param('id') id: string) { 39 | return this.fileUploaderService.deleteFile(id); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/file-upload/file-upload.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { FileUploadController } from './file-upload.controller'; 3 | import { FileUploadService } from './file-upload.service'; 4 | import { PrismaService } from 'src/prisma/prisma.service'; 5 | 6 | @Module({ 7 | controllers: [FileUploadController], 8 | providers: [FileUploadService, PrismaService], 9 | }) 10 | export class FileUploadModule {} 11 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/file-upload/file-upload.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { FileUploadService } from './file-upload.service'; 3 | 4 | describe('FileUploadService', () => { 5 | let service: FileUploadService; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | providers: [FileUploadService], 10 | }).compile(); 11 | 12 | service = module.get(FileUploadService); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/file-upload/file-upload.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, InternalServerErrorException } from '@nestjs/common'; 2 | import { PrismaService } from 'src/prisma/prisma.service'; 3 | import { v2 as cloudinary } from 'cloudinary'; 4 | import * as fs from 'fs'; 5 | 6 | @Injectable() 7 | export class FileUploadService { 8 | constructor(private prisma: PrismaService) { 9 | cloudinary.config({ 10 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 11 | api_key: process.env.CLOUDINARY_API_KEY, 12 | api_secret: process.env.CLOUDINARY_API_SECRET, 13 | }); 14 | } 15 | 16 | async uploadFile(file: Express.Multer.File) { 17 | try { 18 | const uploadResult = await this.uploadToCloudinary(file.path); 19 | 20 | const newlySavedFile = await this.prisma.file.create({ 21 | data: { 22 | filename: file.originalname, 23 | publicId: uploadResult.public_id, 24 | url: uploadResult.secure_url, 25 | }, 26 | }); 27 | 28 | fs.unlinkSync(file.path); 29 | 30 | return newlySavedFile; 31 | } catch (error) { 32 | //removing in case of any error -> this file from local folder 33 | if (file.path && fs.existsSync(file.path)) { 34 | fs.unlinkSync(file.path); 35 | } 36 | 37 | throw new InternalServerErrorException( 38 | 'File upload failed! Please try again after some time', 39 | ); 40 | } 41 | } 42 | 43 | private uploadToCloudinary(filePath: string): Promise { 44 | return new Promise((resolve, reject) => { 45 | cloudinary.uploader.upload(filePath, (error, result) => { 46 | if (error) reject(error); 47 | resolve(result); 48 | }); 49 | }); 50 | } 51 | 52 | async deleteFile(fileId: string) { 53 | try { 54 | const file = await this.prisma.file.findUnique({ 55 | where: { 56 | id: fileId, 57 | }, 58 | }); 59 | 60 | if (!file) { 61 | throw new Error('File not found! Please try with a different file ID'); 62 | } 63 | 64 | await cloudinary.uploader.destroy(file.publicId); 65 | 66 | await this.prisma.file.delete({ 67 | where: { id: fileId }, 68 | }); 69 | 70 | return { 71 | message: 'File deleted successfully', 72 | }; 73 | } catch (e) { 74 | throw new InternalServerErrorException( 75 | 'File deletion failed! Please try again after some time', 76 | ); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import * as path from 'path'; 4 | import * as fs from 'fs'; 5 | 6 | async function bootstrap() { 7 | const uploadDirectory = path.join(__dirname, '..', 'uploads'); 8 | 9 | if (!fs.existsSync(uploadDirectory)) { 10 | fs.mkdirSync(uploadDirectory); 11 | } 12 | 13 | const app = await NestFactory.create(AppModule); 14 | await app.listen(process.env.PORT ?? 3000); 15 | } 16 | bootstrap(); 17 | -------------------------------------------------------------------------------- /nest-js/file-upload/src/prisma/prisma.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; 2 | import { PrismaClient } from '@prisma/client'; 3 | 4 | @Injectable() 5 | export class PrismaService 6 | extends PrismaClient 7 | implements OnModuleInit, OnModuleDestroy 8 | { 9 | async onModuleInit() { 10 | await this.$connect(); 11 | } 12 | 13 | async onModuleDestroy() { 14 | await this.$disconnect(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /nest-js/file-upload/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { App } from 'supertest/types'; 5 | import { AppModule } from './../src/app.module'; 6 | 7 | describe('AppController (e2e)', () => { 8 | let app: INestApplication; 9 | 10 | beforeEach(async () => { 11 | const moduleFixture: TestingModule = await Test.createTestingModule({ 12 | imports: [AppModule], 13 | }).compile(); 14 | 15 | app = moduleFixture.createNestApplication(); 16 | await app.init(); 17 | }); 18 | 19 | it('/ (GET)', () => { 20 | return request(app.getHttpServer()) 21 | .get('/') 22 | .expect(200) 23 | .expect('Hello World!'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /nest-js/file-upload/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /nest-js/file-upload/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /nest-js/file-upload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2023", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "noImplicitAny": false, 18 | "strictBindCallApply": false, 19 | "noFallthroughCasesInSwitch": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | /build 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | pnpm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | # OS 16 | .DS_Store 17 | 18 | # Tests 19 | /coverage 20 | /.nyc_output 21 | 22 | # IDEs and editors 23 | /.idea 24 | .project 25 | .classpath 26 | .c9/ 27 | *.launch 28 | .settings/ 29 | *.sublime-workspace 30 | 31 | # IDE - VSCode 32 | .vscode/* 33 | !.vscode/settings.json 34 | !.vscode/tasks.json 35 | !.vscode/launch.json 36 | !.vscode/extensions.json 37 | 38 | # dotenv environment variable files 39 | .env 40 | .env.development.local 41 | .env.test.local 42 | .env.production.local 43 | .env.local 44 | 45 | # temp directory 46 | .temp 47 | .tmp 48 | 49 | # Runtime data 50 | pids 51 | *.pid 52 | *.seed 53 | *.pid.lock 54 | 55 | # Diagnostic reports (https://nodejs.org/api/report.html) 56 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 57 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /nest-js/travel-tracker/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | Donate us 19 | Support us 20 | Follow us on Twitter 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Project setup 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Compile and run the project 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Run tests 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Deployment 62 | 63 | When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. 64 | 65 | If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: 66 | 67 | ```bash 68 | $ npm install -g mau 69 | $ mau deploy 70 | ``` 71 | 72 | With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. 73 | 74 | ## Resources 75 | 76 | Check out a few resources that may come in handy when working with NestJS: 77 | 78 | - Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. 79 | - For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). 80 | - To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). 81 | - Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. 82 | - Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). 83 | - Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). 84 | - To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). 85 | - Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). 86 | 87 | ## Support 88 | 89 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). 90 | 91 | ## Stay in touch 92 | 93 | - Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) 94 | - Website - [https://nestjs.com](https://nestjs.com/) 95 | - Twitter - [@nestframework](https://twitter.com/nestframework) 96 | 97 | ## License 98 | 99 | Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). 100 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import eslint from '@eslint/js'; 3 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; 4 | import globals from 'globals'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { 9 | ignores: ['eslint.config.mjs'], 10 | }, 11 | eslint.configs.recommended, 12 | ...tseslint.configs.recommendedTypeChecked, 13 | eslintPluginPrettierRecommended, 14 | { 15 | languageOptions: { 16 | globals: { 17 | ...globals.node, 18 | ...globals.jest, 19 | }, 20 | ecmaVersion: 5, 21 | sourceType: 'module', 22 | parserOptions: { 23 | projectService: true, 24 | tsconfigRootDir: import.meta.dirname, 25 | }, 26 | }, 27 | }, 28 | { 29 | rules: { 30 | '@typescript-eslint/no-explicit-any': 'off', 31 | '@typescript-eslint/no-floating-promises': 'warn', 32 | '@typescript-eslint/no-unsafe-argument': 'warn' 33 | }, 34 | }, 35 | ); -------------------------------------------------------------------------------- /nest-js/travel-tracker/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "travel-tracker", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@nestjs/common": "^11.0.1", 24 | "@nestjs/config": "^4.0.0", 25 | "@nestjs/core": "^11.0.1", 26 | "@nestjs/jwt": "^11.0.0", 27 | "@nestjs/mapped-types": "^2.1.0", 28 | "@nestjs/passport": "^11.0.5", 29 | "@nestjs/platform-express": "^11.0.1", 30 | "@prisma/client": "^6.4.1", 31 | "bcrypt": "^5.1.1", 32 | "class-transformer": "^0.5.1", 33 | "class-validator": "^0.14.1", 34 | "passport-jwt": "^4.0.1", 35 | "prisma": "^6.4.1", 36 | "reflect-metadata": "^0.2.2", 37 | "rxjs": "^7.8.1" 38 | }, 39 | "devDependencies": { 40 | "@eslint/eslintrc": "^3.2.0", 41 | "@eslint/js": "^9.18.0", 42 | "@nestjs/cli": "^11.0.0", 43 | "@nestjs/schematics": "^11.0.0", 44 | "@nestjs/testing": "^11.0.1", 45 | "@swc/cli": "^0.6.0", 46 | "@swc/core": "^1.10.7", 47 | "@types/bcrypt": "^5.0.2", 48 | "@types/express": "^5.0.0", 49 | "@types/jest": "^29.5.14", 50 | "@types/node": "^22.10.7", 51 | "@types/passport-jwt": "^4.0.1", 52 | "@types/supertest": "^6.0.2", 53 | "eslint": "^9.18.0", 54 | "eslint-config-prettier": "^10.0.1", 55 | "eslint-plugin-prettier": "^5.2.2", 56 | "globals": "^15.14.0", 57 | "jest": "^29.7.0", 58 | "prettier": "^3.4.2", 59 | "source-map-support": "^0.5.21", 60 | "supertest": "^7.0.0", 61 | "ts-jest": "^29.2.5", 62 | "ts-loader": "^9.5.2", 63 | "ts-node": "^10.9.2", 64 | "tsconfig-paths": "^4.2.0", 65 | "typescript": "^5.7.3", 66 | "typescript-eslint": "^8.20.0" 67 | }, 68 | "jest": { 69 | "moduleFileExtensions": [ 70 | "js", 71 | "json", 72 | "ts" 73 | ], 74 | "rootDir": "src", 75 | "testRegex": ".*\\.spec\\.ts$", 76 | "transform": { 77 | "^.+\\.(t|j)s$": "ts-jest" 78 | }, 79 | "collectCoverageFrom": [ 80 | "**/*.(t|j)s" 81 | ], 82 | "coverageDirectory": "../coverage", 83 | "testEnvironment": "node" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/prisma/migrations/20250301182602_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "User" ( 3 | "id" SERIAL NOT NULL, 4 | "email" TEXT NOT NULL, 5 | "password" TEXT NOT NULL, 6 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | "updatedAt" TIMESTAMP(3) NOT NULL, 8 | 9 | CONSTRAINT "User_pkey" PRIMARY KEY ("id") 10 | ); 11 | 12 | -- CreateTable 13 | CREATE TABLE "Destination" ( 14 | "id" SERIAL NOT NULL, 15 | "name" TEXT NOT NULL, 16 | "travelDate" TIMESTAMP(3), 17 | "notes" TEXT, 18 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 19 | "updatedAt" TIMESTAMP(3) NOT NULL, 20 | "userId" INTEGER NOT NULL, 21 | 22 | CONSTRAINT "Destination_pkey" PRIMARY KEY ("id") 23 | ); 24 | 25 | -- CreateIndex 26 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); 27 | 28 | -- AddForeignKey 29 | ALTER TABLE "Destination" ADD CONSTRAINT "Destination_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 30 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /nest-js/travel-tracker/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? 5 | // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init 6 | 7 | generator client { 8 | provider = "prisma-client-js" 9 | } 10 | 11 | datasource db { 12 | provider = "postgresql" 13 | url = env("DATABASE_URL") 14 | } 15 | 16 | // -> users can register and login, once login happens jwt, jwtguard 17 | // -> destination module -> auth users 18 | 19 | model User { 20 | id Int @id @default(autoincrement()) 21 | email String @unique 22 | password String 23 | createdAt DateTime @default(now()) 24 | updatedAt DateTime @updatedAt 25 | destinations Destination[] 26 | } 27 | 28 | model Destination { 29 | id Int @id @default(autoincrement()) 30 | name String 31 | travelDate DateTime? 32 | notes String? 33 | createdAt DateTime @default(now()) 34 | updatedAt DateTime @updatedAt 35 | userId Int 36 | user User @relation(fields: [userId], references: [id]) 37 | } 38 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { ConfigModule } from '@nestjs/config'; 5 | import { PrismaModule } from './prisma/prisma.module'; 6 | import { AuthModule } from './auth/auth.module'; 7 | import { DestinationsModule } from './destinations/destinations.module'; 8 | 9 | @Module({ 10 | imports: [ 11 | ConfigModule.forRoot({ 12 | isGlobal: true, 13 | }), 14 | PrismaModule, 15 | AuthModule, 16 | DestinationsModule, 17 | ], 18 | controllers: [AppController], 19 | providers: [AppService], 20 | }) 21 | export class AppModule {} 22 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/auth.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AuthController } from './auth.controller'; 3 | 4 | describe('AuthController', () => { 5 | let controller: AuthController; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | controllers: [AuthController], 10 | }).compile(); 11 | 12 | controller = module.get(AuthController); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(controller).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Post } from '@nestjs/common'; 2 | import { AuthService } from './auth.service'; 3 | import { RegisterDto } from './dto/register.dto'; 4 | import { LoginDto } from './dto/login.dto'; 5 | 6 | @Controller('auth') 7 | export class AuthController { 8 | constructor(private readonly authService: AuthService) {} 9 | 10 | @Post('register-new-user') 11 | async register(@Body() registerDto: RegisterDto) { 12 | return this.authService.register(registerDto); 13 | } 14 | 15 | @Post('login') 16 | async login(@Body() loginDto: LoginDto) { 17 | return this.authService.login(loginDto); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AuthController } from './auth.controller'; 3 | import { AuthService } from './auth.service'; 4 | import { PrismaModule } from 'src/prisma/prisma.module'; 5 | import { JwtModule } from '@nestjs/jwt'; 6 | import { PassportModule } from '@nestjs/passport'; 7 | import { ConfigModule, ConfigService } from '@nestjs/config'; 8 | import { JwtStrategy } from './jwt.strategy'; 9 | 10 | @Module({ 11 | imports: [ 12 | PrismaModule, 13 | PassportModule, 14 | JwtModule.registerAsync({ 15 | imports: [ConfigModule], 16 | useFactory: async (configService: ConfigService) => ({ 17 | secret: configService.get('JWT_SECRET') || 'JWT_SECRET', 18 | signOptions: { expiresIn: '1h' }, 19 | }), 20 | inject: [ConfigService], 21 | }), 22 | ConfigModule, 23 | ], 24 | controllers: [AuthController], 25 | providers: [AuthService, JwtStrategy], 26 | exports: [JwtStrategy, PassportModule], 27 | }) 28 | export class AuthModule {} 29 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AuthService } from './auth.service'; 3 | 4 | describe('AuthService', () => { 5 | let service: AuthService; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | providers: [AuthService], 10 | }).compile(); 11 | 12 | service = module.get(AuthService); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ConflictException, 3 | Injectable, 4 | UnauthorizedException, 5 | } from '@nestjs/common'; 6 | import { PrismaService } from 'src/prisma/prisma.service'; 7 | import { RegisterDto } from './dto/register.dto'; 8 | import * as bcrypt from 'bcrypt'; 9 | import { LoginDto } from './dto/login.dto'; 10 | import { JwtService } from '@nestjs/jwt'; 11 | 12 | @Injectable() 13 | export class AuthService { 14 | constructor( 15 | private prisma: PrismaService, 16 | private jwtService: JwtService, 17 | ) {} 18 | 19 | //handle the new user registration 20 | async register(registerDto: RegisterDto) { 21 | const { email, password } = registerDto; 22 | //check if the user is exists 23 | const existingUser = await this.prisma.user.findUnique({ 24 | where: { email }, 25 | }); 26 | 27 | if (existingUser) { 28 | throw new ConflictException( 29 | 'User already exists! Please try with a different email', 30 | ); 31 | } 32 | //hash the password 33 | const hashedPassword = await bcrypt.hash(password, 10); 34 | //create new user 35 | const newlyCreatedUser = await this.prisma.user.create({ 36 | data: { 37 | email, 38 | password: hashedPassword, 39 | }, 40 | }); 41 | //remove password from the return object 42 | const { password: _, ...result } = newlyCreatedUser; 43 | return result; 44 | } 45 | 46 | async login(loginDto: LoginDto) { 47 | const { email, password } = loginDto; 48 | 49 | //find the current user by email as email is an unique property 50 | const user = await this.prisma.user.findUnique({ where: { email } }); 51 | if (!user) { 52 | throw new UnauthorizedException('Invalid credentials! Please try again'); 53 | } 54 | 55 | //verify the password 56 | const isPasswordValid = await bcrypt.compare(password, user.password); 57 | if (!isPasswordValid) { 58 | throw new UnauthorizedException('Invalid credentials! Please try again'); 59 | } 60 | 61 | const token = this.jwtService.sign({ userId: user.id }); 62 | 63 | const { password: _, ...result } = user; 64 | 65 | return { ...result, token }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/dto/login.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsEmail, IsString, MinLength } from 'class-validator'; 2 | 3 | export class LoginDto { 4 | @IsEmail({}, { message: 'Please provide a valid email address' }) 5 | email: string; 6 | 7 | @IsString() 8 | @MinLength(6, { message: 'Password must be at least 6 characters long' }) 9 | password: string; 10 | } 11 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/dto/register.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsEmail, IsString, MinLength } from 'class-validator'; 2 | 3 | export class RegisterDto { 4 | @IsEmail({}, { message: 'Please provide a valid email address' }) 5 | email: string; 6 | 7 | @IsString() 8 | @MinLength(6, { message: 'Password must be at least 6 characters long' }) 9 | password: string; 10 | } 11 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/jwt-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | 4 | @Injectable() 5 | export class JwtAuthGuard extends AuthGuard('jwt') {} 6 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/auth/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { ConfigService } from '@nestjs/config'; 3 | import { PassportStrategy } from '@nestjs/passport'; 4 | import { ExtractJwt, Strategy } from 'passport-jwt'; 5 | 6 | @Injectable() 7 | export class JwtStrategy extends PassportStrategy(Strategy) { 8 | constructor(private configService: ConfigService) { 9 | super({ 10 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 11 | ignoreExpiration: false, 12 | secretOrKey: configService.get('JWT_SECRET') || 'JWT_SECRET', 13 | }); 14 | } 15 | 16 | async validate(payload: any) { 17 | return { userId: payload.userId }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/destinations/destinations.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { DestinationsController } from './destinations.controller'; 3 | 4 | describe('DestinationsController', () => { 5 | let controller: DestinationsController; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | controllers: [DestinationsController], 10 | }).compile(); 11 | 12 | controller = module.get(DestinationsController); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(controller).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/destinations/destinations.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Body, 3 | Controller, 4 | Delete, 5 | Get, 6 | Param, 7 | Patch, 8 | Post, 9 | Request, 10 | UseGuards, 11 | } from '@nestjs/common'; 12 | import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'; 13 | import { DestinationsService } from './destinations.service'; 14 | import { CreateDestinationDto } from './dto/create-destination.dto'; 15 | import { UpdateDestinationDto } from './dto/update-destination.dto'; 16 | 17 | //creating new dest 18 | //fetching all desc 19 | //delete, update -> problem -> auth user ? 20 | 21 | //guard 22 | 23 | @Controller('destinations') 24 | @UseGuards(JwtAuthGuard) 25 | export class DestinationsController { 26 | constructor(private readonly destinationsService: DestinationsService) {} 27 | 28 | @Post() 29 | create(@Request() req, @Body() createDestinationDto: CreateDestinationDto) { 30 | return this.destinationsService.create( 31 | req.user.userId, 32 | createDestinationDto, 33 | ); 34 | } 35 | 36 | @Get() 37 | findAll(@Request() req) { 38 | return this.destinationsService.findAll(req.user.userId); 39 | } 40 | 41 | @Get(':id') 42 | findOne(@Request() req, @Param('id') id: string) { 43 | return this.destinationsService.findOne(req.user.userId, +id); 44 | } 45 | 46 | @Patch(':id') 47 | update( 48 | @Request() req, 49 | @Param('id') id: string, 50 | @Body() updateDestinationDto: UpdateDestinationDto, 51 | ) { 52 | return this.destinationsService.update( 53 | req.user.userId, 54 | +id, 55 | updateDestinationDto, 56 | ); 57 | } 58 | 59 | @Delete(':id') 60 | remove(@Request() req, @Param('id') id: string) { 61 | return this.destinationsService.removeDestination(req.user.userId, +id); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/destinations/destinations.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { DestinationsController } from './destinations.controller'; 3 | import { DestinationsService } from './destinations.service'; 4 | 5 | @Module({ 6 | controllers: [DestinationsController], 7 | providers: [DestinationsService] 8 | }) 9 | export class DestinationsModule {} 10 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/destinations/destinations.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { DestinationsService } from './destinations.service'; 3 | 4 | describe('DestinationsService', () => { 5 | let service: DestinationsService; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | providers: [DestinationsService], 10 | }).compile(); 11 | 12 | service = module.get(DestinationsService); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/destinations/destinations.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { PrismaService } from 'src/prisma/prisma.service'; 3 | import { CreateDestinationDto } from './dto/create-destination.dto'; 4 | import { UpdateDestinationDto } from './dto/update-destination.dto'; 5 | 6 | @Injectable() 7 | export class DestinationsService { 8 | constructor(private prisma: PrismaService) {} 9 | 10 | async create(userId: number, createDestinationDto: CreateDestinationDto) { 11 | return this.prisma.destination.create({ 12 | data: { 13 | ...createDestinationDto, 14 | travelDate: new Date(createDestinationDto.travelDate).toISOString(), 15 | userId, 16 | }, 17 | }); 18 | } 19 | 20 | async findAll(userId: number) { 21 | return this.prisma.destination.findMany({ 22 | where: { userId }, 23 | }); 24 | } 25 | 26 | async findOne(userId: number, id: number) { 27 | const destination = await this.prisma.destination.findFirst({ 28 | where: { id, userId }, 29 | }); 30 | 31 | if (!destination) { 32 | throw new NotFoundException(`Destination not found with this id ${id}`); 33 | } 34 | 35 | return destination; 36 | } 37 | 38 | async removeDestination(userId: number, id: number) { 39 | await this.findOne(userId, id); 40 | 41 | return this.prisma.destination.delete({ 42 | where: { id }, 43 | }); 44 | } 45 | 46 | async update( 47 | userId: number, 48 | id: number, 49 | updateDestinationDto: UpdateDestinationDto, 50 | ) { 51 | await this.findOne(userId, id); 52 | 53 | return this.prisma.destination.update({ 54 | where: { id }, 55 | data: updateDestinationDto, 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/destinations/dto/create-destination.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsDateString, 3 | IsNotEmpty, 4 | IsOptional, 5 | IsString, 6 | } from 'class-validator'; 7 | 8 | export class CreateDestinationDto { 9 | @IsNotEmpty() 10 | @IsString() 11 | name: string; 12 | 13 | @IsNotEmpty() 14 | @IsDateString() 15 | travelDate: string; 16 | 17 | @IsOptional() 18 | @IsString() 19 | notes?: string; 20 | } 21 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/destinations/dto/update-destination.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | import { CreateDestinationDto } from './create-destination.dto'; 3 | 4 | export class UpdateDestinationDto extends PartialType(CreateDestinationDto) {} 5 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { ValidationPipe } from '@nestjs/common'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.create(AppModule); 7 | app.useGlobalPipes(new ValidationPipe()); 8 | await app.listen(process.env.PORT ?? 3000); 9 | } 10 | bootstrap(); 11 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/prisma/prisma.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | import { PrismaService } from './prisma.service'; 3 | 4 | //makes the module as globally scoped 5 | @Global() 6 | @Module({ 7 | //registering prisma service in this module 8 | providers: [PrismaService], 9 | 10 | //making sure that this PrismaService is available to other modules that will import PrismaModule 11 | exports: [PrismaService], 12 | }) 13 | export class PrismaModule {} 14 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/prisma/prisma.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { PrismaService } from './prisma.service'; 3 | 4 | describe('PrismaService', () => { 5 | let service: PrismaService; 6 | 7 | beforeEach(async () => { 8 | const module: TestingModule = await Test.createTestingModule({ 9 | providers: [PrismaService], 10 | }).compile(); 11 | 12 | service = module.get(PrismaService); 13 | }); 14 | 15 | it('should be defined', () => { 16 | expect(service).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/src/prisma/prisma.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | type OnModuleDestroy, 4 | type OnModuleInit, 5 | } from '@nestjs/common'; 6 | import { PrismaClient } from '@prisma/client'; 7 | 8 | @Injectable() 9 | export class PrismaService 10 | extends PrismaClient 11 | implements OnModuleInit, OnModuleDestroy 12 | { 13 | async onModuleInit() { 14 | await this.$connect(); 15 | } 16 | 17 | async onModuleDestroy() { 18 | await this.$disconnect(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { App } from 'supertest/types'; 5 | import { AppModule } from './../src/app.module'; 6 | 7 | describe('AppController (e2e)', () => { 8 | let app: INestApplication; 9 | 10 | beforeEach(async () => { 11 | const moduleFixture: TestingModule = await Test.createTestingModule({ 12 | imports: [AppModule], 13 | }).compile(); 14 | 15 | app = moduleFixture.createNestApplication(); 16 | await app.init(); 17 | }); 18 | 19 | it('/ (GET)', () => { 20 | return request(app.getHttpServer()) 21 | .get('/') 22 | .expect(200) 23 | .expect('Hello World!'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /nest-js/travel-tracker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2023", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "noImplicitAny": false, 18 | "strictBindCallApply": false, 19 | "noFallthroughCasesInSwitch": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .env -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | # Keep environment variables out of version control 3 | .env 4 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | EXPOSE 3000 12 | 13 | CMD ["node", "src/server.js"] 14 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | database: 5 | image: postgres:15 6 | container_name: postgres_prisma_db 7 | restart: always 8 | environment: 9 | POSTGRES_USER: postgres 10 | POSTGRES_PASSWORD: root 11 | postgres_prisma_db: prisma-with-postgres 12 | ports: 13 | - "5434:5432" 14 | volumes: 15 | - postgres_data:/var/lib/postgresql/data 16 | 17 | app: 18 | build: . 19 | container_name: express_prisma_api 20 | depends_on: 21 | - database 22 | environment: 23 | - DATABASE_URL=postgresql://postgres:root@database:5432/prisma-with-postgres?schema=public 24 | ports: 25 | - "3003:3000" 26 | volumes: 27 | - .:/usr/src/app 28 | - /usr/src/app/node_modules 29 | command: [ "sh", "-c", "npx prisma generate && node src/server.js" ] 30 | 31 | prometheus: 32 | image: prom/prometheus:latest 33 | container_name: prometheus_prisma 34 | restart: always 35 | volumes: 36 | - ./prometheus.yml:/etc/prometheus/prometheus.yml 37 | ports: 38 | - "9091:9090" 39 | command: 40 | - --config.file=/etc/prometheus/prometheus.yml 41 | 42 | grafana: 43 | image: grafana/grafana:latest 44 | container_name: prisma_grafana 45 | restart: always 46 | ports: 47 | - "3005:3000" 48 | volumes: 49 | - grafana_data:/var/lib/grafana 50 | environment: 51 | - GF_SECURITY_ADMIN_USER=admin 52 | - GF_SECURITY_ADMIN_PASSWORD=admin 53 | - GF_USERS_ALLOW_SIGN_UP=false 54 | depends_on: 55 | - prometheus 56 | 57 | volumes: 58 | postgres_data: 59 | grafana_data: 60 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgres-with-prisma", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "postgres-with-prisma", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@prisma/client": "^6.4.1", 13 | "dotenv": "^16.4.7", 14 | "express": "^4.21.2", 15 | "nodemon": "^3.1.9", 16 | "prisma": "^6.4.1", 17 | "prom-client": "^15.1.3" 18 | } 19 | }, 20 | "node_modules/@esbuild/aix-ppc64": { 21 | "version": "0.25.0", 22 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", 23 | "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", 24 | "cpu": [ 25 | "ppc64" 26 | ], 27 | "optional": true, 28 | "os": [ 29 | "aix" 30 | ], 31 | "engines": { 32 | "node": ">=18" 33 | } 34 | }, 35 | "node_modules/@esbuild/android-arm": { 36 | "version": "0.25.0", 37 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", 38 | "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", 39 | "cpu": [ 40 | "arm" 41 | ], 42 | "optional": true, 43 | "os": [ 44 | "android" 45 | ], 46 | "engines": { 47 | "node": ">=18" 48 | } 49 | }, 50 | "node_modules/@esbuild/android-arm64": { 51 | "version": "0.25.0", 52 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", 53 | "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", 54 | "cpu": [ 55 | "arm64" 56 | ], 57 | "optional": true, 58 | "os": [ 59 | "android" 60 | ], 61 | "engines": { 62 | "node": ">=18" 63 | } 64 | }, 65 | "node_modules/@esbuild/android-x64": { 66 | "version": "0.25.0", 67 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", 68 | "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", 69 | "cpu": [ 70 | "x64" 71 | ], 72 | "optional": true, 73 | "os": [ 74 | "android" 75 | ], 76 | "engines": { 77 | "node": ">=18" 78 | } 79 | }, 80 | "node_modules/@esbuild/darwin-arm64": { 81 | "version": "0.25.0", 82 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", 83 | "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", 84 | "cpu": [ 85 | "arm64" 86 | ], 87 | "optional": true, 88 | "os": [ 89 | "darwin" 90 | ], 91 | "engines": { 92 | "node": ">=18" 93 | } 94 | }, 95 | "node_modules/@esbuild/darwin-x64": { 96 | "version": "0.25.0", 97 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", 98 | "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", 99 | "cpu": [ 100 | "x64" 101 | ], 102 | "optional": true, 103 | "os": [ 104 | "darwin" 105 | ], 106 | "engines": { 107 | "node": ">=18" 108 | } 109 | }, 110 | "node_modules/@esbuild/freebsd-arm64": { 111 | "version": "0.25.0", 112 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", 113 | "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", 114 | "cpu": [ 115 | "arm64" 116 | ], 117 | "optional": true, 118 | "os": [ 119 | "freebsd" 120 | ], 121 | "engines": { 122 | "node": ">=18" 123 | } 124 | }, 125 | "node_modules/@esbuild/freebsd-x64": { 126 | "version": "0.25.0", 127 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", 128 | "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", 129 | "cpu": [ 130 | "x64" 131 | ], 132 | "optional": true, 133 | "os": [ 134 | "freebsd" 135 | ], 136 | "engines": { 137 | "node": ">=18" 138 | } 139 | }, 140 | "node_modules/@esbuild/linux-arm": { 141 | "version": "0.25.0", 142 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", 143 | "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", 144 | "cpu": [ 145 | "arm" 146 | ], 147 | "optional": true, 148 | "os": [ 149 | "linux" 150 | ], 151 | "engines": { 152 | "node": ">=18" 153 | } 154 | }, 155 | "node_modules/@esbuild/linux-arm64": { 156 | "version": "0.25.0", 157 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", 158 | "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", 159 | "cpu": [ 160 | "arm64" 161 | ], 162 | "optional": true, 163 | "os": [ 164 | "linux" 165 | ], 166 | "engines": { 167 | "node": ">=18" 168 | } 169 | }, 170 | "node_modules/@esbuild/linux-ia32": { 171 | "version": "0.25.0", 172 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", 173 | "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", 174 | "cpu": [ 175 | "ia32" 176 | ], 177 | "optional": true, 178 | "os": [ 179 | "linux" 180 | ], 181 | "engines": { 182 | "node": ">=18" 183 | } 184 | }, 185 | "node_modules/@esbuild/linux-loong64": { 186 | "version": "0.25.0", 187 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", 188 | "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", 189 | "cpu": [ 190 | "loong64" 191 | ], 192 | "optional": true, 193 | "os": [ 194 | "linux" 195 | ], 196 | "engines": { 197 | "node": ">=18" 198 | } 199 | }, 200 | "node_modules/@esbuild/linux-mips64el": { 201 | "version": "0.25.0", 202 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", 203 | "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", 204 | "cpu": [ 205 | "mips64el" 206 | ], 207 | "optional": true, 208 | "os": [ 209 | "linux" 210 | ], 211 | "engines": { 212 | "node": ">=18" 213 | } 214 | }, 215 | "node_modules/@esbuild/linux-ppc64": { 216 | "version": "0.25.0", 217 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", 218 | "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", 219 | "cpu": [ 220 | "ppc64" 221 | ], 222 | "optional": true, 223 | "os": [ 224 | "linux" 225 | ], 226 | "engines": { 227 | "node": ">=18" 228 | } 229 | }, 230 | "node_modules/@esbuild/linux-riscv64": { 231 | "version": "0.25.0", 232 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", 233 | "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", 234 | "cpu": [ 235 | "riscv64" 236 | ], 237 | "optional": true, 238 | "os": [ 239 | "linux" 240 | ], 241 | "engines": { 242 | "node": ">=18" 243 | } 244 | }, 245 | "node_modules/@esbuild/linux-s390x": { 246 | "version": "0.25.0", 247 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", 248 | "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", 249 | "cpu": [ 250 | "s390x" 251 | ], 252 | "optional": true, 253 | "os": [ 254 | "linux" 255 | ], 256 | "engines": { 257 | "node": ">=18" 258 | } 259 | }, 260 | "node_modules/@esbuild/linux-x64": { 261 | "version": "0.25.0", 262 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", 263 | "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", 264 | "cpu": [ 265 | "x64" 266 | ], 267 | "optional": true, 268 | "os": [ 269 | "linux" 270 | ], 271 | "engines": { 272 | "node": ">=18" 273 | } 274 | }, 275 | "node_modules/@esbuild/netbsd-arm64": { 276 | "version": "0.25.0", 277 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", 278 | "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", 279 | "cpu": [ 280 | "arm64" 281 | ], 282 | "optional": true, 283 | "os": [ 284 | "netbsd" 285 | ], 286 | "engines": { 287 | "node": ">=18" 288 | } 289 | }, 290 | "node_modules/@esbuild/netbsd-x64": { 291 | "version": "0.25.0", 292 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", 293 | "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", 294 | "cpu": [ 295 | "x64" 296 | ], 297 | "optional": true, 298 | "os": [ 299 | "netbsd" 300 | ], 301 | "engines": { 302 | "node": ">=18" 303 | } 304 | }, 305 | "node_modules/@esbuild/openbsd-arm64": { 306 | "version": "0.25.0", 307 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", 308 | "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", 309 | "cpu": [ 310 | "arm64" 311 | ], 312 | "optional": true, 313 | "os": [ 314 | "openbsd" 315 | ], 316 | "engines": { 317 | "node": ">=18" 318 | } 319 | }, 320 | "node_modules/@esbuild/openbsd-x64": { 321 | "version": "0.25.0", 322 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", 323 | "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", 324 | "cpu": [ 325 | "x64" 326 | ], 327 | "optional": true, 328 | "os": [ 329 | "openbsd" 330 | ], 331 | "engines": { 332 | "node": ">=18" 333 | } 334 | }, 335 | "node_modules/@esbuild/sunos-x64": { 336 | "version": "0.25.0", 337 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", 338 | "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", 339 | "cpu": [ 340 | "x64" 341 | ], 342 | "optional": true, 343 | "os": [ 344 | "sunos" 345 | ], 346 | "engines": { 347 | "node": ">=18" 348 | } 349 | }, 350 | "node_modules/@esbuild/win32-arm64": { 351 | "version": "0.25.0", 352 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", 353 | "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", 354 | "cpu": [ 355 | "arm64" 356 | ], 357 | "optional": true, 358 | "os": [ 359 | "win32" 360 | ], 361 | "engines": { 362 | "node": ">=18" 363 | } 364 | }, 365 | "node_modules/@esbuild/win32-ia32": { 366 | "version": "0.25.0", 367 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", 368 | "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", 369 | "cpu": [ 370 | "ia32" 371 | ], 372 | "optional": true, 373 | "os": [ 374 | "win32" 375 | ], 376 | "engines": { 377 | "node": ">=18" 378 | } 379 | }, 380 | "node_modules/@esbuild/win32-x64": { 381 | "version": "0.25.0", 382 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", 383 | "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", 384 | "cpu": [ 385 | "x64" 386 | ], 387 | "optional": true, 388 | "os": [ 389 | "win32" 390 | ], 391 | "engines": { 392 | "node": ">=18" 393 | } 394 | }, 395 | "node_modules/@opentelemetry/api": { 396 | "version": "1.9.0", 397 | "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", 398 | "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", 399 | "engines": { 400 | "node": ">=8.0.0" 401 | } 402 | }, 403 | "node_modules/@prisma/client": { 404 | "version": "6.4.1", 405 | "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.4.1.tgz", 406 | "integrity": "sha512-A7Mwx44+GVZVexT5e2GF/WcKkEkNNKbgr059xpr5mn+oUm2ZW1svhe+0TRNBwCdzhfIZ+q23jEgsNPvKD9u+6g==", 407 | "hasInstallScript": true, 408 | "engines": { 409 | "node": ">=18.18" 410 | }, 411 | "peerDependencies": { 412 | "prisma": "*", 413 | "typescript": ">=5.1.0" 414 | }, 415 | "peerDependenciesMeta": { 416 | "prisma": { 417 | "optional": true 418 | }, 419 | "typescript": { 420 | "optional": true 421 | } 422 | } 423 | }, 424 | "node_modules/@prisma/debug": { 425 | "version": "6.4.1", 426 | "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.4.1.tgz", 427 | "integrity": "sha512-Q9xk6yjEGIThjSD8zZegxd5tBRNHYd13GOIG0nLsanbTXATiPXCLyvlYEfvbR2ft6dlRsziQXfQGxAgv7zcMUA==" 428 | }, 429 | "node_modules/@prisma/engines": { 430 | "version": "6.4.1", 431 | "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.4.1.tgz", 432 | "integrity": "sha512-KldENzMHtKYwsOSLThghOIdXOBEsfDuGSrxAZjMnimBiDKd3AE4JQ+Kv+gBD/x77WoV9xIPf25GXMWffXZ17BA==", 433 | "hasInstallScript": true, 434 | "dependencies": { 435 | "@prisma/debug": "6.4.1", 436 | "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", 437 | "@prisma/fetch-engine": "6.4.1", 438 | "@prisma/get-platform": "6.4.1" 439 | } 440 | }, 441 | "node_modules/@prisma/engines-version": { 442 | "version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", 443 | "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d.tgz", 444 | "integrity": "sha512-Xq54qw55vaCGrGgIJqyDwOq0TtjZPJEWsbQAHugk99hpDf2jcEeQhUcF+yzEsSqegBaDNLA4IC8Nn34sXmkiTQ==" 445 | }, 446 | "node_modules/@prisma/fetch-engine": { 447 | "version": "6.4.1", 448 | "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.4.1.tgz", 449 | "integrity": "sha512-uZ5hVeTmDspx7KcaRCNoXmcReOD+84nwlO2oFvQPRQh9xiFYnnUKDz7l9bLxp8t4+25CsaNlgrgilXKSQwrIGQ==", 450 | "dependencies": { 451 | "@prisma/debug": "6.4.1", 452 | "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", 453 | "@prisma/get-platform": "6.4.1" 454 | } 455 | }, 456 | "node_modules/@prisma/get-platform": { 457 | "version": "6.4.1", 458 | "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.4.1.tgz", 459 | "integrity": "sha512-gXqZaDI5scDkBF8oza7fOD3Q3QMD0e0rBynlzDDZdTWbWmzjuW58PRZtj+jkvKje2+ZigCWkH8SsWZAsH6q1Yw==", 460 | "dependencies": { 461 | "@prisma/debug": "6.4.1" 462 | } 463 | }, 464 | "node_modules/accepts": { 465 | "version": "1.3.8", 466 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 467 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 468 | "dependencies": { 469 | "mime-types": "~2.1.34", 470 | "negotiator": "0.6.3" 471 | }, 472 | "engines": { 473 | "node": ">= 0.6" 474 | } 475 | }, 476 | "node_modules/anymatch": { 477 | "version": "3.1.3", 478 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 479 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 480 | "dependencies": { 481 | "normalize-path": "^3.0.0", 482 | "picomatch": "^2.0.4" 483 | }, 484 | "engines": { 485 | "node": ">= 8" 486 | } 487 | }, 488 | "node_modules/array-flatten": { 489 | "version": "1.1.1", 490 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 491 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 492 | }, 493 | "node_modules/balanced-match": { 494 | "version": "1.0.2", 495 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 496 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 497 | }, 498 | "node_modules/binary-extensions": { 499 | "version": "2.3.0", 500 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 501 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 502 | "engines": { 503 | "node": ">=8" 504 | }, 505 | "funding": { 506 | "url": "https://github.com/sponsors/sindresorhus" 507 | } 508 | }, 509 | "node_modules/bintrees": { 510 | "version": "1.0.2", 511 | "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", 512 | "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" 513 | }, 514 | "node_modules/body-parser": { 515 | "version": "1.20.3", 516 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", 517 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", 518 | "dependencies": { 519 | "bytes": "3.1.2", 520 | "content-type": "~1.0.5", 521 | "debug": "2.6.9", 522 | "depd": "2.0.0", 523 | "destroy": "1.2.0", 524 | "http-errors": "2.0.0", 525 | "iconv-lite": "0.4.24", 526 | "on-finished": "2.4.1", 527 | "qs": "6.13.0", 528 | "raw-body": "2.5.2", 529 | "type-is": "~1.6.18", 530 | "unpipe": "1.0.0" 531 | }, 532 | "engines": { 533 | "node": ">= 0.8", 534 | "npm": "1.2.8000 || >= 1.4.16" 535 | } 536 | }, 537 | "node_modules/brace-expansion": { 538 | "version": "1.1.11", 539 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 540 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 541 | "dependencies": { 542 | "balanced-match": "^1.0.0", 543 | "concat-map": "0.0.1" 544 | } 545 | }, 546 | "node_modules/braces": { 547 | "version": "3.0.3", 548 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 549 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 550 | "dependencies": { 551 | "fill-range": "^7.1.1" 552 | }, 553 | "engines": { 554 | "node": ">=8" 555 | } 556 | }, 557 | "node_modules/bytes": { 558 | "version": "3.1.2", 559 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 560 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 561 | "engines": { 562 | "node": ">= 0.8" 563 | } 564 | }, 565 | "node_modules/call-bind-apply-helpers": { 566 | "version": "1.0.2", 567 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 568 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 569 | "dependencies": { 570 | "es-errors": "^1.3.0", 571 | "function-bind": "^1.1.2" 572 | }, 573 | "engines": { 574 | "node": ">= 0.4" 575 | } 576 | }, 577 | "node_modules/call-bound": { 578 | "version": "1.0.3", 579 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", 580 | "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", 581 | "dependencies": { 582 | "call-bind-apply-helpers": "^1.0.1", 583 | "get-intrinsic": "^1.2.6" 584 | }, 585 | "engines": { 586 | "node": ">= 0.4" 587 | }, 588 | "funding": { 589 | "url": "https://github.com/sponsors/ljharb" 590 | } 591 | }, 592 | "node_modules/chokidar": { 593 | "version": "3.6.0", 594 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 595 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 596 | "dependencies": { 597 | "anymatch": "~3.1.2", 598 | "braces": "~3.0.2", 599 | "glob-parent": "~5.1.2", 600 | "is-binary-path": "~2.1.0", 601 | "is-glob": "~4.0.1", 602 | "normalize-path": "~3.0.0", 603 | "readdirp": "~3.6.0" 604 | }, 605 | "engines": { 606 | "node": ">= 8.10.0" 607 | }, 608 | "funding": { 609 | "url": "https://paulmillr.com/funding/" 610 | }, 611 | "optionalDependencies": { 612 | "fsevents": "~2.3.2" 613 | } 614 | }, 615 | "node_modules/concat-map": { 616 | "version": "0.0.1", 617 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 618 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 619 | }, 620 | "node_modules/content-disposition": { 621 | "version": "0.5.4", 622 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 623 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 624 | "dependencies": { 625 | "safe-buffer": "5.2.1" 626 | }, 627 | "engines": { 628 | "node": ">= 0.6" 629 | } 630 | }, 631 | "node_modules/content-type": { 632 | "version": "1.0.5", 633 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 634 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 635 | "engines": { 636 | "node": ">= 0.6" 637 | } 638 | }, 639 | "node_modules/cookie": { 640 | "version": "0.7.1", 641 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", 642 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", 643 | "engines": { 644 | "node": ">= 0.6" 645 | } 646 | }, 647 | "node_modules/cookie-signature": { 648 | "version": "1.0.6", 649 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 650 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 651 | }, 652 | "node_modules/debug": { 653 | "version": "2.6.9", 654 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 655 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 656 | "dependencies": { 657 | "ms": "2.0.0" 658 | } 659 | }, 660 | "node_modules/depd": { 661 | "version": "2.0.0", 662 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 663 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 664 | "engines": { 665 | "node": ">= 0.8" 666 | } 667 | }, 668 | "node_modules/destroy": { 669 | "version": "1.2.0", 670 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 671 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 672 | "engines": { 673 | "node": ">= 0.8", 674 | "npm": "1.2.8000 || >= 1.4.16" 675 | } 676 | }, 677 | "node_modules/dotenv": { 678 | "version": "16.4.7", 679 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", 680 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", 681 | "engines": { 682 | "node": ">=12" 683 | }, 684 | "funding": { 685 | "url": "https://dotenvx.com" 686 | } 687 | }, 688 | "node_modules/dunder-proto": { 689 | "version": "1.0.1", 690 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 691 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 692 | "dependencies": { 693 | "call-bind-apply-helpers": "^1.0.1", 694 | "es-errors": "^1.3.0", 695 | "gopd": "^1.2.0" 696 | }, 697 | "engines": { 698 | "node": ">= 0.4" 699 | } 700 | }, 701 | "node_modules/ee-first": { 702 | "version": "1.1.1", 703 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 704 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 705 | }, 706 | "node_modules/encodeurl": { 707 | "version": "2.0.0", 708 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 709 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 710 | "engines": { 711 | "node": ">= 0.8" 712 | } 713 | }, 714 | "node_modules/es-define-property": { 715 | "version": "1.0.1", 716 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 717 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 718 | "engines": { 719 | "node": ">= 0.4" 720 | } 721 | }, 722 | "node_modules/es-errors": { 723 | "version": "1.3.0", 724 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 725 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 726 | "engines": { 727 | "node": ">= 0.4" 728 | } 729 | }, 730 | "node_modules/es-object-atoms": { 731 | "version": "1.1.1", 732 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 733 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 734 | "dependencies": { 735 | "es-errors": "^1.3.0" 736 | }, 737 | "engines": { 738 | "node": ">= 0.4" 739 | } 740 | }, 741 | "node_modules/esbuild": { 742 | "version": "0.25.0", 743 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", 744 | "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", 745 | "hasInstallScript": true, 746 | "bin": { 747 | "esbuild": "bin/esbuild" 748 | }, 749 | "engines": { 750 | "node": ">=18" 751 | }, 752 | "optionalDependencies": { 753 | "@esbuild/aix-ppc64": "0.25.0", 754 | "@esbuild/android-arm": "0.25.0", 755 | "@esbuild/android-arm64": "0.25.0", 756 | "@esbuild/android-x64": "0.25.0", 757 | "@esbuild/darwin-arm64": "0.25.0", 758 | "@esbuild/darwin-x64": "0.25.0", 759 | "@esbuild/freebsd-arm64": "0.25.0", 760 | "@esbuild/freebsd-x64": "0.25.0", 761 | "@esbuild/linux-arm": "0.25.0", 762 | "@esbuild/linux-arm64": "0.25.0", 763 | "@esbuild/linux-ia32": "0.25.0", 764 | "@esbuild/linux-loong64": "0.25.0", 765 | "@esbuild/linux-mips64el": "0.25.0", 766 | "@esbuild/linux-ppc64": "0.25.0", 767 | "@esbuild/linux-riscv64": "0.25.0", 768 | "@esbuild/linux-s390x": "0.25.0", 769 | "@esbuild/linux-x64": "0.25.0", 770 | "@esbuild/netbsd-arm64": "0.25.0", 771 | "@esbuild/netbsd-x64": "0.25.0", 772 | "@esbuild/openbsd-arm64": "0.25.0", 773 | "@esbuild/openbsd-x64": "0.25.0", 774 | "@esbuild/sunos-x64": "0.25.0", 775 | "@esbuild/win32-arm64": "0.25.0", 776 | "@esbuild/win32-ia32": "0.25.0", 777 | "@esbuild/win32-x64": "0.25.0" 778 | } 779 | }, 780 | "node_modules/esbuild-register": { 781 | "version": "3.6.0", 782 | "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", 783 | "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", 784 | "dependencies": { 785 | "debug": "^4.3.4" 786 | }, 787 | "peerDependencies": { 788 | "esbuild": ">=0.12 <1" 789 | } 790 | }, 791 | "node_modules/esbuild-register/node_modules/debug": { 792 | "version": "4.4.0", 793 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 794 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 795 | "dependencies": { 796 | "ms": "^2.1.3" 797 | }, 798 | "engines": { 799 | "node": ">=6.0" 800 | }, 801 | "peerDependenciesMeta": { 802 | "supports-color": { 803 | "optional": true 804 | } 805 | } 806 | }, 807 | "node_modules/esbuild-register/node_modules/ms": { 808 | "version": "2.1.3", 809 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 810 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 811 | }, 812 | "node_modules/escape-html": { 813 | "version": "1.0.3", 814 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 815 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 816 | }, 817 | "node_modules/etag": { 818 | "version": "1.8.1", 819 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 820 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 821 | "engines": { 822 | "node": ">= 0.6" 823 | } 824 | }, 825 | "node_modules/express": { 826 | "version": "4.21.2", 827 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", 828 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", 829 | "dependencies": { 830 | "accepts": "~1.3.8", 831 | "array-flatten": "1.1.1", 832 | "body-parser": "1.20.3", 833 | "content-disposition": "0.5.4", 834 | "content-type": "~1.0.4", 835 | "cookie": "0.7.1", 836 | "cookie-signature": "1.0.6", 837 | "debug": "2.6.9", 838 | "depd": "2.0.0", 839 | "encodeurl": "~2.0.0", 840 | "escape-html": "~1.0.3", 841 | "etag": "~1.8.1", 842 | "finalhandler": "1.3.1", 843 | "fresh": "0.5.2", 844 | "http-errors": "2.0.0", 845 | "merge-descriptors": "1.0.3", 846 | "methods": "~1.1.2", 847 | "on-finished": "2.4.1", 848 | "parseurl": "~1.3.3", 849 | "path-to-regexp": "0.1.12", 850 | "proxy-addr": "~2.0.7", 851 | "qs": "6.13.0", 852 | "range-parser": "~1.2.1", 853 | "safe-buffer": "5.2.1", 854 | "send": "0.19.0", 855 | "serve-static": "1.16.2", 856 | "setprototypeof": "1.2.0", 857 | "statuses": "2.0.1", 858 | "type-is": "~1.6.18", 859 | "utils-merge": "1.0.1", 860 | "vary": "~1.1.2" 861 | }, 862 | "engines": { 863 | "node": ">= 0.10.0" 864 | }, 865 | "funding": { 866 | "type": "opencollective", 867 | "url": "https://opencollective.com/express" 868 | } 869 | }, 870 | "node_modules/fill-range": { 871 | "version": "7.1.1", 872 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 873 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 874 | "dependencies": { 875 | "to-regex-range": "^5.0.1" 876 | }, 877 | "engines": { 878 | "node": ">=8" 879 | } 880 | }, 881 | "node_modules/finalhandler": { 882 | "version": "1.3.1", 883 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", 884 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", 885 | "dependencies": { 886 | "debug": "2.6.9", 887 | "encodeurl": "~2.0.0", 888 | "escape-html": "~1.0.3", 889 | "on-finished": "2.4.1", 890 | "parseurl": "~1.3.3", 891 | "statuses": "2.0.1", 892 | "unpipe": "~1.0.0" 893 | }, 894 | "engines": { 895 | "node": ">= 0.8" 896 | } 897 | }, 898 | "node_modules/forwarded": { 899 | "version": "0.2.0", 900 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 901 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 902 | "engines": { 903 | "node": ">= 0.6" 904 | } 905 | }, 906 | "node_modules/fresh": { 907 | "version": "0.5.2", 908 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 909 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 910 | "engines": { 911 | "node": ">= 0.6" 912 | } 913 | }, 914 | "node_modules/fsevents": { 915 | "version": "2.3.3", 916 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 917 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 918 | "hasInstallScript": true, 919 | "optional": true, 920 | "os": [ 921 | "darwin" 922 | ], 923 | "engines": { 924 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 925 | } 926 | }, 927 | "node_modules/function-bind": { 928 | "version": "1.1.2", 929 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 930 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 931 | "funding": { 932 | "url": "https://github.com/sponsors/ljharb" 933 | } 934 | }, 935 | "node_modules/get-intrinsic": { 936 | "version": "1.3.0", 937 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 938 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 939 | "dependencies": { 940 | "call-bind-apply-helpers": "^1.0.2", 941 | "es-define-property": "^1.0.1", 942 | "es-errors": "^1.3.0", 943 | "es-object-atoms": "^1.1.1", 944 | "function-bind": "^1.1.2", 945 | "get-proto": "^1.0.1", 946 | "gopd": "^1.2.0", 947 | "has-symbols": "^1.1.0", 948 | "hasown": "^2.0.2", 949 | "math-intrinsics": "^1.1.0" 950 | }, 951 | "engines": { 952 | "node": ">= 0.4" 953 | }, 954 | "funding": { 955 | "url": "https://github.com/sponsors/ljharb" 956 | } 957 | }, 958 | "node_modules/get-proto": { 959 | "version": "1.0.1", 960 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 961 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 962 | "dependencies": { 963 | "dunder-proto": "^1.0.1", 964 | "es-object-atoms": "^1.0.0" 965 | }, 966 | "engines": { 967 | "node": ">= 0.4" 968 | } 969 | }, 970 | "node_modules/glob-parent": { 971 | "version": "5.1.2", 972 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 973 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 974 | "dependencies": { 975 | "is-glob": "^4.0.1" 976 | }, 977 | "engines": { 978 | "node": ">= 6" 979 | } 980 | }, 981 | "node_modules/gopd": { 982 | "version": "1.2.0", 983 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 984 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 985 | "engines": { 986 | "node": ">= 0.4" 987 | }, 988 | "funding": { 989 | "url": "https://github.com/sponsors/ljharb" 990 | } 991 | }, 992 | "node_modules/has-flag": { 993 | "version": "3.0.0", 994 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 995 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 996 | "engines": { 997 | "node": ">=4" 998 | } 999 | }, 1000 | "node_modules/has-symbols": { 1001 | "version": "1.1.0", 1002 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 1003 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 1004 | "engines": { 1005 | "node": ">= 0.4" 1006 | }, 1007 | "funding": { 1008 | "url": "https://github.com/sponsors/ljharb" 1009 | } 1010 | }, 1011 | "node_modules/hasown": { 1012 | "version": "2.0.2", 1013 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 1014 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 1015 | "dependencies": { 1016 | "function-bind": "^1.1.2" 1017 | }, 1018 | "engines": { 1019 | "node": ">= 0.4" 1020 | } 1021 | }, 1022 | "node_modules/http-errors": { 1023 | "version": "2.0.0", 1024 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 1025 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 1026 | "dependencies": { 1027 | "depd": "2.0.0", 1028 | "inherits": "2.0.4", 1029 | "setprototypeof": "1.2.0", 1030 | "statuses": "2.0.1", 1031 | "toidentifier": "1.0.1" 1032 | }, 1033 | "engines": { 1034 | "node": ">= 0.8" 1035 | } 1036 | }, 1037 | "node_modules/iconv-lite": { 1038 | "version": "0.4.24", 1039 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1040 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1041 | "dependencies": { 1042 | "safer-buffer": ">= 2.1.2 < 3" 1043 | }, 1044 | "engines": { 1045 | "node": ">=0.10.0" 1046 | } 1047 | }, 1048 | "node_modules/ignore-by-default": { 1049 | "version": "1.0.1", 1050 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 1051 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" 1052 | }, 1053 | "node_modules/inherits": { 1054 | "version": "2.0.4", 1055 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1056 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1057 | }, 1058 | "node_modules/ipaddr.js": { 1059 | "version": "1.9.1", 1060 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1061 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 1062 | "engines": { 1063 | "node": ">= 0.10" 1064 | } 1065 | }, 1066 | "node_modules/is-binary-path": { 1067 | "version": "2.1.0", 1068 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1069 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1070 | "dependencies": { 1071 | "binary-extensions": "^2.0.0" 1072 | }, 1073 | "engines": { 1074 | "node": ">=8" 1075 | } 1076 | }, 1077 | "node_modules/is-extglob": { 1078 | "version": "2.1.1", 1079 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1080 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1081 | "engines": { 1082 | "node": ">=0.10.0" 1083 | } 1084 | }, 1085 | "node_modules/is-glob": { 1086 | "version": "4.0.3", 1087 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1088 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1089 | "dependencies": { 1090 | "is-extglob": "^2.1.1" 1091 | }, 1092 | "engines": { 1093 | "node": ">=0.10.0" 1094 | } 1095 | }, 1096 | "node_modules/is-number": { 1097 | "version": "7.0.0", 1098 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1099 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1100 | "engines": { 1101 | "node": ">=0.12.0" 1102 | } 1103 | }, 1104 | "node_modules/math-intrinsics": { 1105 | "version": "1.1.0", 1106 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 1107 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 1108 | "engines": { 1109 | "node": ">= 0.4" 1110 | } 1111 | }, 1112 | "node_modules/media-typer": { 1113 | "version": "0.3.0", 1114 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1115 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 1116 | "engines": { 1117 | "node": ">= 0.6" 1118 | } 1119 | }, 1120 | "node_modules/merge-descriptors": { 1121 | "version": "1.0.3", 1122 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", 1123 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", 1124 | "funding": { 1125 | "url": "https://github.com/sponsors/sindresorhus" 1126 | } 1127 | }, 1128 | "node_modules/methods": { 1129 | "version": "1.1.2", 1130 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1131 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 1132 | "engines": { 1133 | "node": ">= 0.6" 1134 | } 1135 | }, 1136 | "node_modules/mime": { 1137 | "version": "1.6.0", 1138 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1139 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1140 | "bin": { 1141 | "mime": "cli.js" 1142 | }, 1143 | "engines": { 1144 | "node": ">=4" 1145 | } 1146 | }, 1147 | "node_modules/mime-db": { 1148 | "version": "1.52.0", 1149 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1150 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1151 | "engines": { 1152 | "node": ">= 0.6" 1153 | } 1154 | }, 1155 | "node_modules/mime-types": { 1156 | "version": "2.1.35", 1157 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1158 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1159 | "dependencies": { 1160 | "mime-db": "1.52.0" 1161 | }, 1162 | "engines": { 1163 | "node": ">= 0.6" 1164 | } 1165 | }, 1166 | "node_modules/minimatch": { 1167 | "version": "3.1.2", 1168 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1169 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1170 | "dependencies": { 1171 | "brace-expansion": "^1.1.7" 1172 | }, 1173 | "engines": { 1174 | "node": "*" 1175 | } 1176 | }, 1177 | "node_modules/ms": { 1178 | "version": "2.0.0", 1179 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1180 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1181 | }, 1182 | "node_modules/negotiator": { 1183 | "version": "0.6.3", 1184 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1185 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 1186 | "engines": { 1187 | "node": ">= 0.6" 1188 | } 1189 | }, 1190 | "node_modules/nodemon": { 1191 | "version": "3.1.9", 1192 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", 1193 | "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", 1194 | "dependencies": { 1195 | "chokidar": "^3.5.2", 1196 | "debug": "^4", 1197 | "ignore-by-default": "^1.0.1", 1198 | "minimatch": "^3.1.2", 1199 | "pstree.remy": "^1.1.8", 1200 | "semver": "^7.5.3", 1201 | "simple-update-notifier": "^2.0.0", 1202 | "supports-color": "^5.5.0", 1203 | "touch": "^3.1.0", 1204 | "undefsafe": "^2.0.5" 1205 | }, 1206 | "bin": { 1207 | "nodemon": "bin/nodemon.js" 1208 | }, 1209 | "engines": { 1210 | "node": ">=10" 1211 | }, 1212 | "funding": { 1213 | "type": "opencollective", 1214 | "url": "https://opencollective.com/nodemon" 1215 | } 1216 | }, 1217 | "node_modules/nodemon/node_modules/debug": { 1218 | "version": "4.4.0", 1219 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 1220 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 1221 | "dependencies": { 1222 | "ms": "^2.1.3" 1223 | }, 1224 | "engines": { 1225 | "node": ">=6.0" 1226 | }, 1227 | "peerDependenciesMeta": { 1228 | "supports-color": { 1229 | "optional": true 1230 | } 1231 | } 1232 | }, 1233 | "node_modules/nodemon/node_modules/ms": { 1234 | "version": "2.1.3", 1235 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1236 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1237 | }, 1238 | "node_modules/normalize-path": { 1239 | "version": "3.0.0", 1240 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1241 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1242 | "engines": { 1243 | "node": ">=0.10.0" 1244 | } 1245 | }, 1246 | "node_modules/object-inspect": { 1247 | "version": "1.13.4", 1248 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 1249 | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 1250 | "engines": { 1251 | "node": ">= 0.4" 1252 | }, 1253 | "funding": { 1254 | "url": "https://github.com/sponsors/ljharb" 1255 | } 1256 | }, 1257 | "node_modules/on-finished": { 1258 | "version": "2.4.1", 1259 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1260 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1261 | "dependencies": { 1262 | "ee-first": "1.1.1" 1263 | }, 1264 | "engines": { 1265 | "node": ">= 0.8" 1266 | } 1267 | }, 1268 | "node_modules/parseurl": { 1269 | "version": "1.3.3", 1270 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1271 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1272 | "engines": { 1273 | "node": ">= 0.8" 1274 | } 1275 | }, 1276 | "node_modules/path-to-regexp": { 1277 | "version": "0.1.12", 1278 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", 1279 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" 1280 | }, 1281 | "node_modules/picomatch": { 1282 | "version": "2.3.1", 1283 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1284 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1285 | "engines": { 1286 | "node": ">=8.6" 1287 | }, 1288 | "funding": { 1289 | "url": "https://github.com/sponsors/jonschlinkert" 1290 | } 1291 | }, 1292 | "node_modules/prisma": { 1293 | "version": "6.4.1", 1294 | "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.4.1.tgz", 1295 | "integrity": "sha512-q2uJkgXnua/jj66mk6P9bX/zgYJFI/jn4Yp0aS6SPRrjH/n6VyOV7RDe1vHD0DX8Aanx4MvgmUPPoYnR6MJnPg==", 1296 | "hasInstallScript": true, 1297 | "dependencies": { 1298 | "@prisma/engines": "6.4.1", 1299 | "esbuild": ">=0.12 <1", 1300 | "esbuild-register": "3.6.0" 1301 | }, 1302 | "bin": { 1303 | "prisma": "build/index.js" 1304 | }, 1305 | "engines": { 1306 | "node": ">=18.18" 1307 | }, 1308 | "optionalDependencies": { 1309 | "fsevents": "2.3.3" 1310 | }, 1311 | "peerDependencies": { 1312 | "typescript": ">=5.1.0" 1313 | }, 1314 | "peerDependenciesMeta": { 1315 | "typescript": { 1316 | "optional": true 1317 | } 1318 | } 1319 | }, 1320 | "node_modules/prom-client": { 1321 | "version": "15.1.3", 1322 | "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz", 1323 | "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==", 1324 | "dependencies": { 1325 | "@opentelemetry/api": "^1.4.0", 1326 | "tdigest": "^0.1.1" 1327 | }, 1328 | "engines": { 1329 | "node": "^16 || ^18 || >=20" 1330 | } 1331 | }, 1332 | "node_modules/proxy-addr": { 1333 | "version": "2.0.7", 1334 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1335 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1336 | "dependencies": { 1337 | "forwarded": "0.2.0", 1338 | "ipaddr.js": "1.9.1" 1339 | }, 1340 | "engines": { 1341 | "node": ">= 0.10" 1342 | } 1343 | }, 1344 | "node_modules/pstree.remy": { 1345 | "version": "1.1.8", 1346 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 1347 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" 1348 | }, 1349 | "node_modules/qs": { 1350 | "version": "6.13.0", 1351 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 1352 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 1353 | "dependencies": { 1354 | "side-channel": "^1.0.6" 1355 | }, 1356 | "engines": { 1357 | "node": ">=0.6" 1358 | }, 1359 | "funding": { 1360 | "url": "https://github.com/sponsors/ljharb" 1361 | } 1362 | }, 1363 | "node_modules/range-parser": { 1364 | "version": "1.2.1", 1365 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1366 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1367 | "engines": { 1368 | "node": ">= 0.6" 1369 | } 1370 | }, 1371 | "node_modules/raw-body": { 1372 | "version": "2.5.2", 1373 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 1374 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 1375 | "dependencies": { 1376 | "bytes": "3.1.2", 1377 | "http-errors": "2.0.0", 1378 | "iconv-lite": "0.4.24", 1379 | "unpipe": "1.0.0" 1380 | }, 1381 | "engines": { 1382 | "node": ">= 0.8" 1383 | } 1384 | }, 1385 | "node_modules/readdirp": { 1386 | "version": "3.6.0", 1387 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1388 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1389 | "dependencies": { 1390 | "picomatch": "^2.2.1" 1391 | }, 1392 | "engines": { 1393 | "node": ">=8.10.0" 1394 | } 1395 | }, 1396 | "node_modules/safe-buffer": { 1397 | "version": "5.2.1", 1398 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1399 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1400 | "funding": [ 1401 | { 1402 | "type": "github", 1403 | "url": "https://github.com/sponsors/feross" 1404 | }, 1405 | { 1406 | "type": "patreon", 1407 | "url": "https://www.patreon.com/feross" 1408 | }, 1409 | { 1410 | "type": "consulting", 1411 | "url": "https://feross.org/support" 1412 | } 1413 | ] 1414 | }, 1415 | "node_modules/safer-buffer": { 1416 | "version": "2.1.2", 1417 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1418 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1419 | }, 1420 | "node_modules/semver": { 1421 | "version": "7.7.1", 1422 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", 1423 | "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", 1424 | "bin": { 1425 | "semver": "bin/semver.js" 1426 | }, 1427 | "engines": { 1428 | "node": ">=10" 1429 | } 1430 | }, 1431 | "node_modules/send": { 1432 | "version": "0.19.0", 1433 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", 1434 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", 1435 | "dependencies": { 1436 | "debug": "2.6.9", 1437 | "depd": "2.0.0", 1438 | "destroy": "1.2.0", 1439 | "encodeurl": "~1.0.2", 1440 | "escape-html": "~1.0.3", 1441 | "etag": "~1.8.1", 1442 | "fresh": "0.5.2", 1443 | "http-errors": "2.0.0", 1444 | "mime": "1.6.0", 1445 | "ms": "2.1.3", 1446 | "on-finished": "2.4.1", 1447 | "range-parser": "~1.2.1", 1448 | "statuses": "2.0.1" 1449 | }, 1450 | "engines": { 1451 | "node": ">= 0.8.0" 1452 | } 1453 | }, 1454 | "node_modules/send/node_modules/encodeurl": { 1455 | "version": "1.0.2", 1456 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1457 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 1458 | "engines": { 1459 | "node": ">= 0.8" 1460 | } 1461 | }, 1462 | "node_modules/send/node_modules/ms": { 1463 | "version": "2.1.3", 1464 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1465 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1466 | }, 1467 | "node_modules/serve-static": { 1468 | "version": "1.16.2", 1469 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", 1470 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", 1471 | "dependencies": { 1472 | "encodeurl": "~2.0.0", 1473 | "escape-html": "~1.0.3", 1474 | "parseurl": "~1.3.3", 1475 | "send": "0.19.0" 1476 | }, 1477 | "engines": { 1478 | "node": ">= 0.8.0" 1479 | } 1480 | }, 1481 | "node_modules/setprototypeof": { 1482 | "version": "1.2.0", 1483 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1484 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1485 | }, 1486 | "node_modules/side-channel": { 1487 | "version": "1.1.0", 1488 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 1489 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 1490 | "dependencies": { 1491 | "es-errors": "^1.3.0", 1492 | "object-inspect": "^1.13.3", 1493 | "side-channel-list": "^1.0.0", 1494 | "side-channel-map": "^1.0.1", 1495 | "side-channel-weakmap": "^1.0.2" 1496 | }, 1497 | "engines": { 1498 | "node": ">= 0.4" 1499 | }, 1500 | "funding": { 1501 | "url": "https://github.com/sponsors/ljharb" 1502 | } 1503 | }, 1504 | "node_modules/side-channel-list": { 1505 | "version": "1.0.0", 1506 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 1507 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 1508 | "dependencies": { 1509 | "es-errors": "^1.3.0", 1510 | "object-inspect": "^1.13.3" 1511 | }, 1512 | "engines": { 1513 | "node": ">= 0.4" 1514 | }, 1515 | "funding": { 1516 | "url": "https://github.com/sponsors/ljharb" 1517 | } 1518 | }, 1519 | "node_modules/side-channel-map": { 1520 | "version": "1.0.1", 1521 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 1522 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 1523 | "dependencies": { 1524 | "call-bound": "^1.0.2", 1525 | "es-errors": "^1.3.0", 1526 | "get-intrinsic": "^1.2.5", 1527 | "object-inspect": "^1.13.3" 1528 | }, 1529 | "engines": { 1530 | "node": ">= 0.4" 1531 | }, 1532 | "funding": { 1533 | "url": "https://github.com/sponsors/ljharb" 1534 | } 1535 | }, 1536 | "node_modules/side-channel-weakmap": { 1537 | "version": "1.0.2", 1538 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 1539 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 1540 | "dependencies": { 1541 | "call-bound": "^1.0.2", 1542 | "es-errors": "^1.3.0", 1543 | "get-intrinsic": "^1.2.5", 1544 | "object-inspect": "^1.13.3", 1545 | "side-channel-map": "^1.0.1" 1546 | }, 1547 | "engines": { 1548 | "node": ">= 0.4" 1549 | }, 1550 | "funding": { 1551 | "url": "https://github.com/sponsors/ljharb" 1552 | } 1553 | }, 1554 | "node_modules/simple-update-notifier": { 1555 | "version": "2.0.0", 1556 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", 1557 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", 1558 | "dependencies": { 1559 | "semver": "^7.5.3" 1560 | }, 1561 | "engines": { 1562 | "node": ">=10" 1563 | } 1564 | }, 1565 | "node_modules/statuses": { 1566 | "version": "2.0.1", 1567 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1568 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1569 | "engines": { 1570 | "node": ">= 0.8" 1571 | } 1572 | }, 1573 | "node_modules/supports-color": { 1574 | "version": "5.5.0", 1575 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1576 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1577 | "dependencies": { 1578 | "has-flag": "^3.0.0" 1579 | }, 1580 | "engines": { 1581 | "node": ">=4" 1582 | } 1583 | }, 1584 | "node_modules/tdigest": { 1585 | "version": "0.1.2", 1586 | "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", 1587 | "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", 1588 | "dependencies": { 1589 | "bintrees": "1.0.2" 1590 | } 1591 | }, 1592 | "node_modules/to-regex-range": { 1593 | "version": "5.0.1", 1594 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1595 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1596 | "dependencies": { 1597 | "is-number": "^7.0.0" 1598 | }, 1599 | "engines": { 1600 | "node": ">=8.0" 1601 | } 1602 | }, 1603 | "node_modules/toidentifier": { 1604 | "version": "1.0.1", 1605 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1606 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1607 | "engines": { 1608 | "node": ">=0.6" 1609 | } 1610 | }, 1611 | "node_modules/touch": { 1612 | "version": "3.1.1", 1613 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", 1614 | "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", 1615 | "bin": { 1616 | "nodetouch": "bin/nodetouch.js" 1617 | } 1618 | }, 1619 | "node_modules/type-is": { 1620 | "version": "1.6.18", 1621 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1622 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1623 | "dependencies": { 1624 | "media-typer": "0.3.0", 1625 | "mime-types": "~2.1.24" 1626 | }, 1627 | "engines": { 1628 | "node": ">= 0.6" 1629 | } 1630 | }, 1631 | "node_modules/undefsafe": { 1632 | "version": "2.0.5", 1633 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 1634 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" 1635 | }, 1636 | "node_modules/unpipe": { 1637 | "version": "1.0.0", 1638 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1639 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1640 | "engines": { 1641 | "node": ">= 0.8" 1642 | } 1643 | }, 1644 | "node_modules/utils-merge": { 1645 | "version": "1.0.1", 1646 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1647 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1648 | "engines": { 1649 | "node": ">= 0.4.0" 1650 | } 1651 | }, 1652 | "node_modules/vary": { 1653 | "version": "1.1.2", 1654 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1655 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1656 | "engines": { 1657 | "node": ">= 0.8" 1658 | } 1659 | } 1660 | } 1661 | } 1662 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgres-with-prisma", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "npx prisma generate && nodemon src/server.js", 9 | "start": "npx prisma generate && nodemon src/server.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@prisma/client": "^6.4.1", 16 | "dotenv": "^16.4.7", 17 | "express": "^4.21.2", 18 | "nodemon": "^3.1.9", 19 | "prisma": "^6.4.1", 20 | "prom-client": "^15.1.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/prisma/migrations/20250223203943_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Book" ( 3 | "id" SERIAL NOT NULL, 4 | "title" TEXT NOT NULL, 5 | "publisedDate" TIMESTAMP(3) NOT NULL, 6 | 7 | CONSTRAINT "Book_pkey" PRIMARY KEY ("id") 8 | ); 9 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/prisma/migrations/20250223205502_author/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `authorId` to the `Book` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Book" ADD COLUMN "authorId" INTEGER NOT NULL; 9 | 10 | -- CreateTable 11 | CREATE TABLE "Author" ( 12 | "id" SERIAL NOT NULL, 13 | "name" TEXT NOT NULL, 14 | 15 | CONSTRAINT "Author_pkey" PRIMARY KEY ("id") 16 | ); 17 | 18 | -- AddForeignKey 19 | ALTER TABLE "Book" ADD CONSTRAINT "Book_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "Author"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 20 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/prisma/migrations/20250223214459_delete/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Book" DROP CONSTRAINT "Book_authorId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Book" ADD CONSTRAINT "Book_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "Author"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (e.g., Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | generator client { 2 | provider = "prisma-client-js" 3 | } 4 | 5 | datasource db { 6 | provider = "postgresql" 7 | url = env("DATABASE_URL") 8 | } 9 | 10 | model Book { 11 | id Int @id @default(autoincrement()) 12 | title String 13 | publisedDate DateTime 14 | authorId Int 15 | author Author @relation(fields: [authorId], references: [id], onDelete: Cascade) 16 | } 17 | 18 | model Author { 19 | id Int @id @default(autoincrement()) 20 | name String 21 | books Book[] 22 | } 23 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 5s 3 | 4 | scrape_configs: 5 | - job_name: express_prisma_api 6 | static_configs: 7 | - targets: ["express_prisma_api:3000"] 8 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/src/controllers/authorController.js: -------------------------------------------------------------------------------- 1 | const authorService = require("../services/authorService"); 2 | 3 | exports.addAuthor = async (req, res) => { 4 | try { 5 | const { name } = req.body; 6 | const author = await authorService.addAuthor(name); 7 | res.status(201).json(author); 8 | } catch (e) { 9 | res.status(400).json({ error: e.message }); 10 | } 11 | }; 12 | 13 | exports.deleteAuthor = async (req, res) => { 14 | try { 15 | const deletedResult = await authorService.deleteAuthor( 16 | parseInt(req.params.id) 17 | ); 18 | res 19 | .status(200) 20 | .json({ 21 | message: `Author deleted with id ${req.params.id}`, 22 | deletedResult, 23 | }); 24 | } catch (error) { 25 | res.status(400).json({ 26 | error: error.message, 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/src/controllers/bookController.js: -------------------------------------------------------------------------------- 1 | const bookService = require("../services/bookService"); 2 | 3 | exports.addBook = async (req, res) => { 4 | try { 5 | const { title, publisedDate, authorId } = req.body; 6 | const book = await bookService.addBook( 7 | title, 8 | new Date(publisedDate), 9 | authorId 10 | ); 11 | 12 | res.status(201).json(book); 13 | } catch (e) { 14 | res.status(400).json({ error: e.message }); 15 | } 16 | }; 17 | 18 | exports.getAllBooks = async (req, res) => { 19 | try { 20 | const books = await bookService.getAllBooks(); 21 | res.json(books); 22 | } catch (e) { 23 | res.status(500).json({ error: e.message }); 24 | } 25 | }; 26 | 27 | exports.getBookById = async (req, res) => { 28 | try { 29 | const book = await bookService.getBookById(parseInt(req.params.id)); 30 | 31 | if (book) { 32 | res.json(book); 33 | } else { 34 | res.status(404).json({ message: "Book not found" }); 35 | } 36 | } catch (e) { 37 | res.status(500).json({ error: e.message }); 38 | } 39 | }; 40 | 41 | exports.updateBook = async (req, res) => { 42 | try { 43 | const { title } = req.body; 44 | const book = await bookService.updateBook(parseInt(req.params.id), title); 45 | 46 | res.json(book); 47 | } catch (e) { 48 | res.status(400).json({ error: e.message }); 49 | } 50 | }; 51 | 52 | exports.deleteBook = async (req, res) => { 53 | try { 54 | await bookService.deleteBook(parseInt(req.params.id)); 55 | res.json({ message: `Deleted book with id ${req.params.id}` }); 56 | } catch (e) { 57 | res.status(400).json({ error: e.message }); 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/src/routes/authorRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const authorController = require("../controllers/authorController"); 3 | 4 | const router = express.Router(); 5 | 6 | router.post("/add-author", authorController.addAuthor); 7 | router.delete("/:id", authorController.deleteAuthor); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/src/routes/bookRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const bookController = require("../controllers/bookController"); 3 | 4 | const router = express.Router(); 5 | 6 | router.post("/add-new-book", bookController.addBook); 7 | router.get("/get-all-books", bookController.getAllBooks); 8 | router.get("/:id", bookController.getBookById); 9 | router.put("/:id", bookController.updateBook); 10 | router.delete("/:id", bookController.deleteBook); 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/src/server.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const express = require("express"); 3 | const authorRoutes = require("./routes/authorRoutes"); 4 | const bookRoutes = require("./routes/bookRoutes"); 5 | const promClient = require("prom-client"); 6 | 7 | const app = express(); 8 | app.use(express.json()); 9 | 10 | const register = new promClient.Registry(); 11 | promClient.collectDefaultMetrics({ register }); 12 | 13 | const httpRequestsCounter = new promClient.Counter({ 14 | name: "http_requests_total", 15 | help: "Total number of HTTP requests", 16 | labelNames: ["method", "route", "status"], 17 | }); 18 | 19 | register.registerMetric(httpRequestsCounter); 20 | 21 | //Middleware to track API requests 22 | app.use((req, res, next) => { 23 | res.on("finish", () => { 24 | httpRequestsCounter.inc({ 25 | method: req.method, 26 | route: req.path, 27 | status: res.statusCode, 28 | }); 29 | }); 30 | 31 | next(); 32 | }); 33 | 34 | //Expose the /metrics endpoint for prometheus 35 | app.get("/metrics", async (req, res) => { 36 | res.set("Content-Type", register.contentType); 37 | res.end(await register.metrics()); 38 | }); 39 | 40 | app.use("/api/author", authorRoutes); 41 | app.use("/api/book", bookRoutes); 42 | 43 | const PORT = process.env.PORT || 3000; 44 | app.listen(PORT, () => console.log(`Server is now running at port ${PORT}`)); 45 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/src/services/authorService.js: -------------------------------------------------------------------------------- 1 | const { PrismaClient } = require("@prisma/client"); 2 | const prisma = new PrismaClient(); 3 | 4 | async function addAuthor(name) { 5 | try { 6 | const newlyCreatedAuthor = await prisma.author.create({ 7 | data: { 8 | name, 9 | }, 10 | }); 11 | 12 | return newlyCreatedAuthor; 13 | } catch (e) { 14 | console.error(e); 15 | throw e; 16 | } 17 | } 18 | 19 | async function deleteAuthor(id) { 20 | try { 21 | const deletedAuthor = await prisma.author.delete({ 22 | where: { id }, 23 | include: { books: true }, 24 | }); 25 | 26 | return deletedAuthor; 27 | } catch (error) { 28 | throw new Error(error.message); 29 | } 30 | } 31 | 32 | module.exports = { addAuthor, deleteAuthor }; 33 | -------------------------------------------------------------------------------- /postgres/postgres-with-prisma/src/services/bookService.js: -------------------------------------------------------------------------------- 1 | const { PrismaClient } = require("@prisma/client"); 2 | 3 | const prisma = new PrismaClient(); 4 | 5 | async function addBook(title, publisedDate, authorId) { 6 | try { 7 | const newlyCreatedBook = await prisma.book.create({ 8 | data: { 9 | title, 10 | publisedDate, 11 | author: { 12 | connect: { id: authorId }, 13 | }, 14 | }, 15 | include: { author: true }, 16 | }); 17 | 18 | return newlyCreatedBook; 19 | } catch (e) { 20 | console.error(e); 21 | throw e; 22 | } 23 | } 24 | 25 | async function getAllBooks() { 26 | try { 27 | const books = await prisma.book.findMany({ 28 | include: { author: true }, 29 | }); 30 | 31 | return books; 32 | } catch (e) { 33 | throw e; 34 | } 35 | } 36 | 37 | async function getBookById(id) { 38 | try { 39 | const book = await prisma.book.findUnique({ 40 | where: { id }, 41 | include: { author: true }, 42 | }); 43 | 44 | if (!book) { 45 | throw new Error(`Book with id ${id} not found`); 46 | } 47 | 48 | return book; 49 | } catch (e) { 50 | throw e; 51 | } 52 | } 53 | 54 | async function updateBook(id, newTitle) { 55 | try { 56 | // const book = await prisma.book.findUnique({ 57 | // where: { id }, 58 | // include: { author: true }, 59 | // }); 60 | 61 | // if (!book) { 62 | // throw new Error(`Book with id ${id} not found`); 63 | // } 64 | // const updatedBook = await prisma.book.update({ 65 | // where: { id }, 66 | // data: { 67 | // title: newTitle, 68 | // }, 69 | // include: { 70 | // author: true, 71 | // }, 72 | // }); 73 | 74 | // return updatedBook; 75 | 76 | //using transactions 77 | const updatedBook = await prisma.$transaction(async (prisma) => { 78 | const book = await prisma.book.findUnique({ where: { id } }); 79 | if (!book) { 80 | throw new Error(`Book with id ${id} not found`); 81 | } 82 | 83 | return prisma.book.update({ 84 | where: { id }, 85 | data: { 86 | title: newTitle, 87 | }, 88 | include: { 89 | author: true, 90 | }, 91 | }); 92 | }); 93 | 94 | return updatedBook; 95 | } catch (e) { 96 | throw e; 97 | } 98 | } 99 | 100 | async function deleteBook(id) { 101 | try { 102 | const deletedBook = await prisma.book.delete({ 103 | where: { id }, 104 | include: { author: true }, 105 | }); 106 | 107 | return deletedBook; 108 | } catch (e) { 109 | throw error; 110 | } 111 | } 112 | 113 | module.exports = { addBook, getAllBooks, getBookById, updateBook, deleteBook }; 114 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgress-concepts", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "postgress-concepts", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.4.7", 13 | "pg": "^8.13.3" 14 | } 15 | }, 16 | "node_modules/dotenv": { 17 | "version": "16.4.7", 18 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", 19 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", 20 | "engines": { 21 | "node": ">=12" 22 | }, 23 | "funding": { 24 | "url": "https://dotenvx.com" 25 | } 26 | }, 27 | "node_modules/pg": { 28 | "version": "8.13.3", 29 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.3.tgz", 30 | "integrity": "sha512-P6tPt9jXbL9HVu/SSRERNYaYG++MjnscnegFh9pPHihfoBSujsrka0hyuymMzeJKFWrcG8wvCKy8rCe8e5nDUQ==", 31 | "dependencies": { 32 | "pg-connection-string": "^2.7.0", 33 | "pg-pool": "^3.7.1", 34 | "pg-protocol": "^1.7.1", 35 | "pg-types": "^2.1.0", 36 | "pgpass": "1.x" 37 | }, 38 | "engines": { 39 | "node": ">= 8.0.0" 40 | }, 41 | "optionalDependencies": { 42 | "pg-cloudflare": "^1.1.1" 43 | }, 44 | "peerDependencies": { 45 | "pg-native": ">=3.0.1" 46 | }, 47 | "peerDependenciesMeta": { 48 | "pg-native": { 49 | "optional": true 50 | } 51 | } 52 | }, 53 | "node_modules/pg-cloudflare": { 54 | "version": "1.1.1", 55 | "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", 56 | "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", 57 | "optional": true 58 | }, 59 | "node_modules/pg-connection-string": { 60 | "version": "2.7.0", 61 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", 62 | "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==" 63 | }, 64 | "node_modules/pg-int8": { 65 | "version": "1.0.1", 66 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 67 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", 68 | "engines": { 69 | "node": ">=4.0.0" 70 | } 71 | }, 72 | "node_modules/pg-pool": { 73 | "version": "3.7.1", 74 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.1.tgz", 75 | "integrity": "sha512-xIOsFoh7Vdhojas6q3596mXFsR8nwBQBXX5JiV7p9buEVAGqYL4yFzclON5P9vFrpu1u7Zwl2oriyDa89n0wbw==", 76 | "peerDependencies": { 77 | "pg": ">=8.0" 78 | } 79 | }, 80 | "node_modules/pg-protocol": { 81 | "version": "1.7.1", 82 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.1.tgz", 83 | "integrity": "sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ==" 84 | }, 85 | "node_modules/pg-types": { 86 | "version": "2.2.0", 87 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 88 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 89 | "dependencies": { 90 | "pg-int8": "1.0.1", 91 | "postgres-array": "~2.0.0", 92 | "postgres-bytea": "~1.0.0", 93 | "postgres-date": "~1.0.4", 94 | "postgres-interval": "^1.1.0" 95 | }, 96 | "engines": { 97 | "node": ">=4" 98 | } 99 | }, 100 | "node_modules/pgpass": { 101 | "version": "1.0.5", 102 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", 103 | "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", 104 | "dependencies": { 105 | "split2": "^4.1.0" 106 | } 107 | }, 108 | "node_modules/postgres-array": { 109 | "version": "2.0.0", 110 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 111 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", 112 | "engines": { 113 | "node": ">=4" 114 | } 115 | }, 116 | "node_modules/postgres-bytea": { 117 | "version": "1.0.0", 118 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 119 | "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", 120 | "engines": { 121 | "node": ">=0.10.0" 122 | } 123 | }, 124 | "node_modules/postgres-date": { 125 | "version": "1.0.7", 126 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", 127 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", 128 | "engines": { 129 | "node": ">=0.10.0" 130 | } 131 | }, 132 | "node_modules/postgres-interval": { 133 | "version": "1.2.0", 134 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 135 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 136 | "dependencies": { 137 | "xtend": "^4.0.0" 138 | }, 139 | "engines": { 140 | "node": ">=0.10.0" 141 | } 142 | }, 143 | "node_modules/split2": { 144 | "version": "4.2.0", 145 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", 146 | "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", 147 | "engines": { 148 | "node": ">= 10.x" 149 | } 150 | }, 151 | "node_modules/xtend": { 152 | "version": "4.0.2", 153 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 154 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 155 | "engines": { 156 | "node": ">=0.4" 157 | } 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgress-concepts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node src/main.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "dotenv": "^16.4.7", 15 | "pg": "^8.13.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/src/concepts/aggregation.js: -------------------------------------------------------------------------------- 1 | const db = require("../db/db"); 2 | 3 | async function countPostsByUser() { 4 | const countPostsByUserQuery = ` 5 | SELECT users.username, COUNT(posts.id) as post_count 6 | FROM users 7 | LEFT JOIN posts ON users.id = posts.user_id 8 | GROUP BY users.id, users.username 9 | `; 10 | 11 | try { 12 | const res = await db.query(countPostsByUserQuery); 13 | return res.rows; 14 | } catch (e) { 15 | console.error(e); 16 | } 17 | } 18 | 19 | async function averagePostsPerUser() { 20 | const averagePostsPerUserQuery = ` 21 | SELECT AVG(post_count) as average_posts 22 | FROM( 23 | SELECT COUNT(posts.id) as post_count 24 | FROM users 25 | LEFT JOIN posts ON users.id = posts.user_id 26 | GROUP BY users.id 27 | 28 | ) as user_per_counts 29 | `; 30 | try { 31 | const res = await db.query(averagePostsPerUserQuery); 32 | return res.rows; 33 | } catch (e) { 34 | console.error(e); 35 | } 36 | } 37 | 38 | module.exports = { 39 | countPostsByUser, 40 | averagePostsPerUser, 41 | }; 42 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/src/concepts/basic-queries.js: -------------------------------------------------------------------------------- 1 | const db = require("../db/db"); 2 | 3 | async function createUsersTable() { 4 | const createTableQuery = ` 5 | CREATE TABLE IF NOT EXISTS users( 6 | id SERIAL PRIMARY KEY, 7 | username VARCHAR(50) UNIQUE NOT NULL, 8 | email VARCHAR(255) UNIQUE NOT NULL, 9 | created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP 10 | ) 11 | `; 12 | 13 | try { 14 | await db.query(createTableQuery); 15 | console.log("Users table created successfully"); 16 | } catch (error) { 17 | console.error("Error while creating users table", error); 18 | } 19 | } 20 | 21 | async function insertUser(username, email) { 22 | const inserUserQuery = ` 23 | INSERT INTO users (username, email) 24 | VALUES ($1, $2) 25 | RETURNING * 26 | `; 27 | 28 | try { 29 | const res = await db.query(inserUserQuery, [username, email]); 30 | console.log("User inserted successfully", res.rows[0]); 31 | 32 | return res.rows[0]; 33 | } catch (error) { 34 | console.error("Error while creating users table", error); 35 | } 36 | } 37 | 38 | async function fetchAllUsers() { 39 | const getAllUsersFromUsersTable = "SELECT * FROM users"; 40 | 41 | try { 42 | const res = await db.query(getAllUsersFromUsersTable); 43 | console.log("Fetched all users", res); 44 | 45 | return res.rows; 46 | } catch (error) { 47 | console.error("Error", error); 48 | } 49 | } 50 | 51 | // update -> sangam@gmail.com to raj@gmail.com where user name is Sangam Mukherjee 52 | 53 | async function updateUserInfo(username, newEmail) { 54 | const updateUserQuery = ` 55 | UPDATE users 56 | SET email = $2 57 | WHERE username = $1 58 | RETURNING * 59 | `; 60 | 61 | try { 62 | const res = await db.query(updateUserQuery, [username, newEmail]); 63 | 64 | if (res.rows.length > 0) { 65 | console.log("User updated successfully!", res.rows[0]); 66 | return res.rows[0]; 67 | } else { 68 | console.log("No user found with given username"); 69 | return null; 70 | } 71 | } catch (error) { 72 | console.error("Error while creating users table", error); 73 | } 74 | } 75 | 76 | async function deleteInfo(username) { 77 | const deleteQuery = ` 78 | DELETE FROM users 79 | WHERE username = $1 80 | RETURNING * 81 | `; 82 | try { 83 | const res = await db.query(deleteQuery, [username]); 84 | 85 | if (res.rows.length > 0) { 86 | console.log("User deleted successfully!", res.rows[0]); 87 | return res.rows[0]; 88 | } else { 89 | console.log("No user found with given username"); 90 | return null; 91 | } 92 | } catch (error) { 93 | console.error("Error while creating users table", error); 94 | } 95 | } 96 | 97 | module.exports = { 98 | createUsersTable, 99 | insertUser, 100 | fetchAllUsers, 101 | updateUserInfo, 102 | deleteInfo, 103 | }; 104 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/src/concepts/filtering-sorting.js: -------------------------------------------------------------------------------- 1 | const db = require("../db/db"); 2 | 3 | //WHERE clause 4 | 5 | async function getUsersWhere(condition) { 6 | const getUsersQuery = ` 7 | SELECT * FROM users 8 | WHERE ${condition} 9 | `; 10 | 11 | try { 12 | const res = await db.query(getUsersQuery); 13 | return res.rows; 14 | } catch (e) { 15 | console.error(e); 16 | } 17 | } 18 | 19 | async function getSortedUsers(column, order = "ASC") { 20 | const getSortedUsersQuery = ` 21 | SELECT * FROM users 22 | ORDER BY ${column} ${order} 23 | `; 24 | 25 | try { 26 | const result = await db.query(getSortedUsersQuery); 27 | 28 | return result.rows; 29 | } catch (e) { 30 | console.error(e); 31 | } 32 | } 33 | 34 | async function getPaginatedUsers(limit, offset) { 35 | const getPaginatedQuery = ` 36 | SELECT * FROM users 37 | LIMIT $1 OFFSET $2 38 | `; 39 | 40 | try { 41 | const result = await db.query(getPaginatedQuery, [limit, offset]); 42 | 43 | return result.rows; 44 | } catch (e) { 45 | console.error(e); 46 | } 47 | } 48 | 49 | module.exports = { getUsersWhere, getSortedUsers, getPaginatedUsers }; 50 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/src/concepts/joins.js: -------------------------------------------------------------------------------- 1 | const db = require("../db/db"); 2 | 3 | // -> inner join returns only the rows where thers is a match in both tables 4 | 5 | async function getUsersWithPosts() { 6 | const getUsersWithPostsQuery = ` 7 | SELECT users.id, users.username, posts.title 8 | FROM users 9 | INNER JOIN posts ON users.id = posts.user_id 10 | `; 11 | 12 | try { 13 | const res = await db.query(getUsersWithPostsQuery); 14 | return res.rows; 15 | } catch (e) { 16 | console.error(e); 17 | } 18 | } 19 | 20 | async function getAllUsersAndTheirPosts() { 21 | const getAllUsersAndTheirPostsQuery = ` 22 | SELECT users.id, users.username, posts.title 23 | FROM users 24 | LEFT JOIN posts ON users.id = posts.user_id 25 | `; 26 | try { 27 | const res = await db.query(getAllUsersAndTheirPostsQuery); 28 | return res.rows; 29 | } catch (e) { 30 | console.error(e); 31 | } 32 | } 33 | 34 | module.exports = { getUsersWithPosts, getAllUsersAndTheirPosts }; 35 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/src/concepts/relationships.js: -------------------------------------------------------------------------------- 1 | const db = require("../db/db"); 2 | 3 | async function createPostsTable() { 4 | const createTableQuery = ` 5 | CREATE TABLE IF NOT EXISTS posts( 6 | id SERIAL PRIMARY KEY, 7 | title VARCHAR(255) NOT NULL, 8 | content TEXT, 9 | user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, 10 | created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP 11 | ) 12 | `; 13 | 14 | try { 15 | await db.query(createTableQuery); 16 | console.log("Posts table created successfully!"); 17 | } catch (e) { 18 | console.error(e); 19 | } 20 | } 21 | 22 | async function insertNewPost(title, content, userId) { 23 | const insertPostQuery = ` 24 | INSERT INTO posts (title, content, user_id) 25 | VALUES ($1, $2, $3) 26 | RETURNING * 27 | `; 28 | 29 | try { 30 | const result = await db.query(insertPostQuery, [title, content, userId]); 31 | 32 | return result.rows[0]; 33 | } catch (e) {} 34 | } 35 | 36 | module.exports = { 37 | createPostsTable, 38 | insertNewPost, 39 | }; 40 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/src/db/db.js: -------------------------------------------------------------------------------- 1 | const { Pool } = require("pg"); 2 | 3 | require("dotenv").config(); 4 | 5 | //create a new pool instance to manage database connections 6 | // -> postgre -> :// -> [user] -> [password] -> @ -> host:port -> [database] 7 | 8 | const pool = new Pool({ 9 | connectionString: process.env.DATABASE_URL, 10 | }); 11 | 12 | async function query(text, params) { 13 | const start = Date.now(); 14 | 15 | try { 16 | const result = await pool.query(text, params); 17 | 18 | //execute the time -> 19 | const duration = Date.now() - start; 20 | 21 | console.log( 22 | `Executed query: , ${{ text, duration, rows: result.rowCount }}` 23 | ); 24 | 25 | return result; 26 | } catch (e) { 27 | console.error(e); 28 | 29 | throw e; 30 | } 31 | } 32 | 33 | module.exports = { query }; 34 | -------------------------------------------------------------------------------- /postgres/postgress-concepts/src/main.js: -------------------------------------------------------------------------------- 1 | const { 2 | countPostsByUser, 3 | averagePostsPerUser, 4 | } = require("./concepts/aggregation"); 5 | const { 6 | insertUser, 7 | createUsersTable, 8 | fetchAllUsers, 9 | updateUserInfo, 10 | deleteInfo, 11 | } = require("./concepts/basic-queries"); 12 | const { 13 | getUsersWhere, 14 | getSortedUsers, 15 | getPaginatedUsers, 16 | } = require("./concepts/filtering-sorting"); 17 | const { 18 | getUsersWithPosts, 19 | getAllUsersAndTheirPosts, 20 | } = require("./concepts/joins"); 21 | const { createPostsTable, insertNewPost } = require("./concepts/relationships"); 22 | 23 | //test basic queries 24 | async function testBasicQueries() { 25 | try { 26 | await createUsersTable(); 27 | // Insert new users 28 | await insertUser("Zxy", "zxy@gmail.com"); 29 | await insertUser("John Doe", "john@gmail.com"); 30 | await insertUser("Travis Mclaren", "travis123@gmail.com"); 31 | await insertUser("Jennifer Lopez", "jennifer@gmail.com"); 32 | await insertUser("Zayn Malik", "zaynmalik@gmail.com"); 33 | console.log("All users"); 34 | const allUsers = await fetchAllUsers(); 35 | console.log(allUsers); 36 | const updatedUser = await updateUserInfo( 37 | "Sangam Mukherjee", 38 | "raja@gmail.com" 39 | ); 40 | console.log(updatedUser); 41 | 42 | const deletedUser = await deleteInfo("Sangam Mukherjee"); 43 | console.log(deletedUser); 44 | } catch (e) { 45 | console.error("Error", error); 46 | } 47 | } 48 | 49 | async function testFilterAndSortQueries() { 50 | try { 51 | //get users with a username whose username starting with z 52 | // const zFilteredUsers = await getUsersWhere("username LIKE 'Z%'"); 53 | // console.log(zFilteredUsers); 54 | 55 | // const sortedUsers = await getSortedUsers("created_at", "ASC"); 56 | // console.log(sortedUsers); 57 | 58 | const paginatedUsers = await getPaginatedUsers(2, 1); 59 | console.log("paginatedUsers", paginatedUsers); 60 | } catch (e) { 61 | console.error("Error", error); 62 | } 63 | } 64 | 65 | async function testRelationshipQueries() { 66 | try { 67 | // await createPostsTable(); 68 | 69 | await insertNewPost("Second post", "This is my second post", 3); 70 | await insertNewPost("Third post", "Tecond post", 4); 71 | } catch (e) { 72 | console.error("Error", error); 73 | } 74 | } 75 | 76 | async function testJoinQueries() { 77 | try { 78 | // const usersWithPosts = await getUsersWithPosts(); 79 | 80 | // console.log(usersWithPosts); 81 | 82 | const allUsersWithAllPosts = await getAllUsersAndTheirPosts(); 83 | console.log(allUsersWithAllPosts); 84 | } catch (e) { 85 | console.error(e); 86 | } 87 | } 88 | 89 | async function testAggregateQuerise() { 90 | try { 91 | // const postCount = await countPostsByUser(); 92 | // console.log(postCount); 93 | 94 | const averagePostsPerUserInfo = await averagePostsPerUser(); 95 | console.log(averagePostsPerUserInfo); 96 | } catch (e) { 97 | console.error(e); 98 | } 99 | } 100 | 101 | async function testAllQueries() { 102 | // await testBasicQueries(); 103 | // await testFilterAndSortQueries(); 104 | // await testRelationshipQueries(); 105 | 106 | // await testJoinQueries(); 107 | await testAggregateQuerise(); 108 | } 109 | 110 | testAllQueries(); 111 | --------------------------------------------------------------------------------