@neevjs/server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/controllers/authController.d.ts +6 -0
- package/dist/controllers/authController.d.ts.map +1 -0
- package/dist/controllers/authController.js +52 -0
- package/dist/controllers/authController.js.map +1 -0
- package/dist/controllers/modelController.d.ts +9 -0
- package/dist/controllers/modelController.d.ts.map +1 -0
- package/dist/controllers/modelController.js +47 -0
- package/dist/controllers/modelController.js.map +1 -0
- package/dist/db/index.d.ts +16 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +72 -0
- package/dist/db/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +4 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +35 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/jwt.d.ts +9 -0
- package/dist/middleware/jwt.d.ts.map +1 -0
- package/dist/middleware/jwt.js +22 -0
- package/dist/middleware/jwt.js.map +1 -0
- package/dist/middleware/response.d.ts +8 -0
- package/dist/middleware/response.d.ts.map +1 -0
- package/dist/middleware/response.js +24 -0
- package/dist/middleware/response.js.map +1 -0
- package/dist/routes/auth.d.ts +4 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +12 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/modelRouter.d.ts +3 -0
- package/dist/routes/modelRouter.d.ts.map +1 -0
- package/dist/routes/modelRouter.js +22 -0
- package/dist/routes/modelRouter.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +37 -0
- package/dist/server.js.map +1 -0
- package/package.json +36 -0
- package/src/controllers/authController.ts +71 -0
- package/src/controllers/modelController.ts +50 -0
- package/src/db/index.ts +77 -0
- package/src/middleware/auth.ts +34 -0
- package/src/middleware/jwt.ts +24 -0
- package/src/middleware/response.ts +28 -0
- package/src/routes/auth.ts +12 -0
- package/src/routes/modelRouter.ts +23 -0
- package/src/server.ts +40 -0
- package/src/types/express.d.ts +11 -0
- package/tsconfig.json +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Rahul7raj/neevjs/main/docs/logo-inline.png" alt="NeevJS" height="80" />
|
|
3
|
+
<br />
|
|
4
|
+
<br />
|
|
5
|
+
<a href="https://github.com/Rahul7raj/neevjs"><strong>GitHub Repository</strong></a> | <a href="https://Rahul7raj.github.io/neevjs"><strong>Documentation</strong></a>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<br />
|
|
9
|
+
|
|
10
|
+
# @neevjs/server
|
|
11
|
+
|
|
12
|
+
> The optional Node.js + Express backend that works out-of-the-box with NeevJS.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @neevjs/server
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import express from 'express'
|
|
24
|
+
// Note: In a real project, you would import the router/controllers from your compiled server code.
|
|
25
|
+
// The @neevjs/server package provides standard backend routes and middleware.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Docs
|
|
29
|
+
|
|
30
|
+
Full documentation at [Rahul7raj.github.io/neevjs](https://Rahul7raj.github.io/neevjs)
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
MIT — Rahul Raj Kushwaha
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Request, Response } from 'express';
|
|
2
|
+
export declare function login(req: Request, res: Response): void;
|
|
3
|
+
export declare function register(req: Request, res: Response): void;
|
|
4
|
+
export declare function me(req: Request, res: Response): void;
|
|
5
|
+
export declare function logout(_req: Request, res: Response): void;
|
|
6
|
+
//# sourceMappingURL=authController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authController.d.ts","sourceRoot":"","sources":["../../src/controllers/authController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAahD,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAmBvD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAuB1D;AAED,wBAAgB,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAMpD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAGzD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.login = login;
|
|
4
|
+
exports.register = register;
|
|
5
|
+
exports.me = me;
|
|
6
|
+
exports.logout = logout;
|
|
7
|
+
const db_1 = require("../db");
|
|
8
|
+
const jwt_1 = require("../middleware/jwt");
|
|
9
|
+
const response_1 = require("../middleware/response");
|
|
10
|
+
function login(req, res) {
|
|
11
|
+
const { email, password } = req.body;
|
|
12
|
+
if (!email || !password) {
|
|
13
|
+
(0, response_1.sendError)(res, 'Email and password are required');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const user = db_1.db.findBy('auth_users', 'email', email);
|
|
17
|
+
if (!user || user.password !== password) {
|
|
18
|
+
(0, response_1.sendError)(res, 'Invalid credentials', 401);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const { password: _pw, ...safeUser } = user;
|
|
22
|
+
const token = (0, jwt_1.generateToken)(safeUser);
|
|
23
|
+
(0, response_1.sendSuccess)(res, { user: safeUser, token });
|
|
24
|
+
}
|
|
25
|
+
function register(req, res) {
|
|
26
|
+
const { email, password, name } = req.body;
|
|
27
|
+
if (!email || !password) {
|
|
28
|
+
(0, response_1.sendError)(res, 'Email and password are required');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const existing = db_1.db.findBy('auth_users', 'email', email);
|
|
32
|
+
if (existing) {
|
|
33
|
+
(0, response_1.sendError)(res, 'Email already registered', 409);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const created = db_1.db.insert('auth_users', { email, password, name, role: 'user' });
|
|
37
|
+
const { password: _pw, ...safeUser } = created;
|
|
38
|
+
const token = (0, jwt_1.generateToken)(safeUser);
|
|
39
|
+
(0, response_1.sendSuccess)(res, { user: safeUser, token }, {}, 201);
|
|
40
|
+
}
|
|
41
|
+
function me(req, res) {
|
|
42
|
+
if (!req.user) {
|
|
43
|
+
(0, response_1.sendError)(res, 'Unauthorized', 401);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
(0, response_1.sendSuccess)(res, req.user);
|
|
47
|
+
}
|
|
48
|
+
function logout(_req, res) {
|
|
49
|
+
// JWT is stateless — client just drops the token
|
|
50
|
+
(0, response_1.sendSuccess)(res, { message: 'Logged out successfully' });
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=authController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authController.js","sourceRoot":"","sources":["../../src/controllers/authController.ts"],"names":[],"mappings":";;AAaA,sBAmBC;AAED,4BAuBC;AAED,gBAMC;AAED,wBAGC;AArED,8BAA0B;AAC1B,2CAAiD;AACjD,qDAA+D;AAU/D,SAAgB,KAAK,CAAC,GAAY,EAAE,GAAa;IAC/C,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAA6C,CAAA;IAE7E,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,IAAA,oBAAS,EAAC,GAAG,EAAE,iCAAiC,CAAC,CAAA;QACjD,OAAM;IACR,CAAC;IAED,MAAM,IAAI,GAAG,OAAE,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,CAA+B,CAAA;IAElF,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACxC,IAAA,oBAAS,EAAC,GAAG,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAAA;QAC1C,OAAM;IACR,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAA;IAC3C,MAAM,KAAK,GAAG,IAAA,mBAAa,EAAC,QAAoB,CAAC,CAAA;IAEjD,IAAA,sBAAW,EAAC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;AAC7C,CAAC;AAED,SAAgB,QAAQ,CAAC,GAAY,EAAE,GAAa;IAClD,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,IAIrC,CAAA;IAED,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,IAAA,oBAAS,EAAC,GAAG,EAAE,iCAAiC,CAAC,CAAA;QACjD,OAAM;IACR,CAAC;IAED,MAAM,QAAQ,GAAG,OAAE,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;IACxD,IAAI,QAAQ,EAAE,CAAC;QACb,IAAA,oBAAS,EAAC,GAAG,EAAE,0BAA0B,EAAE,GAAG,CAAC,CAAA;QAC/C,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,OAAE,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAmB,CAAA;IAClG,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAA;IAC9C,MAAM,KAAK,GAAG,IAAA,mBAAa,EAAC,QAAoB,CAAC,CAAA;IAEjD,IAAA,sBAAW,EAAC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;AACtD,CAAC;AAED,SAAgB,EAAE,CAAC,GAAY,EAAE,GAAa;IAC5C,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,IAAA,oBAAS,EAAC,GAAG,EAAE,cAAc,EAAE,GAAG,CAAC,CAAA;QACnC,OAAM;IACR,CAAC;IACD,IAAA,sBAAW,EAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED,SAAgB,MAAM,CAAC,IAAa,EAAE,GAAa;IACjD,iDAAiD;IACjD,IAAA,sBAAW,EAAC,GAAG,EAAE,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAA;AAC1D,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Request, Response } from 'express';
|
|
2
|
+
export declare function createModelController(tableName: string): {
|
|
3
|
+
index(req: Request, res: Response): void;
|
|
4
|
+
show(req: Request, res: Response): void;
|
|
5
|
+
store(req: Request, res: Response): void;
|
|
6
|
+
update(req: Request, res: Response): void;
|
|
7
|
+
destroy(req: Request, res: Response): void;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=modelController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelController.d.ts","sourceRoot":"","sources":["../../src/controllers/modelController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAKhD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM;eAExC,OAAO,OAAO,QAAQ,GAAG,IAAI;cAS9B,OAAO,OAAO,QAAQ,GAAG,IAAI;eAS5B,OAAO,OAAO,QAAQ,GAAG,IAAI;gBAM5B,OAAO,OAAO,QAAQ,GAAG,IAAI;iBAS5B,OAAO,OAAO,QAAQ,GAAG,IAAI;EAS7C"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createModelController = createModelController;
|
|
4
|
+
const db_1 = require("../db");
|
|
5
|
+
const response_1 = require("../middleware/response");
|
|
6
|
+
function createModelController(tableName) {
|
|
7
|
+
return {
|
|
8
|
+
index(req, res) {
|
|
9
|
+
const all = db_1.db.findAll(tableName);
|
|
10
|
+
const page = parseInt(String(req.query['page'] ?? '1'), 10);
|
|
11
|
+
const perPage = parseInt(String(req.query['perPage'] ?? '20'), 10);
|
|
12
|
+
const start = (page - 1) * perPage;
|
|
13
|
+
const paginated = all.slice(start, start + perPage);
|
|
14
|
+
(0, response_1.sendSuccess)(res, paginated, (0, response_1.paginateMeta)(all.length, page, perPage));
|
|
15
|
+
},
|
|
16
|
+
show(req, res) {
|
|
17
|
+
const record = db_1.db.findById(tableName, req.params['id'] ?? '');
|
|
18
|
+
if (!record) {
|
|
19
|
+
(0, response_1.sendError)(res, `${tableName} not found`, 404);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
(0, response_1.sendSuccess)(res, record);
|
|
23
|
+
},
|
|
24
|
+
store(req, res) {
|
|
25
|
+
const body = req.body;
|
|
26
|
+
const record = db_1.db.insert(tableName, body);
|
|
27
|
+
(0, response_1.sendSuccess)(res, record, {}, 201);
|
|
28
|
+
},
|
|
29
|
+
update(req, res) {
|
|
30
|
+
const record = db_1.db.update(tableName, req.params['id'] ?? '', req.body);
|
|
31
|
+
if (!record) {
|
|
32
|
+
(0, response_1.sendError)(res, `${tableName} not found`, 404);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
(0, response_1.sendSuccess)(res, record);
|
|
36
|
+
},
|
|
37
|
+
destroy(req, res) {
|
|
38
|
+
const deleted = db_1.db.delete(tableName, req.params['id'] ?? '');
|
|
39
|
+
if (!deleted) {
|
|
40
|
+
(0, response_1.sendError)(res, `${tableName} not found`, 404);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
(0, response_1.sendSuccess)(res, { message: `${tableName} deleted` });
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=modelController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelController.js","sourceRoot":"","sources":["../../src/controllers/modelController.ts"],"names":[],"mappings":";;AAKA,sDA4CC;AAhDD,8BAA0B;AAC1B,qDAA6E;AAG7E,SAAgB,qBAAqB,CAAC,SAAiB;IACrD,OAAO;QACL,KAAK,CAAC,GAAY,EAAE,GAAa;YAC/B,MAAM,GAAG,GAAG,OAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;YACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC,CAAA;YAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;YAClE,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,CAAA;YAClC,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,CAAA;YACnD,IAAA,sBAAW,EAAC,GAAG,EAAE,SAAS,EAAE,IAAA,uBAAY,EAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;QACtE,CAAC;QAED,IAAI,CAAC,GAAY,EAAE,GAAa;YAC9B,MAAM,MAAM,GAAG,OAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;YAC7D,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAA,oBAAS,EAAC,GAAG,EAAE,GAAG,SAAS,YAAY,EAAE,GAAG,CAAC,CAAA;gBAC7C,OAAM;YACR,CAAC;YACD,IAAA,sBAAW,EAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC1B,CAAC;QAED,KAAK,CAAC,GAAY,EAAE,GAAa;YAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,IAA+B,CAAA;YAChD,MAAM,MAAM,GAAG,OAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;YACzC,IAAA,sBAAW,EAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;QACnC,CAAC;QAED,MAAM,CAAC,GAAY,EAAE,GAAa;YAChC,MAAM,MAAM,GAAG,OAAE,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,IAA4B,CAAC,CAAA;YAC7F,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAA,oBAAS,EAAC,GAAG,EAAE,GAAG,SAAS,YAAY,EAAE,GAAG,CAAC,CAAA;gBAC7C,OAAM;YACR,CAAC;YACD,IAAA,sBAAW,EAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC1B,CAAC;QAED,OAAO,CAAC,GAAY,EAAE,GAAa;YACjC,MAAM,OAAO,GAAG,OAAE,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;YAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,IAAA,oBAAS,EAAC,GAAG,EAAE,GAAG,SAAS,YAAY,EAAE,GAAG,CAAC,CAAA;gBAC7C,OAAM;YACR,CAAC;YACD,IAAA,sBAAW,EAAC,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,SAAS,UAAU,EAAE,CAAC,CAAA;QACvD,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ModelRecord } from '@neevjs/shared';
|
|
2
|
+
declare class InMemoryDB {
|
|
3
|
+
private store;
|
|
4
|
+
private counters;
|
|
5
|
+
private nextId;
|
|
6
|
+
seed(table: string, rows: ModelRecord[]): void;
|
|
7
|
+
findAll(table: string): ModelRecord[];
|
|
8
|
+
findById(table: string, id: number | string): ModelRecord | undefined;
|
|
9
|
+
findBy(table: string, key: string, value: unknown): ModelRecord | undefined;
|
|
10
|
+
insert(table: string, data: Omit<ModelRecord, 'id'>): ModelRecord;
|
|
11
|
+
update(table: string, id: number | string, data: Partial<ModelRecord>): ModelRecord | null;
|
|
12
|
+
delete(table: string, id: number | string): boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare const db: InMemoryDB;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD,cAAM,UAAU;IACd,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,QAAQ,CAA4B;IAE5C,OAAO,CAAC,MAAM;IAOd,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,IAAI;IAM9C,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE;IAIrC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS;IAIrE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS;IAI3E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG,WAAW;IAQjE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,GAAG,IAAI;IAS1F,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO;CAQpD;AAED,eAAO,MAAM,EAAE,YAAmB,CAAA"}
|
package/dist/db/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.db = void 0;
|
|
4
|
+
// Simple in-memory store — replace with PostgreSQL/SQLite in production
|
|
5
|
+
class InMemoryDB {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.store = new Map();
|
|
8
|
+
this.counters = new Map();
|
|
9
|
+
}
|
|
10
|
+
nextId(table) {
|
|
11
|
+
const current = this.counters.get(table) ?? 0;
|
|
12
|
+
const next = current + 1;
|
|
13
|
+
this.counters.set(table, next);
|
|
14
|
+
return next;
|
|
15
|
+
}
|
|
16
|
+
seed(table, rows) {
|
|
17
|
+
this.store.set(table, rows);
|
|
18
|
+
const maxId = rows.reduce((m, r) => Math.max(m, Number(r.id ?? 0)), 0);
|
|
19
|
+
this.counters.set(table, maxId);
|
|
20
|
+
}
|
|
21
|
+
findAll(table) {
|
|
22
|
+
return this.store.get(table) ?? [];
|
|
23
|
+
}
|
|
24
|
+
findById(table, id) {
|
|
25
|
+
return (this.store.get(table) ?? []).find((r) => String(r.id) === String(id));
|
|
26
|
+
}
|
|
27
|
+
findBy(table, key, value) {
|
|
28
|
+
return (this.store.get(table) ?? []).find((r) => r[key] === value);
|
|
29
|
+
}
|
|
30
|
+
insert(table, data) {
|
|
31
|
+
const rows = this.store.get(table) ?? [];
|
|
32
|
+
const record = { id: this.nextId(table), ...data };
|
|
33
|
+
rows.push(record);
|
|
34
|
+
this.store.set(table, rows);
|
|
35
|
+
return record;
|
|
36
|
+
}
|
|
37
|
+
update(table, id, data) {
|
|
38
|
+
const rows = this.store.get(table) ?? [];
|
|
39
|
+
const index = rows.findIndex((r) => String(r.id) === String(id));
|
|
40
|
+
if (index === -1)
|
|
41
|
+
return null;
|
|
42
|
+
rows[index] = { ...rows[index], ...data };
|
|
43
|
+
this.store.set(table, rows);
|
|
44
|
+
return rows[index];
|
|
45
|
+
}
|
|
46
|
+
delete(table, id) {
|
|
47
|
+
const rows = this.store.get(table) ?? [];
|
|
48
|
+
const index = rows.findIndex((r) => String(r.id) === String(id));
|
|
49
|
+
if (index === -1)
|
|
50
|
+
return false;
|
|
51
|
+
rows.splice(index, 1);
|
|
52
|
+
this.store.set(table, rows);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.db = new InMemoryDB();
|
|
57
|
+
// Seed default tables
|
|
58
|
+
exports.db.seed('users', [
|
|
59
|
+
{ id: 1, name: 'Rahul Kushwaha', email: 'rahul@example.com', role: 'admin' },
|
|
60
|
+
{ id: 2, name: 'Priya Sharma', email: 'priya@example.com', role: 'user' },
|
|
61
|
+
]);
|
|
62
|
+
exports.db.seed('auth_users', [
|
|
63
|
+
{
|
|
64
|
+
id: 1,
|
|
65
|
+
name: 'Admin',
|
|
66
|
+
email: 'admin@neevjs.dev',
|
|
67
|
+
// In production use bcrypt — for demo plain text
|
|
68
|
+
password: 'admin123',
|
|
69
|
+
role: 'admin',
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
export declare function authMiddleware(req: Request, res: Response, next: NextFunction): void;
|
|
3
|
+
export declare function requireRole(role: string): (req: Request, res: Response, next: NextFunction) => void;
|
|
4
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAG9D,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAoBpF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,IAC9B,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAO/D"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.authMiddleware = authMiddleware;
|
|
4
|
+
exports.requireRole = requireRole;
|
|
5
|
+
const jwt_1 = require("./jwt");
|
|
6
|
+
function authMiddleware(req, res, next) {
|
|
7
|
+
const authHeader = req.headers.authorization;
|
|
8
|
+
const token = authHeader?.split(' ')[1];
|
|
9
|
+
if (!token) {
|
|
10
|
+
res.status(401).json({ data: null, meta: {}, error: 'Unauthorized — no token provided' });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const decoded = (0, jwt_1.verifyToken)(token);
|
|
15
|
+
req.user = {
|
|
16
|
+
id: decoded.id,
|
|
17
|
+
email: decoded.email,
|
|
18
|
+
role: decoded.role,
|
|
19
|
+
};
|
|
20
|
+
next();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
res.status(401).json({ data: null, meta: {}, error: 'Unauthorized — invalid or expired token' });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function requireRole(role) {
|
|
27
|
+
return (req, res, next) => {
|
|
28
|
+
if (req.user?.role !== role) {
|
|
29
|
+
res.status(403).json({ data: null, meta: {}, error: 'Forbidden — insufficient permissions' });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
next();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":";;AAGA,wCAoBC;AAED,kCAQC;AAhCD,+BAAmC;AAEnC,SAAgB,cAAc,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC5E,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAA;IAC5C,MAAM,KAAK,GAAG,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAEvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC,CAAA;QACzF,OAAM;IACR,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,iBAAW,EAAC,KAAK,CAAC,CAAA;QAClC,GAAG,CAAC,IAAI,GAAG;YACT,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAA;QACD,IAAI,EAAE,CAAA;IACR,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC,CAAA;IAClG,CAAC;AACH,CAAC;AAED,SAAgB,WAAW,CAAC,IAAY;IACtC,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/D,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;YAC5B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC,CAAA;YAC7F,OAAM;QACR,CAAC;QACD,IAAI,EAAE,CAAA;IACR,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AuthUser } from '@neevjs/shared';
|
|
2
|
+
export interface JwtPayload {
|
|
3
|
+
id: number | string;
|
|
4
|
+
email: string;
|
|
5
|
+
role?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function generateToken(user: AuthUser): string;
|
|
8
|
+
export declare function verifyToken(token: string): JwtPayload;
|
|
9
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/middleware/jwt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAK9C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAOpD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAErD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateToken = generateToken;
|
|
7
|
+
exports.verifyToken = verifyToken;
|
|
8
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
9
|
+
const SECRET = process.env['JWT_SECRET'] ?? 'neevjs-secret-change-in-production';
|
|
10
|
+
const EXPIRES_IN = '7d';
|
|
11
|
+
function generateToken(user) {
|
|
12
|
+
const payload = {
|
|
13
|
+
id: user.id,
|
|
14
|
+
email: user.email,
|
|
15
|
+
role: typeof user.role === 'string' ? user.role : undefined,
|
|
16
|
+
};
|
|
17
|
+
return jsonwebtoken_1.default.sign(payload, SECRET, { expiresIn: EXPIRES_IN });
|
|
18
|
+
}
|
|
19
|
+
function verifyToken(token) {
|
|
20
|
+
return jsonwebtoken_1.default.verify(token, SECRET);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/middleware/jwt.ts"],"names":[],"mappings":";;;;;AAYA,sCAOC;AAED,kCAEC;AAvBD,gEAA8B;AAG9B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,oCAAoC,CAAA;AAChF,MAAM,UAAU,GAAG,IAAI,CAAA;AAQvB,SAAgB,aAAa,CAAC,IAAc;IAC1C,MAAM,OAAO,GAAe;QAC1B,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAA;IACD,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAA;AAC7D,CAAC;AAED,SAAgB,WAAW,CAAC,KAAa;IACvC,OAAO,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAe,CAAA;AAChD,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
import type { Pagination } from '@neevjs/shared';
|
|
3
|
+
export declare function sendSuccess<T>(res: Response, data: T, meta?: Record<string, unknown>, status?: number): void;
|
|
4
|
+
export declare function sendError(res: Response, message: string, status?: number): void;
|
|
5
|
+
export declare function paginateMeta(total: number, page: number, perPage: number): {
|
|
6
|
+
pagination: Pagination;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/middleware/response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE7D,wBAAgB,WAAW,CAAC,CAAC,EAC3B,GAAG,EAAE,QAAQ,EACb,IAAI,EAAE,CAAC,EACP,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,MAAM,SAAM,GACX,IAAI,CAGN;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,IAAI,CAG5E;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,UAAU,CAAA;CAAE,CASrG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sendSuccess = sendSuccess;
|
|
4
|
+
exports.sendError = sendError;
|
|
5
|
+
exports.paginateMeta = paginateMeta;
|
|
6
|
+
function sendSuccess(res, data, meta = {}, status = 200) {
|
|
7
|
+
const body = { data, meta, error: null };
|
|
8
|
+
res.status(status).json(body);
|
|
9
|
+
}
|
|
10
|
+
function sendError(res, message, status = 400) {
|
|
11
|
+
const body = { data: null, meta: {}, error: message };
|
|
12
|
+
res.status(status).json(body);
|
|
13
|
+
}
|
|
14
|
+
function paginateMeta(total, page, perPage) {
|
|
15
|
+
return {
|
|
16
|
+
pagination: {
|
|
17
|
+
page,
|
|
18
|
+
perPage,
|
|
19
|
+
total,
|
|
20
|
+
lastPage: Math.ceil(total / perPage),
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../src/middleware/response.ts"],"names":[],"mappings":";;AAGA,kCAQC;AAED,8BAGC;AAED,oCASC;AAxBD,SAAgB,WAAW,CACzB,GAAa,EACb,IAAO,EACP,OAAgC,EAAE,EAClC,MAAM,GAAG,GAAG;IAEZ,MAAM,IAAI,GAAmB,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACxD,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC/B,CAAC;AAED,SAAgB,SAAS,CAAC,GAAa,EAAE,OAAe,EAAE,MAAM,GAAG,GAAG;IACpE,MAAM,IAAI,GAAsB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IACxE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC/B,CAAC;AAED,SAAgB,YAAY,CAAC,KAAa,EAAE,IAAY,EAAE,OAAe;IACvE,OAAO;QACL,UAAU,EAAE;YACV,IAAI;YACJ,OAAO;YACP,KAAK;YACL,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;SACrC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAIhC,QAAA,MAAM,MAAM,EAAE,MAAiB,CAAA;AAO/B,eAAe,MAAM,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const express_1 = require("express");
|
|
4
|
+
const authController_1 = require("../controllers/authController");
|
|
5
|
+
const auth_1 = require("../middleware/auth");
|
|
6
|
+
const router = (0, express_1.Router)();
|
|
7
|
+
router.post('/login', authController_1.login);
|
|
8
|
+
router.post('/register', authController_1.register);
|
|
9
|
+
router.get('/me', auth_1.authMiddleware, authController_1.me);
|
|
10
|
+
router.post('/logout', auth_1.authMiddleware, authController_1.logout);
|
|
11
|
+
exports.default = router;
|
|
12
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":";;AAAA,qCAAgC;AAChC,kEAA2E;AAC3E,6CAAmD;AAEnD,MAAM,MAAM,GAAW,IAAA,gBAAM,GAAE,CAAA;AAE/B,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,sBAAK,CAAC,CAAA;AAC5B,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,yBAAQ,CAAC,CAAA;AAClC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,qBAAc,EAAE,mBAAE,CAAC,CAAA;AACrC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAc,EAAE,uBAAM,CAAC,CAAA;AAE9C,kBAAe,MAAM,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelRouter.d.ts","sourceRoot":"","sources":["../../src/routes/modelRouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAGhC,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAmB/E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createModelRouter = createModelRouter;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
const modelController_1 = require("../controllers/modelController");
|
|
6
|
+
function createModelRouter(tableName, protected_ = false) {
|
|
7
|
+
const router = (0, express_1.Router)();
|
|
8
|
+
const ctrl = (0, modelController_1.createModelController)(tableName);
|
|
9
|
+
// Lazy import to avoid circular dep — only import when protected routes needed
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
11
|
+
const { authMiddleware } = protected_
|
|
12
|
+
? require('../middleware/auth')
|
|
13
|
+
: { authMiddleware: null };
|
|
14
|
+
const guard = protected_ && authMiddleware ? [authMiddleware] : [];
|
|
15
|
+
router.get('/', ...guard, ctrl.index);
|
|
16
|
+
router.get('/:id', ...guard, ctrl.show);
|
|
17
|
+
router.post('/', ...guard, ctrl.store);
|
|
18
|
+
router.put('/:id', ...guard, ctrl.update);
|
|
19
|
+
router.delete('/:id', ...guard, ctrl.destroy);
|
|
20
|
+
return router;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=modelRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelRouter.js","sourceRoot":"","sources":["../../src/routes/modelRouter.ts"],"names":[],"mappings":";;AAGA,8CAmBC;AAtBD,qCAAgC;AAChC,oEAAsE;AAEtE,SAAgB,iBAAiB,CAAC,SAAiB,EAAE,UAAU,GAAG,KAAK;IACrE,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAA;IACvB,MAAM,IAAI,GAAG,IAAA,uCAAqB,EAAC,SAAS,CAAC,CAAA;IAE7C,+EAA+E;IAC/E,8DAA8D;IAC9D,MAAM,EAAE,cAAc,EAAE,GAAG,UAAU;QACnC,CAAC,CAAE,OAAO,CAAC,oBAAoB,CAAyI;QACxK,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAA;IAE5B,MAAM,KAAK,GAAG,UAAU,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAElE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IACrC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;IACvC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IACtC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACzC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAE7C,OAAO,MAAM,CAAA;AACf,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAK9C,QAAA,MAAM,GAAG,EAAE,WAAuB,CAAA;AAkClC,eAAe,GAAG,CAAA"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const cors_1 = __importDefault(require("cors"));
|
|
8
|
+
const auth_1 = __importDefault(require("./routes/auth"));
|
|
9
|
+
const modelRouter_1 = require("./routes/modelRouter");
|
|
10
|
+
const app = (0, express_1.default)();
|
|
11
|
+
const PORT = process.env['PORT'] ?? 3001;
|
|
12
|
+
// ─── Middleware ───────────────────────────────────────────────────────────────
|
|
13
|
+
app.use((0, cors_1.default)({ origin: '*' }));
|
|
14
|
+
app.use(express_1.default.json());
|
|
15
|
+
// ─── Health check ─────────────────────────────────────────────────────────────
|
|
16
|
+
app.get('/api/health', (_req, res) => {
|
|
17
|
+
res.json({ data: { status: 'ok', framework: 'NeevJS Server v0.0.1' }, meta: {}, error: null });
|
|
18
|
+
});
|
|
19
|
+
// ─── Auth routes ──────────────────────────────────────────────────────────────
|
|
20
|
+
app.use('/api/auth', auth_1.default);
|
|
21
|
+
// ─── Auto-registered model routes ─────────────────────────────────────────────
|
|
22
|
+
// Add your models here. Set second arg to true to require authentication.
|
|
23
|
+
app.use('/api/users', (0, modelRouter_1.createModelRouter)('users', false));
|
|
24
|
+
// Example of a protected route:
|
|
25
|
+
// app.use('/api/orders', createModelRouter('orders', true))
|
|
26
|
+
// ─── 404 handler ──────────────────────────────────────────────────────────────
|
|
27
|
+
app.use((_req, res) => {
|
|
28
|
+
res.status(404).json({ data: null, meta: {}, error: 'Route not found' });
|
|
29
|
+
});
|
|
30
|
+
// ─── Start ────────────────────────────────────────────────────────────────────
|
|
31
|
+
app.listen(PORT, () => {
|
|
32
|
+
console.log(`\n🚀 NeevJS Server running at http://localhost:${PORT}`);
|
|
33
|
+
console.log(` API: http://localhost:${PORT}/api`);
|
|
34
|
+
console.log(` Health: http://localhost:${PORT}/api/health\n`);
|
|
35
|
+
});
|
|
36
|
+
exports.default = app;
|
|
37
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;;;AAAA,sDAA8C;AAC9C,gDAAuB;AACvB,yDAAsC;AACtC,sDAAwD;AAExD,MAAM,GAAG,GAAgB,IAAA,iBAAO,GAAE,CAAA;AAClC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAA;AAExC,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,EAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;AAC9B,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAA;AAEvB,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACnC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,sBAAsB,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AAChG,CAAC,CAAC,CAAA;AAEF,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,cAAU,CAAC,CAAA;AAEhC,iFAAiF;AACjF,0EAA0E;AAC1E,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,IAAA,+BAAiB,EAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;AAExD,gCAAgC;AAChC,4DAA4D;AAE5D,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAA;AAC1E,CAAC,CAAC,CAAA;AAEF,iFAAiF;AACjF,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,kDAAkD,IAAI,EAAE,CAAC,CAAA;IACrE,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,MAAM,CAAC,CAAA;IACnD,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,eAAe,CAAC,CAAA;AACjE,CAAC,CAAC,CAAA;AAEF,kBAAe,GAAG,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@neevjs/server",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "NeevJS optional Node.js backend starter — REST API with auth, middleware, and JSON contract",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"neevjs",
|
|
7
|
+
"node",
|
|
8
|
+
"express",
|
|
9
|
+
"api",
|
|
10
|
+
"backend"
|
|
11
|
+
],
|
|
12
|
+
"author": "Rahul Raj Kushwaha <rahulkraj.dev@gmail.com>",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"main": "dist/server.js",
|
|
15
|
+
"types": "dist/server.d.ts",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"cors": "^2.8.5",
|
|
18
|
+
"express": "^4.18.2",
|
|
19
|
+
"jsonwebtoken": "^9.0.2",
|
|
20
|
+
"@neevjs/shared": "0.0.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/cors": "^2.8.17",
|
|
24
|
+
"@types/express": "^4.17.21",
|
|
25
|
+
"@types/jsonwebtoken": "^9.0.5",
|
|
26
|
+
"@types/node": "^20.10.0",
|
|
27
|
+
"ts-node-dev": "^2.0.0",
|
|
28
|
+
"typescript": "^5.3.3"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
|
|
32
|
+
"build": "tsc -p tsconfig.json",
|
|
33
|
+
"start": "node dist/server.js",
|
|
34
|
+
"clean": "rm -rf dist"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Request, Response } from 'express'
|
|
2
|
+
import { db } from '../db'
|
|
3
|
+
import { generateToken } from '../middleware/jwt'
|
|
4
|
+
import { sendError, sendSuccess } from '../middleware/response'
|
|
5
|
+
import type { AuthUser, ModelRecord } from '@neevjs/shared'
|
|
6
|
+
|
|
7
|
+
interface AuthUserRecord extends ModelRecord {
|
|
8
|
+
email: string
|
|
9
|
+
password: string
|
|
10
|
+
name?: string
|
|
11
|
+
role?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function login(req: Request, res: Response): void {
|
|
15
|
+
const { email, password } = req.body as { email?: string; password?: string }
|
|
16
|
+
|
|
17
|
+
if (!email || !password) {
|
|
18
|
+
sendError(res, 'Email and password are required')
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const user = db.findBy('auth_users', 'email', email) as AuthUserRecord | undefined
|
|
23
|
+
|
|
24
|
+
if (!user || user.password !== password) {
|
|
25
|
+
sendError(res, 'Invalid credentials', 401)
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { password: _pw, ...safeUser } = user
|
|
30
|
+
const token = generateToken(safeUser as AuthUser)
|
|
31
|
+
|
|
32
|
+
sendSuccess(res, { user: safeUser, token })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function register(req: Request, res: Response): void {
|
|
36
|
+
const { email, password, name } = req.body as {
|
|
37
|
+
email?: string
|
|
38
|
+
password?: string
|
|
39
|
+
name?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!email || !password) {
|
|
43
|
+
sendError(res, 'Email and password are required')
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const existing = db.findBy('auth_users', 'email', email)
|
|
48
|
+
if (existing) {
|
|
49
|
+
sendError(res, 'Email already registered', 409)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const created = db.insert('auth_users', { email, password, name, role: 'user' }) as AuthUserRecord
|
|
54
|
+
const { password: _pw, ...safeUser } = created
|
|
55
|
+
const token = generateToken(safeUser as AuthUser)
|
|
56
|
+
|
|
57
|
+
sendSuccess(res, { user: safeUser, token }, {}, 201)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function me(req: Request, res: Response): void {
|
|
61
|
+
if (!req.user) {
|
|
62
|
+
sendError(res, 'Unauthorized', 401)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
sendSuccess(res, req.user)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function logout(_req: Request, res: Response): void {
|
|
69
|
+
// JWT is stateless — client just drops the token
|
|
70
|
+
sendSuccess(res, { message: 'Logged out successfully' })
|
|
71
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Request, Response } from 'express'
|
|
2
|
+
import { db } from '../db'
|
|
3
|
+
import { sendError, sendSuccess, paginateMeta } from '../middleware/response'
|
|
4
|
+
import type { ModelRecord } from '@neevjs/shared'
|
|
5
|
+
|
|
6
|
+
export function createModelController(tableName: string) {
|
|
7
|
+
return {
|
|
8
|
+
index(req: Request, res: Response): void {
|
|
9
|
+
const all = db.findAll(tableName)
|
|
10
|
+
const page = parseInt(String(req.query['page'] ?? '1'), 10)
|
|
11
|
+
const perPage = parseInt(String(req.query['perPage'] ?? '20'), 10)
|
|
12
|
+
const start = (page - 1) * perPage
|
|
13
|
+
const paginated = all.slice(start, start + perPage)
|
|
14
|
+
sendSuccess(res, paginated, paginateMeta(all.length, page, perPage))
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
show(req: Request, res: Response): void {
|
|
18
|
+
const record = db.findById(tableName, req.params['id'] ?? '')
|
|
19
|
+
if (!record) {
|
|
20
|
+
sendError(res, `${tableName} not found`, 404)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
sendSuccess(res, record)
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
store(req: Request, res: Response): void {
|
|
27
|
+
const body = req.body as Omit<ModelRecord, 'id'>
|
|
28
|
+
const record = db.insert(tableName, body)
|
|
29
|
+
sendSuccess(res, record, {}, 201)
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
update(req: Request, res: Response): void {
|
|
33
|
+
const record = db.update(tableName, req.params['id'] ?? '', req.body as Partial<ModelRecord>)
|
|
34
|
+
if (!record) {
|
|
35
|
+
sendError(res, `${tableName} not found`, 404)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
sendSuccess(res, record)
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
destroy(req: Request, res: Response): void {
|
|
42
|
+
const deleted = db.delete(tableName, req.params['id'] ?? '')
|
|
43
|
+
if (!deleted) {
|
|
44
|
+
sendError(res, `${tableName} not found`, 404)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
sendSuccess(res, { message: `${tableName} deleted` })
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/db/index.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ModelRecord } from '@neevjs/shared'
|
|
2
|
+
|
|
3
|
+
// Simple in-memory store — replace with PostgreSQL/SQLite in production
|
|
4
|
+
class InMemoryDB {
|
|
5
|
+
private store = new Map<string, ModelRecord[]>()
|
|
6
|
+
private counters = new Map<string, number>()
|
|
7
|
+
|
|
8
|
+
private nextId(table: string): number {
|
|
9
|
+
const current = this.counters.get(table) ?? 0
|
|
10
|
+
const next = current + 1
|
|
11
|
+
this.counters.set(table, next)
|
|
12
|
+
return next
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
seed(table: string, rows: ModelRecord[]): void {
|
|
16
|
+
this.store.set(table, rows)
|
|
17
|
+
const maxId = rows.reduce((m, r) => Math.max(m, Number(r.id ?? 0)), 0)
|
|
18
|
+
this.counters.set(table, maxId)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
findAll(table: string): ModelRecord[] {
|
|
22
|
+
return this.store.get(table) ?? []
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
findById(table: string, id: number | string): ModelRecord | undefined {
|
|
26
|
+
return (this.store.get(table) ?? []).find((r) => String(r.id) === String(id))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
findBy(table: string, key: string, value: unknown): ModelRecord | undefined {
|
|
30
|
+
return (this.store.get(table) ?? []).find((r) => r[key] === value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
insert(table: string, data: Omit<ModelRecord, 'id'>): ModelRecord {
|
|
34
|
+
const rows = this.store.get(table) ?? []
|
|
35
|
+
const record: ModelRecord = { id: this.nextId(table), ...data }
|
|
36
|
+
rows.push(record)
|
|
37
|
+
this.store.set(table, rows)
|
|
38
|
+
return record
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
update(table: string, id: number | string, data: Partial<ModelRecord>): ModelRecord | null {
|
|
42
|
+
const rows = this.store.get(table) ?? []
|
|
43
|
+
const index = rows.findIndex((r) => String(r.id) === String(id))
|
|
44
|
+
if (index === -1) return null
|
|
45
|
+
rows[index] = { ...rows[index], ...data }
|
|
46
|
+
this.store.set(table, rows)
|
|
47
|
+
return rows[index]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
delete(table: string, id: number | string): boolean {
|
|
51
|
+
const rows = this.store.get(table) ?? []
|
|
52
|
+
const index = rows.findIndex((r) => String(r.id) === String(id))
|
|
53
|
+
if (index === -1) return false
|
|
54
|
+
rows.splice(index, 1)
|
|
55
|
+
this.store.set(table, rows)
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const db = new InMemoryDB()
|
|
61
|
+
|
|
62
|
+
// Seed default tables
|
|
63
|
+
db.seed('users', [
|
|
64
|
+
{ id: 1, name: 'Rahul Kushwaha', email: 'rahul@example.com', role: 'admin' },
|
|
65
|
+
{ id: 2, name: 'Priya Sharma', email: 'priya@example.com', role: 'user' },
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
db.seed('auth_users', [
|
|
69
|
+
{
|
|
70
|
+
id: 1,
|
|
71
|
+
name: 'Admin',
|
|
72
|
+
email: 'admin@neevjs.dev',
|
|
73
|
+
// In production use bcrypt — for demo plain text
|
|
74
|
+
password: 'admin123',
|
|
75
|
+
role: 'admin',
|
|
76
|
+
},
|
|
77
|
+
])
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express'
|
|
2
|
+
import { verifyToken } from './jwt'
|
|
3
|
+
|
|
4
|
+
export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
5
|
+
const authHeader = req.headers.authorization
|
|
6
|
+
const token = authHeader?.split(' ')[1]
|
|
7
|
+
|
|
8
|
+
if (!token) {
|
|
9
|
+
res.status(401).json({ data: null, meta: {}, error: 'Unauthorized — no token provided' })
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const decoded = verifyToken(token)
|
|
15
|
+
req.user = {
|
|
16
|
+
id: decoded.id,
|
|
17
|
+
email: decoded.email,
|
|
18
|
+
role: decoded.role,
|
|
19
|
+
}
|
|
20
|
+
next()
|
|
21
|
+
} catch {
|
|
22
|
+
res.status(401).json({ data: null, meta: {}, error: 'Unauthorized — invalid or expired token' })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function requireRole(role: string) {
|
|
27
|
+
return (req: Request, res: Response, next: NextFunction): void => {
|
|
28
|
+
if (req.user?.role !== role) {
|
|
29
|
+
res.status(403).json({ data: null, meta: {}, error: 'Forbidden — insufficient permissions' })
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
next()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken'
|
|
2
|
+
import type { AuthUser } from '@neevjs/shared'
|
|
3
|
+
|
|
4
|
+
const SECRET = process.env['JWT_SECRET'] ?? 'neevjs-secret-change-in-production'
|
|
5
|
+
const EXPIRES_IN = '7d'
|
|
6
|
+
|
|
7
|
+
export interface JwtPayload {
|
|
8
|
+
id: number | string
|
|
9
|
+
email: string
|
|
10
|
+
role?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function generateToken(user: AuthUser): string {
|
|
14
|
+
const payload: JwtPayload = {
|
|
15
|
+
id: user.id,
|
|
16
|
+
email: user.email,
|
|
17
|
+
role: typeof user.role === 'string' ? user.role : undefined,
|
|
18
|
+
}
|
|
19
|
+
return jwt.sign(payload, SECRET, { expiresIn: EXPIRES_IN })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function verifyToken(token: string): JwtPayload {
|
|
23
|
+
return jwt.verify(token, SECRET) as JwtPayload
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Response } from 'express'
|
|
2
|
+
import type { ApiResponse, Pagination } from '@neevjs/shared'
|
|
3
|
+
|
|
4
|
+
export function sendSuccess<T>(
|
|
5
|
+
res: Response,
|
|
6
|
+
data: T,
|
|
7
|
+
meta: Record<string, unknown> = {},
|
|
8
|
+
status = 200
|
|
9
|
+
): void {
|
|
10
|
+
const body: ApiResponse<T> = { data, meta, error: null }
|
|
11
|
+
res.status(status).json(body)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function sendError(res: Response, message: string, status = 400): void {
|
|
15
|
+
const body: ApiResponse<null> = { data: null, meta: {}, error: message }
|
|
16
|
+
res.status(status).json(body)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function paginateMeta(total: number, page: number, perPage: number): { pagination: Pagination } {
|
|
20
|
+
return {
|
|
21
|
+
pagination: {
|
|
22
|
+
page,
|
|
23
|
+
perPage,
|
|
24
|
+
total,
|
|
25
|
+
lastPage: Math.ceil(total / perPage),
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Router } from 'express'
|
|
2
|
+
import { login, register, me, logout } from '../controllers/authController'
|
|
3
|
+
import { authMiddleware } from '../middleware/auth'
|
|
4
|
+
|
|
5
|
+
const router: Router = Router()
|
|
6
|
+
|
|
7
|
+
router.post('/login', login)
|
|
8
|
+
router.post('/register', register)
|
|
9
|
+
router.get('/me', authMiddleware, me)
|
|
10
|
+
router.post('/logout', authMiddleware, logout)
|
|
11
|
+
|
|
12
|
+
export default router
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Router } from 'express'
|
|
2
|
+
import { createModelController } from '../controllers/modelController'
|
|
3
|
+
|
|
4
|
+
export function createModelRouter(tableName: string, protected_ = false): Router {
|
|
5
|
+
const router = Router()
|
|
6
|
+
const ctrl = createModelController(tableName)
|
|
7
|
+
|
|
8
|
+
// Lazy import to avoid circular dep — only import when protected routes needed
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
10
|
+
const { authMiddleware } = protected_
|
|
11
|
+
? (require('../middleware/auth') as { authMiddleware: (req: import('express').Request, res: import('express').Response, next: import('express').NextFunction) => void })
|
|
12
|
+
: { authMiddleware: null }
|
|
13
|
+
|
|
14
|
+
const guard = protected_ && authMiddleware ? [authMiddleware] : []
|
|
15
|
+
|
|
16
|
+
router.get('/', ...guard, ctrl.index)
|
|
17
|
+
router.get('/:id', ...guard, ctrl.show)
|
|
18
|
+
router.post('/', ...guard, ctrl.store)
|
|
19
|
+
router.put('/:id', ...guard, ctrl.update)
|
|
20
|
+
router.delete('/:id', ...guard, ctrl.destroy)
|
|
21
|
+
|
|
22
|
+
return router
|
|
23
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import express, { Application } from 'express'
|
|
2
|
+
import cors from 'cors'
|
|
3
|
+
import authRoutes from './routes/auth'
|
|
4
|
+
import { createModelRouter } from './routes/modelRouter'
|
|
5
|
+
|
|
6
|
+
const app: Application = express()
|
|
7
|
+
const PORT = process.env['PORT'] ?? 3001
|
|
8
|
+
|
|
9
|
+
// ─── Middleware ───────────────────────────────────────────────────────────────
|
|
10
|
+
app.use(cors({ origin: '*' }))
|
|
11
|
+
app.use(express.json())
|
|
12
|
+
|
|
13
|
+
// ─── Health check ─────────────────────────────────────────────────────────────
|
|
14
|
+
app.get('/api/health', (_req, res) => {
|
|
15
|
+
res.json({ data: { status: 'ok', framework: 'NeevJS Server v0.0.1' }, meta: {}, error: null })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// ─── Auth routes ──────────────────────────────────────────────────────────────
|
|
19
|
+
app.use('/api/auth', authRoutes)
|
|
20
|
+
|
|
21
|
+
// ─── Auto-registered model routes ─────────────────────────────────────────────
|
|
22
|
+
// Add your models here. Set second arg to true to require authentication.
|
|
23
|
+
app.use('/api/users', createModelRouter('users', false))
|
|
24
|
+
|
|
25
|
+
// Example of a protected route:
|
|
26
|
+
// app.use('/api/orders', createModelRouter('orders', true))
|
|
27
|
+
|
|
28
|
+
// ─── 404 handler ──────────────────────────────────────────────────────────────
|
|
29
|
+
app.use((_req, res) => {
|
|
30
|
+
res.status(404).json({ data: null, meta: {}, error: 'Route not found' })
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// ─── Start ────────────────────────────────────────────────────────────────────
|
|
34
|
+
app.listen(PORT, () => {
|
|
35
|
+
console.log(`\n🚀 NeevJS Server running at http://localhost:${PORT}`)
|
|
36
|
+
console.log(` API: http://localhost:${PORT}/api`)
|
|
37
|
+
console.log(` Health: http://localhost:${PORT}/api/health\n`)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export default app
|
package/tsconfig.json
ADDED