create-backlist 5.0.7 → 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 +205 -75
- package/src/generators/node.js +262 -85
- package/src/generators/python.js +54 -25
- 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 +30 -0
- package/src/templates/java-spring/partials/AuthController.java.ejs +62 -0
- package/src/templates/java-spring/partials/Controller.java.ejs +40 -50
- package/src/templates/java-spring/partials/Dockerfile.ejs +16 -0
- package/src/templates/java-spring/partials/Entity.java.ejs +16 -15
- package/src/templates/java-spring/partials/JwtAuthFilter.java.ejs +66 -0
- package/src/templates/java-spring/partials/JwtService.java.ejs +58 -0
- package/src/templates/java-spring/partials/Repository.java.ejs +9 -3
- package/src/templates/java-spring/partials/SecurityConfig.java.ejs +44 -0
- package/src/templates/java-spring/partials/Service.java.ejs +69 -0
- package/src/templates/java-spring/partials/User.java.ejs +33 -0
- package/src/templates/java-spring/partials/UserDetailsServiceImpl.java.ejs +33 -0
- package/src/templates/java-spring/partials/UserRepository.java.ejs +20 -0
- package/src/templates/java-spring/partials/docker-compose.yml.ejs +35 -0
- 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/templates/python-fastapi/Dockerfile.ejs +8 -0
- package/src/templates/python-fastapi/app/core/config.py.ejs +8 -0
- package/src/templates/python-fastapi/app/core/security.py.ejs +8 -0
- package/src/templates/python-fastapi/app/db.py.ejs +7 -0
- package/src/templates/python-fastapi/app/main.py.ejs +24 -0
- package/src/templates/python-fastapi/app/models/user.py.ejs +9 -0
- package/src/templates/python-fastapi/app/routers/auth.py.ejs +33 -0
- package/src/templates/python-fastapi/app/routers/model_routes.py.ejs +72 -0
- package/src/templates/python-fastapi/app/schemas/user.py.ejs +16 -0
- package/src/templates/python-fastapi/docker-compose.yml.ejs +19 -0
- package/src/templates/python-fastapi/requirements.txt.ejs +5 -1
- package/src/utils.js +19 -4
- package/bin/index.js +0 -141
|
@@ -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
|
+
<% }) -%>
|
|
@@ -1,26 +1,41 @@
|
|
|
1
|
-
// Auto-generated by create-backlist v5.
|
|
1
|
+
// Auto-generated by create-backlist v5.1
|
|
2
2
|
|
|
3
3
|
generator client {
|
|
4
4
|
provider = "prisma-client-js"
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
datasource db {
|
|
8
|
-
provider = "postgresql"
|
|
8
|
+
provider = "postgresql"
|
|
9
9
|
url = env("DATABASE_URL")
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<%
|
|
13
|
+
function mapPrismaType(t) {
|
|
14
|
+
const x = String(t || '').toLowerCase();
|
|
15
|
+
if (x === 'number' || x === 'int' || x === 'integer') return 'Int';
|
|
16
|
+
if (x === 'float' || x === 'double') return 'Float';
|
|
17
|
+
if (x === 'boolean' || x === 'bool') return 'Boolean';
|
|
18
|
+
if (x === 'date' || x === 'datetime') return 'DateTime';
|
|
19
|
+
return 'String';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function safeName(n) {
|
|
23
|
+
return String(n || '').replace(/[^a-zA-Z0-9_]/g, '');
|
|
24
|
+
}
|
|
25
|
+
%>
|
|
26
|
+
|
|
13
27
|
<% modelsToGenerate.forEach(model => { %>
|
|
14
|
-
model <%= model.name %> {
|
|
28
|
+
model <%= safeName(model.name) %> {
|
|
15
29
|
id String @id @default(cuid())
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
|
|
31
|
+
<% (model.fields || []).forEach(field => {
|
|
32
|
+
const fname = safeName(field.name);
|
|
33
|
+
const prismaType = mapPrismaType(field.type);
|
|
34
|
+
const optional = field.isOptional ? '?' : '';
|
|
35
|
+
const unique = field.isUnique ? ' @unique' : '';
|
|
36
|
+
-%>
|
|
37
|
+
<%= fname %> <%= prismaType %><%= optional %><%= unique %>
|
|
38
|
+
<% }); -%>
|
|
24
39
|
|
|
25
40
|
createdAt DateTime @default(now())
|
|
26
41
|
updatedAt DateTime @updatedAt
|
|
@@ -2,15 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
This backend was auto-generated by **Backlist**.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
npm run dev
|
|
15
|
-
```
|
|
16
|
-
The server will start on `http://localhost:8000`.
|
|
5
|
+
## Requirements
|
|
6
|
+
- Node.js 18+
|
|
7
|
+
- npm 9+
|
|
8
|
+
|
|
9
|
+
<% if (dbType === 'prisma') { -%>
|
|
10
|
+
## Database (Prisma + PostgreSQL)
|
|
11
|
+
1. Copy env:
|
|
12
|
+
```bash
|
|
13
|
+
cp .env.example .env
|
|
@@ -1,83 +1,63 @@
|
|
|
1
|
-
// Auto-generated by create-backlist
|
|
1
|
+
// Auto-generated by create-backlist v5.1 on <%= new Date().toISOString() %>
|
|
2
2
|
import mongoose from 'mongoose';
|
|
3
3
|
import dotenv from 'dotenv';
|
|
4
4
|
import { faker } from '@faker-js/faker';
|
|
5
|
-
import chalk from 'chalk';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
6
|
|
|
7
|
-
// Load env vars
|
|
8
7
|
dotenv.config();
|
|
9
8
|
|
|
10
|
-
// We assume a User model exists for seeding.
|
|
11
|
-
// The path is relative to the generated 'backend' project root.
|
|
12
9
|
import User from '../src/models/User.model';
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!MONGO_URI) {
|
|
19
|
-
throw new Error('MONGO_URI is not defined in your .env file');
|
|
20
|
-
}
|
|
21
|
-
await mongoose.connect(MONGO_URI);
|
|
22
|
-
console.log(chalk.green('MongoDB Connected for Seeder...'));
|
|
23
|
-
} catch (err) {
|
|
24
|
-
console.error(chalk.red(`Seeder DB Connection Error: ${err.message}`));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
11
|
+
function getErrorMessage(err: unknown) {
|
|
12
|
+
if (err instanceof Error) return err.message;
|
|
13
|
+
return String(err);
|
|
14
|
+
}
|
|
28
15
|
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
// Clear existing data
|
|
33
|
-
await User.deleteMany();
|
|
16
|
+
async function connectDB() {
|
|
17
|
+
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/<%= projectName %>';
|
|
18
|
+
if (!MONGO_URI) throw new Error('MONGO_URI is not defined');
|
|
34
19
|
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
await mongoose.connect(MONGO_URI);
|
|
21
|
+
console.log(chalk.green('MongoDB Connected for Seeder...'));
|
|
22
|
+
}
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
email: faker.internet.email().toLowerCase(),
|
|
42
|
-
password: 'password123', // All sample users will have the same password for easy testing
|
|
43
|
-
});
|
|
44
|
-
}
|
|
24
|
+
async function importData() {
|
|
25
|
+
// Clear existing data
|
|
26
|
+
await User.deleteMany({});
|
|
45
27
|
|
|
46
|
-
|
|
28
|
+
const sampleUsers = Array.from({ length: 10 }).map(() => ({
|
|
29
|
+
name: faker.person.fullName(),
|
|
30
|
+
email: faker.internet.email().toLowerCase(),
|
|
31
|
+
password: 'password123',
|
|
32
|
+
}));
|
|
47
33
|
|
|
48
|
-
|
|
49
|
-
process.exit();
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.error(chalk.red(`Error with data import: ${error.message}`));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
34
|
+
await User.insertMany(sampleUsers);
|
|
55
35
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
await User.deleteMany();
|
|
60
|
-
// If you have other models, you can add them here for destruction
|
|
61
|
-
// e.g., await Product.deleteMany();
|
|
36
|
+
console.log(chalk.green.bold('Data Imported Successfully!'));
|
|
37
|
+
}
|
|
62
38
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
39
|
+
async function destroyData() {
|
|
40
|
+
await User.deleteMany({});
|
|
41
|
+
console.log(chalk.red.bold('Data Destroyed Successfully!'));
|
|
42
|
+
}
|
|
70
43
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
44
|
+
async function run() {
|
|
45
|
+
try {
|
|
46
|
+
await connectDB();
|
|
74
47
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
48
|
+
if (process.argv.includes('-d')) {
|
|
49
|
+
await destroyData();
|
|
50
|
+
} else {
|
|
51
|
+
await importData();
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error(chalk.red(`Seeder error: ${getErrorMessage(err)}`));
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
} finally {
|
|
57
|
+
try {
|
|
58
|
+
await mongoose.disconnect();
|
|
59
|
+
} catch {}
|
|
80
60
|
}
|
|
81
|
-
}
|
|
61
|
+
}
|
|
82
62
|
|
|
83
|
-
|
|
63
|
+
run();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Auto-generated by create-backlist v5.
|
|
1
|
+
# Auto-generated by create-backlist v5.1
|
|
2
2
|
version: '3.8'
|
|
3
3
|
|
|
4
4
|
services:
|
|
@@ -8,40 +8,55 @@ services:
|
|
|
8
8
|
ports:
|
|
9
9
|
- '<%= port %>:<%= port %>'
|
|
10
10
|
environment:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
PORT: <%= port %>
|
|
12
|
+
JWT_SECRET: ${JWT_SECRET:-change_me_long_secret_change_me_long_secret}
|
|
13
|
+
<% if (dbType === 'mongoose') { -%>
|
|
14
|
+
MONGO_URI: ${MONGO_URI:-mongodb://db:27017/<%= projectName %>}
|
|
15
|
+
<% } else if (dbType === 'prisma') { -%>
|
|
16
|
+
DATABASE_URL: ${DATABASE_URL:-postgresql://${DB_USER:-postgres}:${DB_PASSWORD:-password}@db:5432/${DB_NAME:-<%= projectName %>}?schema=public}
|
|
17
|
+
<% } -%>
|
|
14
18
|
depends_on:
|
|
15
|
-
|
|
19
|
+
db:
|
|
20
|
+
condition: service_healthy
|
|
16
21
|
volumes:
|
|
17
22
|
- .:/usr/src/app
|
|
18
23
|
- /usr/src/app/node_modules
|
|
19
24
|
command: npm run dev
|
|
20
25
|
|
|
21
26
|
db:
|
|
22
|
-
|
|
23
|
-
image: mongo:
|
|
27
|
+
<% if (dbType === 'mongoose') { -%>
|
|
28
|
+
image: mongo:7
|
|
24
29
|
container_name: <%= projectName %>-mongo-db
|
|
25
30
|
ports:
|
|
26
31
|
- '27017:27017'
|
|
27
32
|
volumes:
|
|
28
33
|
- mongo-data:/data/db
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
healthcheck:
|
|
35
|
+
test: ["CMD", "mongosh", "--quiet", "mongodb://localhost:27017/admin", "--eval", "db.adminCommand('ping').ok"]
|
|
36
|
+
interval: 5s
|
|
37
|
+
timeout: 5s
|
|
38
|
+
retries: 20
|
|
39
|
+
<% } else if (dbType === 'prisma') { -%>
|
|
40
|
+
image: postgres:16-alpine
|
|
31
41
|
container_name: <%= projectName %>-postgres-db
|
|
32
42
|
ports:
|
|
33
43
|
- '5432:5432'
|
|
34
44
|
environment:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
POSTGRES_USER: ${DB_USER:-postgres}
|
|
46
|
+
POSTGRES_PASSWORD: ${DB_PASSWORD:-password}
|
|
47
|
+
POSTGRES_DB: ${DB_NAME:-<%= projectName %>}
|
|
38
48
|
volumes:
|
|
39
49
|
- postgres-data:/var/lib/postgresql/data
|
|
40
|
-
|
|
50
|
+
healthcheck:
|
|
51
|
+
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-<%= projectName %>}"]
|
|
52
|
+
interval: 5s
|
|
53
|
+
timeout: 5s
|
|
54
|
+
retries: 20
|
|
55
|
+
<% } -%>
|
|
41
56
|
|
|
42
57
|
volumes:
|
|
43
|
-
|
|
58
|
+
<% if (dbType === 'mongoose') { -%>
|
|
44
59
|
mongo-data:
|
|
45
|
-
|
|
60
|
+
<% } else if (dbType === 'prisma') { -%>
|
|
46
61
|
postgres-data:
|
|
47
|
-
|
|
62
|
+
<% } -%>
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"scripts": {
|
|
7
|
+
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
|
|
7
8
|
"build": "tsc",
|
|
8
9
|
"start": "node dist/server.js",
|
|
9
|
-
"
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"clean": "rimraf dist"
|
|
10
12
|
},
|
|
11
13
|
"dependencies": {
|
|
12
14
|
"cors": "^2.8.5",
|
|
@@ -1,56 +1,67 @@
|
|
|
1
1
|
// Auto-generated by create-backlist on <%= new Date().toISOString() %>
|
|
2
2
|
import { Router, Request, Response } from 'express';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
<%
|
|
5
|
+
/**
|
|
6
|
+
* Collect controllers safely
|
|
7
|
+
*/
|
|
5
8
|
const controllers = [];
|
|
6
9
|
if (Array.isArray(endpoints)) {
|
|
7
|
-
endpoints.forEach(
|
|
10
|
+
endpoints.forEach(ep => {
|
|
8
11
|
if (ep && ep.controllerName && ep.controllerName !== 'Default' && !controllers.includes(ep.controllerName)) {
|
|
9
12
|
controllers.push(ep.controllerName);
|
|
10
13
|
}
|
|
11
14
|
});
|
|
12
15
|
}
|
|
13
16
|
%>
|
|
17
|
+
|
|
14
18
|
<% controllers.forEach((ctrl) => { %>
|
|
15
19
|
import * as <%= ctrl %>Controller from './controllers/<%= ctrl %>.controller';
|
|
16
20
|
<% }) %>
|
|
17
21
|
|
|
18
|
-
<% if (addAuth) {
|
|
22
|
+
<% if (addAuth) { -%>
|
|
19
23
|
import { protect } from './middleware/Auth.middleware';
|
|
20
|
-
<% }
|
|
24
|
+
<% } -%>
|
|
21
25
|
|
|
22
26
|
const router = Router();
|
|
23
27
|
|
|
24
|
-
// If no endpoints detected, emit a basic route so file is valid
|
|
25
28
|
<% if (!Array.isArray(endpoints) || endpoints.length === 0) { %>
|
|
26
29
|
router.get('/health', (_req: Request, res: Response) => {
|
|
27
30
|
res.status(200).json({ ok: true, message: 'Auto-generated routes alive' });
|
|
28
31
|
});
|
|
29
32
|
<% } %>
|
|
30
33
|
|
|
31
|
-
<%
|
|
34
|
+
<%
|
|
35
|
+
/**
|
|
36
|
+
* Render endpoints
|
|
37
|
+
* Prefer ep.route (normalized) and fallback to ep.path
|
|
38
|
+
*/
|
|
32
39
|
if (Array.isArray(endpoints)) {
|
|
33
|
-
endpoints.forEach((ep) => {
|
|
34
|
-
|
|
35
|
-
const expressPath = (rawPath.replace(/^\/api/, '') || '/').replace(/{(\w+)}/g, ':$1');
|
|
36
|
-
const method = ((ep && ep.method) ? ep.method : 'GET').toLowerCase();
|
|
37
|
-
const ctrl = (ep && ep.controllerName) ? ep.controllerName : 'Default';
|
|
38
|
-
const hasId = expressPath.includes(':');
|
|
39
|
-
let handler = '';
|
|
40
|
+
endpoints.forEach((ep) => {
|
|
41
|
+
if (!ep) return;
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
const raw = (ep.route || ep.path || '/');
|
|
44
|
+
// mount router at /api in server.ts, so here we remove leading /api
|
|
45
|
+
const expressPath = (String(raw).replace(/^\/api/, '') || '/')
|
|
46
|
+
.replace(/{(\w+)}/g, ':$1'); // backward compat if analyzer still produces {id}
|
|
47
|
+
|
|
48
|
+
const method = String(ep.method || 'GET').toLowerCase();
|
|
49
|
+
const ctrl = ep.controllerName || 'Default';
|
|
50
|
+
const action = ep.actionName || null;
|
|
48
51
|
|
|
49
|
-
const needsProtect = !!addAuth &&
|
|
52
|
+
const needsProtect = !!addAuth && method !== 'get'; // default: protect non-GET
|
|
50
53
|
const middleware = needsProtect ? 'protect, ' : '';
|
|
54
|
+
|
|
55
|
+
let handler = '';
|
|
56
|
+
if (ctrl !== 'Default' && action) {
|
|
57
|
+
handler = `${ctrl}Controller.${action}`;
|
|
58
|
+
}
|
|
51
59
|
%>
|
|
52
|
-
router.<%= method %>(
|
|
53
|
-
|
|
60
|
+
router.<%= method %>(
|
|
61
|
+
'<%- expressPath %>',
|
|
62
|
+
<%- middleware %><%- handler || '(req: Request, res: Response) => res.status(501).json({ message: "Not Implemented" })' %>
|
|
63
|
+
);
|
|
64
|
+
<%
|
|
54
65
|
});
|
|
55
66
|
}
|
|
56
67
|
%>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
from jose import jwt
|
|
3
|
+
from app.core.config import settings
|
|
4
|
+
|
|
5
|
+
def create_token(sub: str):
|
|
6
|
+
expire = datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
|
|
7
|
+
payload = {"sub": sub, "exp": expire}
|
|
8
|
+
return jwt.encode(payload, settings.JWT_SECRET, algorithm="HS256")
|