create-backlist 6.0.0 → 6.0.2
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/bin/backlist.js +227 -0
- package/package.json +10 -4
- package/src/analyzer.js +210 -89
- package/src/db/prisma.ts +4 -0
- package/src/generators/dotnet.js +120 -94
- package/src/generators/java.js +157 -109
- package/src/generators/node.js +262 -85
- package/src/generators/template.js +38 -2
- package/src/scanner/index.js +99 -0
- package/src/templates/dotnet/partials/Controller.cs.ejs +7 -14
- package/src/templates/dotnet/partials/Dto.cs.ejs +8 -0
- package/src/templates/java-spring/partials/ApplicationSeeder.java.ejs +7 -2
- package/src/templates/java-spring/partials/AuthController.java.ejs +23 -10
- package/src/templates/java-spring/partials/Controller.java.ejs +17 -6
- package/src/templates/java-spring/partials/Dockerfile.ejs +6 -1
- package/src/templates/java-spring/partials/Entity.java.ejs +15 -5
- package/src/templates/java-spring/partials/JwtAuthFilter.java.ejs +30 -7
- package/src/templates/java-spring/partials/JwtService.java.ejs +38 -10
- package/src/templates/java-spring/partials/Repository.java.ejs +10 -1
- package/src/templates/java-spring/partials/Service.java.ejs +45 -7
- package/src/templates/java-spring/partials/User.java.ejs +17 -4
- package/src/templates/java-spring/partials/UserDetailsServiceImpl.java.ejs +10 -4
- package/src/templates/java-spring/partials/UserRepository.java.ejs +8 -0
- package/src/templates/java-spring/partials/docker-compose.yml.ejs +16 -8
- package/src/templates/node-ts-express/base/server.ts +12 -5
- package/src/templates/node-ts-express/base/tsconfig.json +13 -3
- package/src/templates/node-ts-express/partials/ApiDocs.ts.ejs +17 -7
- package/src/templates/node-ts-express/partials/App.test.ts.ejs +27 -27
- package/src/templates/node-ts-express/partials/Auth.controller.ts.ejs +56 -62
- package/src/templates/node-ts-express/partials/Auth.middleware.ts.ejs +21 -10
- package/src/templates/node-ts-express/partials/Controller.ts.ejs +40 -40
- package/src/templates/node-ts-express/partials/DbContext.cs.ejs +3 -3
- package/src/templates/node-ts-express/partials/Dockerfile.ejs +9 -11
- package/src/templates/node-ts-express/partials/Model.cs.ejs +25 -7
- package/src/templates/node-ts-express/partials/Model.ts.ejs +20 -12
- package/src/templates/node-ts-express/partials/PrismaController.ts.ejs +72 -55
- package/src/templates/node-ts-express/partials/PrismaSchema.prisma.ejs +27 -12
- package/src/templates/node-ts-express/partials/README.md.ejs +9 -12
- package/src/templates/node-ts-express/partials/Seeder.ts.ejs +44 -64
- package/src/templates/node-ts-express/partials/docker-compose.yml.ejs +31 -16
- package/src/templates/node-ts-express/partials/package.json.ejs +3 -1
- package/src/templates/node-ts-express/partials/prismaClient.ts.ejs +4 -0
- package/src/templates/node-ts-express/partials/routes.ts.ejs +35 -24
- package/src/utils.js +19 -4
- package/bin/index.js +0 -141
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
// Auto-generated by create-backlist v5.
|
|
1
|
+
// Auto-generated by create-backlist v5.1
|
|
2
2
|
import request from 'supertest';
|
|
3
3
|
import express from 'express';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import authRoutes from '../routes/Auth.routes';
|
|
4
|
+
|
|
5
|
+
import apiRoutes from '../routes';
|
|
6
|
+
<% if (addAuth) { -%>
|
|
7
|
+
import authRoutes from '../routes/Auth.routes';
|
|
8
|
+
<% } -%>
|
|
8
9
|
|
|
9
10
|
const app = express();
|
|
10
11
|
app.use(express.json());
|
|
11
|
-
app.use('/api', apiRoutes);
|
|
12
|
-
app.use('/api/auth', authRoutes);
|
|
13
|
-
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
<% if (addAuth) { -%>
|
|
14
|
+
app.use('/api/auth', authRoutes);
|
|
15
|
+
<% } -%>
|
|
16
|
+
app.use('/api', apiRoutes);
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// You might need to adjust this to a real endpoint from your app.
|
|
20
|
-
// Example for GET /api/users
|
|
21
|
-
// const res = await request(app).get('/api/users');
|
|
22
|
-
// expect(res.statusCode).toEqual(200);
|
|
23
|
-
|
|
24
|
-
// For now, a placeholder test:
|
|
18
|
+
describe('API Endpoints (Generated)', () => {
|
|
19
|
+
it('sanity check', () => {
|
|
25
20
|
expect(1 + 1).toBe(2);
|
|
26
21
|
});
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
<% endpoints
|
|
24
|
+
.filter(ep => ep && ep.route && ep.method)
|
|
25
|
+
.forEach(ep => {
|
|
26
|
+
const method = ep.method.toLowerCase();
|
|
27
|
+
const url = (ep.route.startsWith('/api/') ? ep.route.replace(/^\/api/, '') : ep.route)
|
|
28
|
+
.replace(/:\w+/g, '1'); // replace path params with dummy value
|
|
29
|
+
-%>
|
|
30
|
+
it('<%= ep.method %> <%= url %> should respond', async () => {
|
|
31
|
+
const res = await request(app).<%= method %>('<%= '/api' + url %>')<% if (['post','put','patch'].includes(method) && ep.schemaFields) { %>
|
|
32
|
+
.send(<%- JSON.stringify(Object.fromEntries(Object.entries(ep.schemaFields).map(([k,t]) => [k, t === 'Number' ? 1 : (t === 'Boolean' ? true : 'test') ]))) %>)<% } %>;
|
|
33
|
+
// We only assert "not 404" because generated handlers may be TODO stubs,
|
|
34
|
+
// and auth/validation may affect exact status codes.
|
|
35
|
+
expect(res.statusCode).not.toBe(404);
|
|
36
|
+
});
|
|
37
|
+
<% }) -%>
|
|
38
38
|
});
|
|
@@ -3,87 +3,81 @@ import { Request, Response } from 'express';
|
|
|
3
3
|
import bcrypt from 'bcryptjs';
|
|
4
4
|
import jwt from 'jsonwebtoken';
|
|
5
5
|
|
|
6
|
-
<% if (dbType === 'mongoose') {
|
|
6
|
+
<% if (dbType === 'mongoose') { -%>
|
|
7
7
|
import User from '../models/User.model';
|
|
8
|
-
<% } else if (dbType === 'prisma') {
|
|
9
|
-
import { prisma } from '../
|
|
10
|
-
<% }
|
|
8
|
+
<% } else if (dbType === 'prisma') { -%>
|
|
9
|
+
import { prisma } from '../db/prisma';
|
|
10
|
+
<% } -%>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
function signToken(res: Response, userId: string) {
|
|
13
|
+
const secret = process.env.JWT_SECRET;
|
|
14
|
+
if (!secret) {
|
|
15
|
+
return res.status(500).json({ message: 'JWT_SECRET is not configured' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const payload = { user: { id: userId } };
|
|
19
|
+
|
|
20
|
+
jwt.sign(payload, secret, { expiresIn: '5h' }, (err, token) => {
|
|
21
|
+
if (err || !token) return res.status(500).json({ message: 'Failed to sign token' });
|
|
22
|
+
return res.status(201).json({ token });
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Register
|
|
13
27
|
export const registerUser = async (req: Request, res: Response) => {
|
|
14
|
-
const { name, email, password } = req.body;
|
|
28
|
+
const { name, email, password } = req.body || {};
|
|
15
29
|
|
|
16
30
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let user = await User.findOne({ email });
|
|
20
|
-
if (user) {
|
|
21
|
-
return res.status(400).json({ message: 'User already exists' });
|
|
31
|
+
if (!name || !email || !password) {
|
|
32
|
+
return res.status(400).json({ message: 'name, email, password are required' });
|
|
22
33
|
}
|
|
23
|
-
|
|
34
|
+
|
|
35
|
+
<% if (dbType === 'mongoose') { -%>
|
|
36
|
+
const existing = await User.findOne({ email });
|
|
37
|
+
if (existing) return res.status(400).json({ message: 'User already exists' });
|
|
38
|
+
|
|
39
|
+
const user = new User({ name, email, password }); // password will be hashed by pre-save hook
|
|
24
40
|
await user.save();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
return res.status(400).json({ message: 'User already exists' });
|
|
30
|
-
}
|
|
31
|
-
const hashedPassword = await bcrypt.hash(password, 10);
|
|
32
|
-
const user = await prisma.user.create({
|
|
33
|
-
data: { name, email, password: hashedPassword },
|
|
34
|
-
});
|
|
35
|
-
<% } %>
|
|
41
|
+
return signToken(res, String(user._id));
|
|
42
|
+
<% } else if (dbType === 'prisma') { -%>
|
|
43
|
+
const existing = await prisma.user.findUnique({ where: { email } });
|
|
44
|
+
if (existing) return res.status(400).json({ message: 'User already exists' });
|
|
36
45
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (err) throw err;
|
|
44
|
-
res.status(201).json({ token });
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.error(error);
|
|
49
|
-
res.status(500).send('Server Error');
|
|
46
|
+
const hashedPassword = await bcrypt.hash(password, 10);
|
|
47
|
+
const user = await prisma.user.create({ data: { name, email, password: hashedPassword } });
|
|
48
|
+
return signToken(res, String(user.id));
|
|
49
|
+
<% } -%>
|
|
50
|
+
} catch (error: any) {
|
|
51
|
+
return res.status(500).json({ message: 'Server Error', error: error?.message || error });
|
|
50
52
|
}
|
|
51
53
|
};
|
|
52
54
|
|
|
53
|
-
//
|
|
55
|
+
// Login
|
|
54
56
|
export const loginUser = async (req: Request, res: Response) => {
|
|
55
|
-
const { email, password } = req.body;
|
|
57
|
+
const { email, password } = req.body || {};
|
|
56
58
|
|
|
57
59
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
if (!email || !password) {
|
|
61
|
+
return res.status(400).json({ message: 'email and password are required' });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
<% if (dbType === 'mongoose') { -%>
|
|
60
65
|
const user = await User.findOne({ email });
|
|
61
|
-
|
|
62
|
-
// Prisma Logic
|
|
66
|
+
<% } else if (dbType === 'prisma') { -%>
|
|
63
67
|
const user = await prisma.user.findUnique({ where: { email } });
|
|
64
|
-
|
|
68
|
+
<% } -%>
|
|
65
69
|
|
|
66
|
-
if (!user) {
|
|
67
|
-
return res.status(400).json({ message: 'Invalid Credentials' });
|
|
68
|
-
}
|
|
70
|
+
if (!user) return res.status(400).json({ message: 'Invalid Credentials' });
|
|
69
71
|
|
|
70
72
|
const isMatch = await bcrypt.compare(password, user.password);
|
|
71
|
-
if (!isMatch) {
|
|
72
|
-
return res.status(400).json({ message: 'Invalid Credentials' });
|
|
73
|
-
}
|
|
73
|
+
if (!isMatch) return res.status(400).json({ message: 'Invalid Credentials' });
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
res.json({ token });
|
|
83
|
-
}
|
|
84
|
-
);
|
|
85
|
-
} catch (error) {
|
|
86
|
-
console.error(error);
|
|
87
|
-
res.status(500).send('Server Error');
|
|
75
|
+
<% if (dbType === 'mongoose') { -%>
|
|
76
|
+
return signToken(res, String(user._id));
|
|
77
|
+
<% } else { -%>
|
|
78
|
+
return signToken(res, String(user.id));
|
|
79
|
+
<% } -%>
|
|
80
|
+
} catch (error: any) {
|
|
81
|
+
return res.status(500).json({ message: 'Server Error', error: error?.message || error });
|
|
88
82
|
}
|
|
89
83
|
};
|
|
@@ -1,27 +1,38 @@
|
|
|
1
|
-
// Auto-generated by create-backlist
|
|
1
|
+
// Auto-generated by create-backlist v5.1 on <%= new Date().toISOString() %>
|
|
2
2
|
import { Request, Response, NextFunction } from 'express';
|
|
3
3
|
import jwt from 'jsonwebtoken';
|
|
4
4
|
|
|
5
|
-
// Extend the default Request interface to include our 'user' property
|
|
6
5
|
interface AuthRequest extends Request {
|
|
7
6
|
user?: any;
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
export const protect = (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const secret = process.env.JWT_SECRET;
|
|
11
|
+
if (!secret) return res.status(500).json({ message: 'JWT_SECRET is not configured' });
|
|
12
|
+
|
|
13
|
+
// Support both:
|
|
14
|
+
// 1) Authorization: Bearer <token>
|
|
15
|
+
// 2) x-auth-token: <token>
|
|
16
|
+
const authHeader = req.header('authorization');
|
|
17
|
+
const xToken = req.header('x-auth-token');
|
|
18
|
+
|
|
19
|
+
let token: string | undefined;
|
|
20
|
+
|
|
21
|
+
if (authHeader && authHeader.toLowerCase().startsWith('bearer ')) {
|
|
22
|
+
token = authHeader.slice(7).trim();
|
|
23
|
+
} else if (xToken) {
|
|
24
|
+
token = xToken.trim();
|
|
25
|
+
}
|
|
13
26
|
|
|
14
|
-
// Check if not token
|
|
15
27
|
if (!token) {
|
|
16
28
|
return res.status(401).json({ message: 'No token, authorization denied' });
|
|
17
29
|
}
|
|
18
30
|
|
|
19
|
-
// Verify token
|
|
20
31
|
try {
|
|
21
|
-
const decoded = jwt.verify(token,
|
|
32
|
+
const decoded: any = jwt.verify(token, secret);
|
|
22
33
|
req.user = decoded.user;
|
|
23
|
-
next();
|
|
24
|
-
} catch
|
|
25
|
-
res.status(401).json({ message: 'Token is not valid' });
|
|
34
|
+
return next();
|
|
35
|
+
} catch {
|
|
36
|
+
return res.status(401).json({ message: 'Token is not valid' });
|
|
26
37
|
}
|
|
27
38
|
};
|
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
// Auto-generated by create-backlist on <%= new Date().toISOString() %>
|
|
2
2
|
import { Request, Response } from 'express';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} catch (error) {
|
|
9
|
-
res.status(500).json({ message: 'Error creating <%= modelName %>', error });
|
|
10
|
-
}
|
|
11
|
-
};
|
|
4
|
+
/**
|
|
5
|
+
* Controller: <%= controllerName %>
|
|
6
|
+
* Generated from frontend AST scan.
|
|
7
|
+
*/
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
res.status(500).json({ message: 'Error fetching <%= modelName %>s', error });
|
|
19
|
-
}
|
|
20
|
-
};
|
|
9
|
+
<%
|
|
10
|
+
function safeAction(name, fallback) {
|
|
11
|
+
if (!name) return fallback;
|
|
12
|
+
return String(name).replace(/[^a-zA-Z0-9_]/g, '');
|
|
13
|
+
}
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} catch (error) {
|
|
28
|
-
res.status(500).json({ message: 'Error fetching <%= modelName %>', error });
|
|
29
|
-
}
|
|
30
|
-
};
|
|
15
|
+
function hasBody(method) {
|
|
16
|
+
const m = String(method || 'GET').toUpperCase();
|
|
17
|
+
return ['POST', 'PUT', 'PATCH'].includes(m);
|
|
18
|
+
}
|
|
19
|
+
%>
|
|
31
20
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} catch (error) {
|
|
38
|
-
res.status(500).json({ message: 'Error updating <%= modelName %>', error });
|
|
39
|
-
}
|
|
40
|
-
};
|
|
21
|
+
<% if (!Array.isArray(endpoints) || endpoints.length === 0) { %>
|
|
22
|
+
export async function health(_req: Request, res: Response) {
|
|
23
|
+
return res.status(200).json({ ok: true });
|
|
24
|
+
}
|
|
25
|
+
<% } %>
|
|
41
26
|
|
|
42
|
-
|
|
27
|
+
<% (endpoints || []).forEach((ep, i) => {
|
|
28
|
+
const method = String(ep.method || 'GET').toUpperCase();
|
|
29
|
+
const actionName = safeAction(ep.actionName, `handler${i}`);
|
|
30
|
+
const route = ep.route || ep.path || '';
|
|
31
|
+
-%>
|
|
32
|
+
/**
|
|
33
|
+
* <%= method %> <%= route %>
|
|
34
|
+
*/
|
|
35
|
+
export async function <%= actionName %>(req: Request, res: Response) {
|
|
43
36
|
try {
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
|
|
37
|
+
<% if (hasBody(method)) { -%>
|
|
38
|
+
// Body schema (inferred):
|
|
39
|
+
// <%- JSON.stringify(ep.schemaFields || ep.requestBody?.fields || {}, null, 2).split('\n').map(l => '// ' + l).join('\n') %>
|
|
40
|
+
const payload = req.body;
|
|
41
|
+
return res.status(<%= method === 'POST' ? 201 : 200 %>).json({ message: 'TODO: implement', payload });
|
|
42
|
+
<% } else { -%>
|
|
43
|
+
return res.status(200).json({ message: 'TODO: implement', params: req.params, query: req.query });
|
|
44
|
+
<% } -%>
|
|
47
45
|
} catch (error) {
|
|
48
|
-
res.status(500).json({ message: '
|
|
46
|
+
return res.status(500).json({ message: 'Internal Server Error', error });
|
|
49
47
|
}
|
|
50
|
-
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
<% }) -%>
|
|
@@ -8,8 +8,8 @@ namespace <%= projectName %>.Data
|
|
|
8
8
|
{
|
|
9
9
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
public DbSet<<%= model.name %>> <%= model.name %>
|
|
13
|
-
|
|
11
|
+
<% modelsToGenerate.forEach(model => { -%>
|
|
12
|
+
public DbSet<<%= model.name %>> <%= model.name %> { get; set; } = default!;
|
|
13
|
+
<% }); -%>
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -1,33 +1,31 @@
|
|
|
1
|
-
# Auto-generated by create-backlist v5.
|
|
1
|
+
# Auto-generated by create-backlist v5.1
|
|
2
2
|
|
|
3
|
-
# ---- Base Stage ----
|
|
4
3
|
FROM node:18-alpine AS base
|
|
5
4
|
WORKDIR /usr/src/app
|
|
6
5
|
COPY package*.json ./
|
|
7
6
|
|
|
8
|
-
# ---- Dependencies Stage ----
|
|
9
7
|
FROM base AS dependencies
|
|
10
|
-
|
|
8
|
+
# Use npm ci for reproducible installs
|
|
9
|
+
RUN npm ci
|
|
11
10
|
|
|
12
|
-
# ---- Build Stage ----
|
|
13
11
|
FROM base AS build
|
|
14
12
|
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
|
|
15
13
|
COPY . .
|
|
16
|
-
<% if (dbType === 'prisma') {
|
|
14
|
+
<% if (dbType === 'prisma') { -%>
|
|
17
15
|
RUN npx prisma generate
|
|
18
|
-
<% }
|
|
16
|
+
<% } -%>
|
|
19
17
|
RUN npm run build
|
|
20
18
|
|
|
21
|
-
# ---- Production Stage ----
|
|
22
19
|
FROM node:18-alpine AS production
|
|
23
20
|
WORKDIR /usr/src/app
|
|
21
|
+
ENV NODE_ENV=production
|
|
22
|
+
|
|
24
23
|
COPY --from=build /usr/src/app/dist ./dist
|
|
25
24
|
COPY --from=dependencies /usr/src/app/node_modules ./node_modules
|
|
26
25
|
COPY package*.json ./
|
|
27
|
-
<% if (dbType === 'prisma') {
|
|
28
|
-
# Copy Prisma schema for runtime
|
|
26
|
+
<% if (dbType === 'prisma') { -%>
|
|
29
27
|
COPY prisma ./prisma
|
|
30
|
-
<% }
|
|
28
|
+
<% } -%>
|
|
31
29
|
|
|
32
30
|
EXPOSE <%= port %>
|
|
33
31
|
CMD ["node", "dist/server.js"]
|
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
// Auto-generated by create-backlist
|
|
2
|
+
using System;
|
|
3
|
+
|
|
2
4
|
namespace <%= projectName %>.Models
|
|
3
5
|
{
|
|
4
6
|
public class <%= modelName %>
|
|
5
7
|
{
|
|
6
|
-
public Guid Id { get; set; }
|
|
8
|
+
public Guid Id { get; set; } = Guid.NewGuid();
|
|
9
|
+
|
|
10
|
+
<%
|
|
11
|
+
function pascal(s){
|
|
12
|
+
return String(s || '')
|
|
13
|
+
.replace(/[-_]+(.)/g, (_,c)=>c.toUpperCase())
|
|
14
|
+
.replace(/^\w/, c => c.toUpperCase())
|
|
15
|
+
.replace(/[^a-zA-Z0-9]/g,'');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function mapType(t){
|
|
19
|
+
if (!t) return "string";
|
|
20
|
+
const x = String(t).toLowerCase();
|
|
21
|
+
if (x === 'number' || x === 'int' || x === 'integer') return "int";
|
|
22
|
+
if (x === 'float' || x === 'double') return "double";
|
|
23
|
+
if (x === 'boolean' || x === 'bool') return "bool";
|
|
24
|
+
if (x === 'date' || x === 'datetime') return "DateTime";
|
|
25
|
+
return "string";
|
|
26
|
+
}
|
|
27
|
+
-%>
|
|
7
28
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<% if (field.type === 'Boolean') csharpType = 'bool'; %>
|
|
12
|
-
public <%= csharpType %> <%= field.name %> { get; set; }
|
|
13
|
-
<% }); %>
|
|
29
|
+
<% (model.fields || []).forEach(field => { -%>
|
|
30
|
+
public <%= mapType(field.type) %> <%= pascal(field.name) %> { get; set; }
|
|
31
|
+
<% }); -%>
|
|
14
32
|
|
|
15
33
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
16
34
|
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
// Auto-generated by create-backlist on <%= new Date().toISOString() %>
|
|
2
2
|
import mongoose, { Schema, Document } from 'mongoose';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
type FieldType = 'string' | 'number' | 'boolean';
|
|
5
|
+
|
|
5
6
|
export interface I<%= modelName %> extends Document {
|
|
6
|
-
<% Object.keys(schema).forEach(key => {
|
|
7
|
-
|
|
7
|
+
<% Object.keys(schema).forEach(key => {
|
|
8
|
+
const t = String(schema[key] || 'string').toLowerCase();
|
|
9
|
+
const tsType = (t === 'number') ? 'number' : (t === 'boolean') ? 'boolean' : 'string';
|
|
10
|
+
-%>
|
|
11
|
+
<%= key %>: <%= tsType %>;
|
|
8
12
|
<% }); %>
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<% Object.keys(schema).forEach(key => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const <%= modelName %>Schema: Schema = new Schema(
|
|
16
|
+
{
|
|
17
|
+
<% Object.keys(schema).forEach(key => {
|
|
18
|
+
const t = String(schema[key] || 'string').toLowerCase();
|
|
19
|
+
const mongoType = (t === 'number') ? 'Number' : (t === 'boolean') ? 'Boolean' : 'String';
|
|
20
|
+
-%>
|
|
21
|
+
<%= key %>: {
|
|
22
|
+
type: <%= mongoType %>,
|
|
23
|
+
required: false,
|
|
24
|
+
},
|
|
18
25
|
<% }); %>
|
|
19
|
-
},
|
|
26
|
+
},
|
|
27
|
+
{ timestamps: true }
|
|
28
|
+
);
|
|
20
29
|
|
|
21
|
-
// Create and export the Model
|
|
22
30
|
export default mongoose.model<I<%= modelName %>>('<%= modelName %>', <%= modelName %>Schema);
|
|
@@ -1,66 +1,83 @@
|
|
|
1
|
-
// Auto-generated by create-backlist v5.
|
|
1
|
+
// Auto-generated by create-backlist v5.1 (Prisma, From Endpoints)
|
|
2
2
|
import { Request, Response } from 'express';
|
|
3
|
-
import { prisma } from '../
|
|
3
|
+
import { prisma } from '../db/prisma';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
11
|
-
res.status(201).json(newDoc);
|
|
12
|
-
} catch (error) {
|
|
13
|
-
res.status(500).json({ message: 'Error creating document', error });
|
|
14
|
-
}
|
|
15
|
-
};
|
|
5
|
+
<%
|
|
6
|
+
function safeAction(name, fallback) {
|
|
7
|
+
if (!name) return fallback;
|
|
8
|
+
return String(name).replace(/[^a-zA-Z0-9_]/g, '');
|
|
9
|
+
}
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const docs = await prisma.<%= modelName.toLowerCase() %>.findMany();
|
|
21
|
-
res.status(200).json(docs);
|
|
22
|
-
} catch (error) {
|
|
23
|
-
res.status(500).json({ message: 'Error fetching documents', error });
|
|
24
|
-
}
|
|
25
|
-
};
|
|
11
|
+
function hasIdInRoute(route) {
|
|
12
|
+
return /:\w+/.test(route || '');
|
|
13
|
+
}
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const doc = await prisma.<%= modelName.toLowerCase() %>.findUnique({
|
|
32
|
-
where: { id },
|
|
33
|
-
});
|
|
34
|
-
if (!doc) return res.status(404).json({ message: 'Document not found' });
|
|
35
|
-
res.status(200).json(doc);
|
|
36
|
-
} catch (error) {
|
|
37
|
-
res.status(500).json({ message: 'Error fetching document', error });
|
|
38
|
-
}
|
|
39
|
-
};
|
|
15
|
+
function hasBody(method) {
|
|
16
|
+
const m = String(method || 'GET').toUpperCase();
|
|
17
|
+
return ['POST', 'PUT', 'PATCH'].includes(m);
|
|
18
|
+
}
|
|
40
19
|
|
|
41
|
-
//
|
|
42
|
-
|
|
20
|
+
// prisma client accessor name. Usually lowerCamelCase in prisma client.
|
|
21
|
+
const prismaAccessor = controllerName.charAt(0).toLowerCase() + controllerName.slice(1);
|
|
22
|
+
%>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Prisma model: <%= controllerName %> (client: prisma.<%= prismaAccessor %>)
|
|
26
|
+
* NOTE: If your prisma model name differs, adjust mapping in generator.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
<% (endpoints || []).forEach((ep, i) => {
|
|
30
|
+
const method = String(ep.method || 'GET').toUpperCase();
|
|
31
|
+
const actionName = safeAction(ep.actionName, `handler${i}`);
|
|
32
|
+
const route = (ep.route || ep.path || '').replace(/^\/api/, '');
|
|
33
|
+
const usesId = hasIdInRoute(route);
|
|
34
|
+
-%>
|
|
35
|
+
/**
|
|
36
|
+
* <%= method %> <%= ep.route || ep.path || '' %>
|
|
37
|
+
*/
|
|
38
|
+
export async function <%= actionName %>(req: Request, res: Response) {
|
|
43
39
|
try {
|
|
44
|
-
|
|
45
|
-
const
|
|
40
|
+
<% if (method === 'POST') { -%>
|
|
41
|
+
const created = await prisma.<%= prismaAccessor %>.create({ data: req.body });
|
|
42
|
+
return res.status(201).json(created);
|
|
43
|
+
<% } else if (method === 'GET' && !usesId) { -%>
|
|
44
|
+
const list = await prisma.<%= prismaAccessor %>.findMany();
|
|
45
|
+
return res.status(200).json(list);
|
|
46
|
+
<% } else if (method === 'GET' && usesId) { -%>
|
|
47
|
+
const idRaw = req.params.id;
|
|
48
|
+
// Try number first; fallback to string
|
|
49
|
+
const id: any = (idRaw !== undefined && idRaw !== null && String(Number(idRaw)) === idRaw) ? Number(idRaw) : idRaw;
|
|
50
|
+
|
|
51
|
+
const found = await prisma.<%= prismaAccessor %>.findUnique({ where: { id } });
|
|
52
|
+
if (!found) return res.status(404).json({ message: 'Not found' });
|
|
53
|
+
return res.status(200).json(found);
|
|
54
|
+
<% } else if ((method === 'PUT' || method === 'PATCH') && usesId) { -%>
|
|
55
|
+
const idRaw = req.params.id;
|
|
56
|
+
const id: any = (idRaw !== undefined && idRaw !== null && String(Number(idRaw)) === idRaw) ? Number(idRaw) : idRaw;
|
|
57
|
+
|
|
58
|
+
const updated = await prisma.<%= prismaAccessor %>.update({
|
|
46
59
|
where: { id },
|
|
47
60
|
data: req.body,
|
|
48
61
|
});
|
|
49
|
-
res.status(200).json(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
};
|
|
62
|
+
return res.status(200).json(updated);
|
|
63
|
+
<% } else if (method === 'DELETE' && usesId) { -%>
|
|
64
|
+
const idRaw = req.params.id;
|
|
65
|
+
const id: any = (idRaw !== undefined && idRaw !== null && String(Number(idRaw)) === idRaw) ? Number(idRaw) : idRaw;
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
await prisma.<%= prismaAccessor %>.delete({ where: { id } });
|
|
68
|
+
return res.status(200).json({ message: 'Deleted' });
|
|
69
|
+
<% } else { -%>
|
|
70
|
+
// Non-CRUD or route not matching the default patterns
|
|
71
|
+
// TODO: implement custom logic (query params, nested routes, etc.)
|
|
72
|
+
<% if (hasBody(method)) { -%>
|
|
73
|
+
return res.status(200).json({ message: 'TODO: implement', body: req.body, params: req.params, query: req.query });
|
|
74
|
+
<% } else { -%>
|
|
75
|
+
return res.status(200).json({ message: 'TODO: implement', params: req.params, query: req.query });
|
|
76
|
+
<% } -%>
|
|
77
|
+
<% } -%>
|
|
78
|
+
} catch (error: any) {
|
|
79
|
+
return res.status(500).json({ message: 'Internal Server Error', error: error?.message || error });
|
|
65
80
|
}
|
|
66
|
-
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
<% }) -%>
|