diff --git a/.gitignore b/.gitignore index f03eaec..fb41c10 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ node_modules #!.yarn/cache .pnp.* + +.env +.env.* diff --git a/package.json b/package.json index d7b04d5..2371128 100644 --- a/package.json +++ b/package.json @@ -3,19 +3,21 @@ "packageManager": "yarn@4.18.0", "type": "module", "scripts": { - "dev": "tsx --watch ./src/index.ts", - "start": "node ./dist/index.js", - "build": "tsc", - "clean": "rm -rf dist", - "compile": "yarn clean && yarn build", - "script": "tsx" + "dev": "tsx --watch ./src/index.ts", + "start": "node ./dist/index.js", + "build": "tsc", + "clean": "rm -rf dist", + "compile": "yarn clean && yarn build", + "script": "tsx" }, "dependencies": { "@jig-software/trest": "2.0.0-beta", "@koa/cors": "^5.0.0", "@koa/router": "^15.7.0", + "dotenv": "^17.4.2", "koa": "^3.2.1", - "koa-bodyparser": "^4.4.1" + "koa-bodyparser": "^4.4.1", + "path": "^0.12.7" }, "devDependencies": { "@types/koa": "^3.0.3", diff --git a/src/configs/index.ts b/src/configs/index.ts new file mode 100644 index 0000000..252cdf9 --- /dev/null +++ b/src/configs/index.ts @@ -0,0 +1,13 @@ +import dotenv from 'dotenv'; +import path from 'path'; + +// 💡 1. 讓 dotenv 去讀取根目錄的 .env 檔案並注入到 process.env +dotenv.config(); + +// 💡 2. 集中導出一個帶有型別的物件 +export const config = { + port: parseInt(process.env.PORT || '3000', 10), + //dbUrl: process.env.DB_URL || 'mongodb://localhost:27017/default', + //jwtSecret: process.env.JWT_SECRET || 'fallback_secret', + //isConfigured: !!process.env.DB_URL +}; diff --git a/src/controllers/userController.ts b/src/controllers/userController.ts new file mode 100644 index 0000000..9126a6c --- /dev/null +++ b/src/controllers/userController.ts @@ -0,0 +1,31 @@ +import type { Context } from "koa"; + +// Simulated database array +const users = [ + { id: 1, name: 'Alice' }, + { id: 2, name: 'Bob' } +]; + +export async function getAllUsers(ctx:Context) { + ctx.body = { success: true, data: users }; +} + +export async function createUser(ctx: Context) { + const body = ctx.request.body; + + if ( + typeof body !== "object" || + body === null || + !("name" in body) || + typeof body.name !== "string" || + body.name.trim() === "" + ) { + ctx.throw(400, 'Name field is required'); + } + + const newUser = { id: users.length + 1, name: body.name }; + users.push(newUser); + + ctx.status = 201; + ctx.body = { success: true, data: newUser }; +} diff --git a/src/index.ts b/src/index.ts index a1f33ed..97f41e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,19 +1,24 @@ import Koa from 'koa'; import cors from '@koa/cors'; import bodyParser from 'koa-bodyparser'; -//import { errorHandler } from './middleware/error'; -//import userRouter from './routes/userRoutes'; +import { errorHandler } from './middleware/error.js'; +import userRouter from './routes/userRoutes.js'; + +import { config } from './configs/index.js'; const app = new Koa(); -const PORT = 3000; +const PORT = config.port || 3000; -//app.use(errorHandler); -app.use(cors()); -app.use(bodyParser()); +// --- Middlewares Stack (Order Matters!) --- -//app.use(userRouter.routes()); -//app.use(userRouter.allowedMethods()); +app.use(errorHandler); // 1. Error interceptor (Outer Onion layer) +app.use(cors()); // 2. Enable Cross-Origin requests +app.use(bodyParser()); // 3. Parse JSON incoming bodies + +// --- Routes Registration --- +app.use(userRouter.routes()); +app.use(userRouter.allowedMethods()); // Handles automatic 405 Method Not Allowed responses app.listen(PORT, () => { - console.log(`🚀 TypeScript Koa server running at http://localhost:${PORT}`); + console.log(`🚀 Koa server running smoothly at http://localhost:${PORT}`); }); diff --git a/src/routes/userRoutes.ts b/src/routes/userRoutes.ts new file mode 100644 index 0000000..5839f10 --- /dev/null +++ b/src/routes/userRoutes.ts @@ -0,0 +1,9 @@ +import Router from '@koa/router'; +import { getAllUsers, createUser } from '../controllers/userController.js'; + +const router = new Router({ prefix: '/api/users' }); + +router.get('/', getAllUsers); +router.post('/', createUser); + +export default router; diff --git a/yarn.lock b/yarn.lock index d7a61fd..db5eeea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -645,6 +645,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^17.4.2": + version: 17.4.2 + resolution: "dotenv@npm:17.4.2" + checksum: 10c0/164f8e77a646c8446867d5b588d26ea6005c8ea7c5eb41cf926f6113d23f2191355f6e0cfd95ea9bab98394a5b0a3f1e51a8399711b666fe55cc7b0bd745f942 + languageName: node + linkType: hard + "dunder-proto@npm:^1.0.1": version: 1.0.1 resolution: "dunder-proto@npm:1.0.1" @@ -1000,6 +1007,13 @@ __metadata: languageName: node linkType: hard +"inherits@npm:2.0.3": + version: 2.0.3 + resolution: "inherits@npm:2.0.3" + checksum: 10c0/6e56402373149ea076a434072671f9982f5fad030c7662be0332122fe6c0fa490acb3cc1010d90b6eff8d640b1167d77674add52dfd1bb85d545cf29e80e73e7 + languageName: node + linkType: hard + "inherits@npm:2.0.4, inherits@npm:~2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" @@ -1286,6 +1300,16 @@ __metadata: languageName: node linkType: hard +"path@npm:^0.12.7": + version: 0.12.7 + resolution: "path@npm:0.12.7" + dependencies: + process: "npm:^0.11.1" + util: "npm:^0.10.3" + checksum: 10c0/f795ce5438a988a590c7b6dfd450ec9baa1c391a8be4c2dea48baa6e0f5b199e56cd83b8c9ebf3991b81bea58236d2c32bdafe2c17a2e70c3a2e4c69891ade59 + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1": version: 2.3.2 resolution: "picomatch@npm:2.3.2" @@ -1307,6 +1331,13 @@ __metadata: languageName: node linkType: hard +"process@npm:^0.11.1": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 + languageName: node + linkType: hard + "pstree.remy@npm:^1.1.8": version: 1.1.8 resolution: "pstree.remy@npm:1.1.8" @@ -1450,9 +1481,11 @@ __metadata: "@types/koa-bodyparser": "npm:^4" "@types/koa__cors": "npm:^5" "@types/node": "npm:^26.4.1" + dotenv: "npm:^17.4.2" koa: "npm:^3.2.1" koa-bodyparser: "npm:^4.4.1" nodemon: "npm:^3.1.14" + path: "npm:^0.12.7" tsx: "npm:^4.23.13" typescript: "npm:^5.9.3" languageName: unknown @@ -1606,6 +1639,15 @@ __metadata: languageName: node linkType: hard +"util@npm:^0.10.3": + version: 0.10.4 + resolution: "util@npm:0.10.4" + dependencies: + inherits: "npm:2.0.3" + checksum: 10c0/d29f6893e406b63b088ce9924da03201df89b31490d4d011f1c07a386ea4b3dbe907464c274023c237da470258e1805d806c7e4009a5974cd6b1d474b675852a + languageName: node + linkType: hard + "vary@npm:^1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2"