create-backlist 6.1.5 → 6.1.7
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/package.json +1 -1
- package/src/analyzer.js +410 -99
- package/src/generators/dotnet.js +1 -1
- package/src/generators/java.js +154 -97
- package/src/generators/node.js +213 -211
- package/src/templates/java-spring/partials/ApplicationSeeder.java.ejs +29 -7
- package/src/templates/java-spring/partials/AuthController.java.ejs +45 -14
- package/src/templates/java-spring/partials/Controller.java.ejs +25 -11
- package/src/templates/java-spring/partials/Dockerfile.ejs +25 -3
- package/src/templates/java-spring/partials/Entity.java.ejs +28 -3
- package/src/templates/java-spring/partials/JwtAuthFilter.java.ejs +41 -7
- package/src/templates/java-spring/partials/JwtService.java.ejs +47 -12
- package/src/templates/java-spring/partials/Repository.java.ejs +8 -1
- package/src/templates/java-spring/partials/Service.java.ejs +30 -6
- package/src/templates/java-spring/partials/User.java.ejs +26 -3
- package/src/templates/java-spring/partials/UserDetailsServiceImpl.java.ejs +10 -4
- package/src/templates/java-spring/partials/UserRepository.java.ejs +6 -0
- package/src/templates/java-spring/partials/docker-compose.yml.ejs +27 -5
- package/src/templates/node-ts-express/base/server.ts +63 -9
- package/src/templates/node-ts-express/base/tsconfig.json +19 -4
- package/src/templates/node-ts-express/partials/ApiDocs.ts.ejs +24 -9
- package/src/templates/node-ts-express/partials/App.test.ts.ejs +47 -27
- package/src/templates/node-ts-express/partials/Auth.controller.ts.ejs +68 -45
- package/src/templates/node-ts-express/partials/Auth.middleware.ts.ejs +45 -14
- package/src/templates/node-ts-express/partials/Auth.routes.ts.ejs +44 -5
- package/src/templates/node-ts-express/partials/Controller.ts.ejs +30 -16
- package/src/templates/node-ts-express/partials/Dockerfile.ejs +33 -11
- package/src/templates/node-ts-express/partials/Model.cs.ejs +38 -5
- package/src/templates/node-ts-express/partials/Model.ts.ejs +42 -12
- package/src/templates/node-ts-express/partials/PrismaController.ts.ejs +57 -23
- package/src/templates/node-ts-express/partials/PrismaSchema.prisma.ejs +33 -10
- package/src/templates/node-ts-express/partials/README.md.ejs +8 -10
- package/src/templates/node-ts-express/partials/Seeder.ts.ejs +99 -56
- package/src/templates/node-ts-express/partials/docker-compose.yml.ejs +30 -3
- package/src/templates/node-ts-express/partials/package.json.ejs +12 -7
- package/src/templates/node-ts-express/partials/routes.ts.ejs +31 -18
|
@@ -1,33 +1,55 @@
|
|
|
1
|
-
# Auto-generated by create-backlist v5.
|
|
1
|
+
# Auto-generated by create-backlist v5.1
|
|
2
2
|
|
|
3
3
|
# ---- Base Stage ----
|
|
4
4
|
FROM node:18-alpine AS base
|
|
5
5
|
WORKDIR /usr/src/app
|
|
6
6
|
COPY package*.json ./
|
|
7
7
|
|
|
8
|
-
# ---- Dependencies Stage ----
|
|
9
|
-
FROM base AS
|
|
10
|
-
|
|
8
|
+
# ---- Dependencies Stage (dev deps included for build) ----
|
|
9
|
+
FROM base AS deps
|
|
10
|
+
# npm ci is the correct "lockfile strict" install for npm
|
|
11
|
+
RUN npm ci
|
|
11
12
|
|
|
12
13
|
# ---- Build Stage ----
|
|
13
14
|
FROM base AS build
|
|
14
|
-
COPY --from=
|
|
15
|
+
COPY --from=deps /usr/src/app/node_modules ./node_modules
|
|
15
16
|
COPY . .
|
|
16
|
-
<% if (dbType === 'prisma') {
|
|
17
|
+
<% if (dbType === 'prisma') { -%>
|
|
17
18
|
RUN npx prisma generate
|
|
18
|
-
<% }
|
|
19
|
+
<% } -%>
|
|
19
20
|
RUN npm run build
|
|
20
21
|
|
|
22
|
+
# ---- Production Dependencies Stage (prod deps only) ----
|
|
23
|
+
FROM base AS prod-deps
|
|
24
|
+
RUN npm ci --omit=dev
|
|
25
|
+
|
|
21
26
|
# ---- Production Stage ----
|
|
22
27
|
FROM node:18-alpine AS production
|
|
23
28
|
WORKDIR /usr/src/app
|
|
29
|
+
|
|
30
|
+
ENV NODE_ENV=production
|
|
31
|
+
|
|
32
|
+
# copy compiled output
|
|
24
33
|
COPY --from=build /usr/src/app/dist ./dist
|
|
25
|
-
|
|
34
|
+
|
|
35
|
+
# copy prod-only node_modules
|
|
36
|
+
COPY --from=prod-deps /usr/src/app/node_modules ./node_modules
|
|
37
|
+
|
|
38
|
+
# package files (optional but nice)
|
|
26
39
|
COPY package*.json ./
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
|
|
41
|
+
<% if (dbType === 'prisma') { -%>
|
|
42
|
+
# Prisma schema (if your runtime uses it)
|
|
29
43
|
COPY prisma ./prisma
|
|
30
|
-
<% }
|
|
44
|
+
<% } -%>
|
|
45
|
+
|
|
46
|
+
# run as non-root user (security)
|
|
47
|
+
RUN addgroup -S app && adduser -S app -G app
|
|
48
|
+
USER app
|
|
31
49
|
|
|
32
50
|
EXPOSE <%= port %>
|
|
51
|
+
|
|
52
|
+
# Optional healthcheck if you have /api/health
|
|
53
|
+
# HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 CMD wget -qO- http://localhost:<%= port %>/api/health || exit 1
|
|
54
|
+
|
|
33
55
|
CMD ["node", "dist/server.js"]
|
|
@@ -1,18 +1,51 @@
|
|
|
1
1
|
// Auto-generated by create-backlist
|
|
2
|
+
using System;
|
|
3
|
+
using System.ComponentModel.DataAnnotations;
|
|
4
|
+
|
|
2
5
|
namespace <%= projectName %>.Models
|
|
3
6
|
{
|
|
4
7
|
public class <%= modelName %>
|
|
5
8
|
{
|
|
6
|
-
|
|
9
|
+
[Key]
|
|
10
|
+
public Guid Id { get; set; } = Guid.NewGuid();
|
|
7
11
|
|
|
8
12
|
<% model.fields.forEach(field => { %>
|
|
9
|
-
<%
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
<%
|
|
14
|
+
// Basic type mapping
|
|
15
|
+
let csharpType = 'string';
|
|
16
|
+
if (field.type === 'Number') csharpType = 'int';
|
|
17
|
+
if (field.type === 'Boolean') csharpType = 'bool';
|
|
18
|
+
|
|
19
|
+
// Small heuristic: money-like fields -> decimal
|
|
20
|
+
const n = String(field.name || '').toLowerCase();
|
|
21
|
+
if (field.type === 'Number' && (n.includes('price') || n.includes('amount') || n.includes('total'))) {
|
|
22
|
+
csharpType = 'decimal';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Required/Unique hints
|
|
26
|
+
const isEmail = n.includes('email');
|
|
27
|
+
%>
|
|
28
|
+
|
|
29
|
+
<% if (field.isUnique) { %>
|
|
30
|
+
// NOTE: Unique constraint should be configured in DbContext (HasIndex().IsUnique()).
|
|
31
|
+
<% } %>
|
|
32
|
+
|
|
33
|
+
<% if (isEmail) { %>
|
|
34
|
+
[EmailAddress]
|
|
35
|
+
<% } %>
|
|
36
|
+
|
|
37
|
+
<% if (csharpType === 'string') { %>
|
|
38
|
+
[Required]
|
|
39
|
+
public string <%= field.name.charAt(0).toUpperCase() + field.name.slice(1) %> { get; set; } = string.Empty;
|
|
40
|
+
<% } else { %>
|
|
41
|
+
public <%= csharpType %> <%= field.name.charAt(0).toUpperCase() + field.name.slice(1) %> { get; set; }
|
|
42
|
+
<% } %>
|
|
43
|
+
|
|
13
44
|
<% }); %>
|
|
14
45
|
|
|
15
46
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
47
|
+
|
|
48
|
+
// NOTE: Ideally update this automatically in DbContext.SaveChanges override.
|
|
16
49
|
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
|
17
50
|
}
|
|
18
51
|
}
|
|
@@ -1,22 +1,52 @@
|
|
|
1
1
|
// Auto-generated by create-backlist on <%= new Date().toISOString() %>
|
|
2
2
|
import mongoose, { Schema, Document } from 'mongoose';
|
|
3
3
|
|
|
4
|
-
// Define the interface for the Document
|
|
5
4
|
export interface I<%= modelName %> extends Document {
|
|
6
|
-
<% Object.keys(schema).forEach(key => {
|
|
7
|
-
|
|
5
|
+
<% Object.keys(schema).forEach(key => {
|
|
6
|
+
const t = String(schema[key] || 'String');
|
|
7
|
+
let tsType = 'string';
|
|
8
|
+
if (t === 'Number') tsType = 'number';
|
|
9
|
+
if (t === 'Boolean') tsType = 'boolean';
|
|
10
|
+
if (t === 'Date') tsType = 'Date';
|
|
11
|
+
%>
|
|
12
|
+
<%= key %>: <%= tsType %>;
|
|
8
13
|
<% }); %>
|
|
9
14
|
}
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<% Object.keys(schema).forEach(key => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const <%= modelName %>Schema = new Schema<I<%= modelName %>>(
|
|
17
|
+
{
|
|
18
|
+
<% Object.keys(schema).forEach(key => {
|
|
19
|
+
const t = String(schema[key] || 'String');
|
|
20
|
+
const lower = String(key).toLowerCase();
|
|
21
|
+
const isEmail = lower === 'email' || lower.includes('email');
|
|
22
|
+
const isPassword = lower === 'password';
|
|
23
|
+
const isNameLike = ['name','title'].some(x => lower === x || lower.includes(x));
|
|
24
|
+
%>
|
|
25
|
+
<%= key %>: {
|
|
26
|
+
type: <%= t %>,
|
|
27
|
+
<% if (isEmail) { -%>
|
|
28
|
+
required: true,
|
|
29
|
+
unique: true,
|
|
30
|
+
trim: true,
|
|
31
|
+
lowercase: true,
|
|
32
|
+
<% } else if (isPassword) { -%>
|
|
33
|
+
required: true,
|
|
34
|
+
select: false,
|
|
35
|
+
<% } else if (isNameLike) { -%>
|
|
36
|
+
required: true,
|
|
37
|
+
trim: true,
|
|
38
|
+
<% } else { -%>
|
|
39
|
+
// TODO: add required/unique/default/enum rules if needed
|
|
40
|
+
<% } -%>
|
|
41
|
+
},
|
|
18
42
|
<% }); %>
|
|
19
|
-
},
|
|
43
|
+
},
|
|
44
|
+
{ timestamps: true }
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Extra indexes (optional)
|
|
48
|
+
<% if (Object.keys(schema).some(k => String(k).toLowerCase().includes('email'))) { -%>
|
|
49
|
+
<%= modelName %>Schema.index({ email: 1 }, { unique: true });
|
|
50
|
+
<% } -%>
|
|
20
51
|
|
|
21
|
-
// Create and export the Model
|
|
22
52
|
export default mongoose.model<I<%= modelName %>>('<%= modelName %>', <%= modelName %>Schema);
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
// Auto-generated by create-backlist v5.
|
|
1
|
+
// Auto-generated by create-backlist v5.1 (Prisma Version)
|
|
2
2
|
import { Request, Response } from 'express';
|
|
3
|
-
import { prisma } from '../
|
|
3
|
+
import { prisma } from '../db/prisma';
|
|
4
|
+
|
|
5
|
+
function safeErrorMessage(error: unknown) {
|
|
6
|
+
return error instanceof Error ? error.message : String(error);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function parseId(id: string) {
|
|
10
|
+
<% if (idIsNumber) { -%>
|
|
11
|
+
return Number(id);
|
|
12
|
+
<% } else { -%>
|
|
13
|
+
return id;
|
|
14
|
+
<% } -%>
|
|
15
|
+
}
|
|
4
16
|
|
|
5
17
|
// @desc Create a new <%= modelName %>
|
|
6
18
|
export const create<%= modelName %> = async (req: Request, res: Response) => {
|
|
@@ -8,59 +20,81 @@ export const create<%= modelName %> = async (req: Request, res: Response) => {
|
|
|
8
20
|
const newDoc = await prisma.<%= modelName.toLowerCase() %>.create({
|
|
9
21
|
data: req.body,
|
|
10
22
|
});
|
|
11
|
-
res.status(201).json(newDoc);
|
|
23
|
+
return res.status(201).json(newDoc);
|
|
12
24
|
} catch (error) {
|
|
13
|
-
res.status(500).json({ message: 'Error creating document', error });
|
|
25
|
+
return res.status(500).json({ message: 'Error creating document', error: safeErrorMessage(error) });
|
|
14
26
|
}
|
|
15
27
|
};
|
|
16
28
|
|
|
17
|
-
// @desc Get all <%= modelName %>s
|
|
29
|
+
// @desc Get all <%= modelName %>s (supports pagination: ?page=&limit=)
|
|
18
30
|
export const getAll<%= modelName %>s = async (req: Request, res: Response) => {
|
|
19
31
|
try {
|
|
20
|
-
const
|
|
21
|
-
|
|
32
|
+
const page = Number(req.query.page || 1);
|
|
33
|
+
const limit = Number(req.query.limit || 50);
|
|
34
|
+
const skip = (page - 1) * limit;
|
|
35
|
+
|
|
36
|
+
const docs = await prisma.<%= modelName.toLowerCase() %>.findMany({
|
|
37
|
+
skip,
|
|
38
|
+
take: limit,
|
|
39
|
+
orderBy: { createdAt: 'desc' } as any
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return res.status(200).json(docs);
|
|
22
43
|
} catch (error) {
|
|
23
|
-
res.status(500).json({ message: 'Error fetching documents', error });
|
|
44
|
+
return res.status(500).json({ message: 'Error fetching documents', error: safeErrorMessage(error) });
|
|
24
45
|
}
|
|
25
46
|
};
|
|
26
47
|
|
|
27
48
|
// @desc Get a single <%= modelName %> by ID
|
|
28
49
|
export const get<%= modelName %>ById = async (req: Request, res: Response) => {
|
|
29
50
|
try {
|
|
30
|
-
const
|
|
51
|
+
const id = parseId(req.params.id);
|
|
52
|
+
|
|
31
53
|
const doc = await prisma.<%= modelName.toLowerCase() %>.findUnique({
|
|
32
|
-
where: { id },
|
|
54
|
+
where: { id } as any,
|
|
33
55
|
});
|
|
34
|
-
|
|
35
|
-
res.status(
|
|
56
|
+
|
|
57
|
+
if (!doc) return res.status(404).json({ message: '<%= modelName %> not found' });
|
|
58
|
+
return res.status(200).json(doc);
|
|
36
59
|
} catch (error) {
|
|
37
|
-
res.status(500).json({ message: 'Error fetching document', error });
|
|
60
|
+
return res.status(500).json({ message: 'Error fetching document', error: safeErrorMessage(error) });
|
|
38
61
|
}
|
|
39
62
|
};
|
|
40
63
|
|
|
41
64
|
// @desc Update a <%= modelName %> by ID
|
|
42
65
|
export const update<%= modelName %>ById = async (req: Request, res: Response) => {
|
|
43
66
|
try {
|
|
44
|
-
const
|
|
67
|
+
const id = parseId(req.params.id);
|
|
68
|
+
|
|
45
69
|
const doc = await prisma.<%= modelName.toLowerCase() %>.update({
|
|
46
|
-
where: { id },
|
|
70
|
+
where: { id } as any,
|
|
47
71
|
data: req.body,
|
|
48
72
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
73
|
+
|
|
74
|
+
return res.status(200).json(doc);
|
|
75
|
+
} catch (error: any) {
|
|
76
|
+
// Prisma "Record not found"
|
|
77
|
+
if (error?.code === 'P2025') {
|
|
78
|
+
return res.status(404).json({ message: '<%= modelName %> not found' });
|
|
79
|
+
}
|
|
80
|
+
return res.status(500).json({ message: 'Error updating document', error: safeErrorMessage(error) });
|
|
52
81
|
}
|
|
53
82
|
};
|
|
54
83
|
|
|
55
84
|
// @desc Delete a <%= modelName %> by ID
|
|
56
85
|
export const delete<%= modelName %>ById = async (req: Request, res: Response) => {
|
|
57
86
|
try {
|
|
58
|
-
const
|
|
87
|
+
const id = parseId(req.params.id);
|
|
88
|
+
|
|
59
89
|
await prisma.<%= modelName.toLowerCase() %>.delete({
|
|
60
|
-
where: { id },
|
|
90
|
+
where: { id } as any,
|
|
61
91
|
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
92
|
+
|
|
93
|
+
return res.status(200).json({ message: '<%= modelName %> deleted successfully' });
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
if (error?.code === 'P2025') {
|
|
96
|
+
return res.status(404).json({ message: '<%= modelName %> not found' });
|
|
97
|
+
}
|
|
98
|
+
return res.status(500).json({ message: 'Error deleting document', error: safeErrorMessage(error) });
|
|
65
99
|
}
|
|
66
100
|
};
|
|
@@ -1,25 +1,48 @@
|
|
|
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 = "
|
|
8
|
+
provider = "<%= dbProvider || 'postgresql' %>"
|
|
9
9
|
url = env("DATABASE_URL")
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<%
|
|
13
|
+
function sanitizeFieldName(name) {
|
|
14
|
+
const s = String(name || '').trim().replace(/[^a-zA-Z0-9_]/g, '_');
|
|
15
|
+
if (!s) return 'field';
|
|
16
|
+
if (/^[0-9]/.test(s)) return '_' + s;
|
|
17
|
+
return s;
|
|
18
|
+
}
|
|
19
|
+
%>
|
|
20
|
+
|
|
13
21
|
<% modelsToGenerate.forEach(model => { %>
|
|
14
22
|
model <%= model.name %> {
|
|
23
|
+
<% if (useIntId) { %>
|
|
24
|
+
id Int @id @default(autoincrement())
|
|
25
|
+
<% } else { %>
|
|
15
26
|
id String @id @default(cuid())
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
<% } %>
|
|
28
|
+
|
|
29
|
+
<% model.fields.forEach(field => {
|
|
30
|
+
const fname = sanitizeFieldName(field.name);
|
|
31
|
+
const lower = String(fname).toLowerCase();
|
|
32
|
+
|
|
33
|
+
let prismaType = 'String';
|
|
34
|
+
if (field.type === 'Number') prismaType = 'Int';
|
|
35
|
+
if (field.type === 'Boolean') prismaType = 'Boolean';
|
|
36
|
+
|
|
37
|
+
// money/decimal heuristic
|
|
38
|
+
if (field.type === 'Number' && (lower.includes('price') || lower.includes('amount') || lower.includes('total'))) {
|
|
39
|
+
prismaType = 'Decimal';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const optional = field.isOptional ? '?' : '';
|
|
43
|
+
const unique = field.isUnique ? ' @unique' : '';
|
|
44
|
+
%>
|
|
45
|
+
<%= fname.padEnd(16) %> <%= prismaType %><%= optional %><%= unique %>
|
|
23
46
|
<% }); %>
|
|
24
47
|
|
|
25
48
|
createdAt DateTime @default(now())
|
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This backend was auto-generated by **Backlist**.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Requirements
|
|
6
|
+
- Node.js 18+ (recommended)
|
|
7
|
+
- npm 9+
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
```
|
|
9
|
+
<% if (dbType === 'mongoose') { -%>
|
|
10
|
+
## Database (MongoDB / Mongoose)
|
|
11
|
+
Set your Mongo connection string in `.env`:
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
npm run dev
|
|
15
|
-
```
|
|
16
|
-
The server will start on `http://localhost:8000`.
|
|
13
|
+
```bash
|
|
14
|
+
MONGO_URI=mongodb://127.0.0.1:27017/<%= projectName %>
|
|
@@ -1,83 +1,126 @@
|
|
|
1
|
-
// Auto-generated by create-backlist
|
|
1
|
+
// Auto-generated by create-backlist 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
|
-
|
|
11
|
-
// The path is relative to the generated 'backend' project root.
|
|
12
|
-
import User from '../src/models/User.model';
|
|
9
|
+
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/<%= projectName %>';
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
function errMsg(e: unknown) {
|
|
12
|
+
return e instanceof Error ? e.message : String(e);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Dynamically import generated models
|
|
16
|
+
<%
|
|
17
|
+
const models = Array.isArray(models) ? models : [];
|
|
18
|
+
// Ensure unique by name
|
|
19
|
+
const uniq = [];
|
|
20
|
+
for (const m of models) {
|
|
21
|
+
if (m && m.name && !uniq.find(x => x.name === m.name)) uniq.push(m);
|
|
22
|
+
}
|
|
23
|
+
%>
|
|
24
|
+
<% uniq.forEach(m => { -%>
|
|
25
|
+
import <%= m.name %> from '../src/models/<%= m.name %>.model';
|
|
26
|
+
<% }); -%>
|
|
27
|
+
|
|
28
|
+
async function connectDB() {
|
|
16
29
|
try {
|
|
17
|
-
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/<%= projectName %>';
|
|
18
|
-
if (!MONGO_URI) {
|
|
19
|
-
throw new Error('MONGO_URI is not defined in your .env file');
|
|
20
|
-
}
|
|
21
30
|
await mongoose.connect(MONGO_URI);
|
|
22
31
|
console.log(chalk.green('MongoDB Connected for Seeder...'));
|
|
23
|
-
} catch (
|
|
24
|
-
console.error(chalk.red(`Seeder DB Connection Error: ${
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error(chalk.red(`Seeder DB Connection Error: ${errMsg(e)}`));
|
|
25
34
|
process.exit(1);
|
|
26
35
|
}
|
|
27
|
-
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function fakeValue(fieldName: string, fieldType: string) {
|
|
39
|
+
const n = fieldName.toLowerCase();
|
|
40
|
+
|
|
41
|
+
if (fieldType === 'Number') {
|
|
42
|
+
if (n.includes('price') || n.includes('amount') || n.includes('total')) return faker.number.int({ min: 10, max: 5000 });
|
|
43
|
+
if (n.includes('age')) return faker.number.int({ min: 18, max: 70 });
|
|
44
|
+
return faker.number.int({ min: 1, max: 999 });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (fieldType === 'Boolean') return faker.datatype.boolean();
|
|
48
|
+
|
|
49
|
+
// String
|
|
50
|
+
if (n.includes('email')) return faker.internet.email().toLowerCase();
|
|
51
|
+
if (n.includes('name')) return faker.person.fullName();
|
|
52
|
+
if (n.includes('phone')) return faker.phone.number();
|
|
53
|
+
if (n.includes('address')) return faker.location.streetAddress();
|
|
54
|
+
if (n.includes('city')) return faker.location.city();
|
|
55
|
+
if (n.includes('country')) return faker.location.country();
|
|
56
|
+
if (n.includes('password')) return 'Password123!';
|
|
57
|
+
if (n.includes('title')) return faker.lorem.words(3);
|
|
58
|
+
if (n.includes('description')) return faker.lorem.paragraph();
|
|
59
|
+
if (n.includes('image') || n.includes('avatar')) return faker.image.url();
|
|
60
|
+
|
|
61
|
+
return faker.lorem.word();
|
|
62
|
+
}
|
|
28
63
|
|
|
29
|
-
|
|
30
|
-
const importData = async () => {
|
|
64
|
+
async function importData() {
|
|
31
65
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
console.log(chalk.blue('Clearing existing data...'));
|
|
67
|
+
|
|
68
|
+
<% uniq.forEach(m => { -%>
|
|
69
|
+
await <%= m.name %>.deleteMany();
|
|
70
|
+
<% }); -%>
|
|
71
|
+
|
|
72
|
+
console.log(chalk.blue('Inserting seed data...'));
|
|
73
|
+
|
|
74
|
+
const count = 10;
|
|
75
|
+
|
|
76
|
+
<% uniq.forEach(m => {
|
|
77
|
+
const fields = Array.isArray(m.fields) ? m.fields : [];
|
|
78
|
+
-%>
|
|
79
|
+
// Seed <%= m.name %>
|
|
80
|
+
{
|
|
81
|
+
const rows: any[] = [];
|
|
82
|
+
for (let i = 0; i < count; i++) {
|
|
83
|
+
const obj: any = {};
|
|
84
|
+
<% fields.forEach(f => { -%>
|
|
85
|
+
obj['<%= f.name %>'] = fakeValue('<%= f.name %>', '<%= f.type %>');
|
|
86
|
+
<% }); -%>
|
|
87
|
+
rows.push(obj);
|
|
88
|
+
}
|
|
45
89
|
|
|
46
|
-
|
|
90
|
+
// Use create() to trigger hooks (e.g., password hashing)
|
|
91
|
+
await <%= m.name %>.create(rows);
|
|
92
|
+
}
|
|
93
|
+
<% }); -%>
|
|
47
94
|
|
|
48
95
|
console.log(chalk.green.bold('✅ Data Imported Successfully!'));
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.error(chalk.red(`Error with data import: ${errMsg(e)}`));
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
} finally {
|
|
100
|
+
await mongoose.disconnect();
|
|
53
101
|
}
|
|
54
|
-
}
|
|
102
|
+
}
|
|
55
103
|
|
|
56
|
-
|
|
57
|
-
const destroyData = async () => {
|
|
104
|
+
async function destroyData() {
|
|
58
105
|
try {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
106
|
+
<% uniq.forEach(m => { -%>
|
|
107
|
+
await <%= m.name %>.deleteMany();
|
|
108
|
+
<% }); -%>
|
|
63
109
|
console.log(chalk.red.bold('🔥 Data Destroyed Successfully!'));
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.error(chalk.red(`Error with data destruction: ${errMsg(e)}`));
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
} finally {
|
|
114
|
+
await mongoose.disconnect();
|
|
68
115
|
}
|
|
69
|
-
}
|
|
116
|
+
}
|
|
70
117
|
|
|
71
|
-
|
|
72
|
-
const runSeeder = async () => {
|
|
118
|
+
async function runSeeder() {
|
|
73
119
|
await connectDB();
|
|
74
120
|
|
|
75
|
-
//
|
|
76
|
-
if (process.argv[2] === '-d')
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
await importData();
|
|
80
|
-
}
|
|
81
|
-
};
|
|
121
|
+
// `npm run destroy` passes "-d"
|
|
122
|
+
if (process.argv[2] === '-d') await destroyData();
|
|
123
|
+
else await importData();
|
|
124
|
+
}
|
|
82
125
|
|
|
83
126
|
runSeeder();
|
|
@@ -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:
|
|
@@ -9,23 +9,44 @@ services:
|
|
|
9
9
|
- '<%= port %>:<%= port %>'
|
|
10
10
|
environment:
|
|
11
11
|
- PORT=<%= port %>
|
|
12
|
+
<% if (dbType === 'mongoose') { -%>
|
|
13
|
+
- MONGO_URI=${MONGO_URI}
|
|
14
|
+
<% } else if (dbType === 'prisma') { -%>
|
|
12
15
|
- DATABASE_URL=${DATABASE_URL}
|
|
16
|
+
<% } -%>
|
|
17
|
+
<% if (addAuth) { -%>
|
|
13
18
|
- JWT_SECRET=${JWT_SECRET}
|
|
19
|
+
<% } -%>
|
|
20
|
+
<% if (extraFeatures && extraFeatures.includes('swagger')) { -%>
|
|
21
|
+
- API_BASE_URL=http://localhost:<%= port %>
|
|
22
|
+
<% } -%>
|
|
14
23
|
depends_on:
|
|
15
|
-
|
|
24
|
+
db:
|
|
25
|
+
condition: service_healthy
|
|
16
26
|
volumes:
|
|
17
27
|
- .:/usr/src/app
|
|
18
28
|
- /usr/src/app/node_modules
|
|
19
29
|
command: npm run dev
|
|
30
|
+
restart: unless-stopped
|
|
20
31
|
|
|
21
32
|
db:
|
|
22
33
|
<% if (dbType === 'mongoose') { %>
|
|
23
|
-
image: mongo:
|
|
34
|
+
image: mongo:6
|
|
24
35
|
container_name: <%= projectName %>-mongo-db
|
|
36
|
+
environment:
|
|
37
|
+
- MONGO_INITDB_ROOT_USERNAME=${DB_USER}
|
|
38
|
+
- MONGO_INITDB_ROOT_PASSWORD=${DB_PASSWORD}
|
|
39
|
+
- MONGO_INITDB_DATABASE=${DB_NAME}
|
|
25
40
|
ports:
|
|
26
41
|
- '27017:27017'
|
|
27
42
|
volumes:
|
|
28
43
|
- mongo-data:/data/db
|
|
44
|
+
healthcheck:
|
|
45
|
+
test: ["CMD", "mongosh", "--quiet", "mongodb://$$DB_USER:$$DB_PASSWORD@localhost:27017/admin", "--eval", "db.runCommand({ ping: 1 }).ok" ]
|
|
46
|
+
interval: 10s
|
|
47
|
+
timeout: 5s
|
|
48
|
+
retries: 10
|
|
49
|
+
restart: unless-stopped
|
|
29
50
|
<% } else if (dbType === 'prisma') { %>
|
|
30
51
|
image: postgres:14-alpine
|
|
31
52
|
container_name: <%= projectName %>-postgres-db
|
|
@@ -37,6 +58,12 @@ services:
|
|
|
37
58
|
- POSTGRES_DB=${DB_NAME}
|
|
38
59
|
volumes:
|
|
39
60
|
- postgres-data:/var/lib/postgresql/data
|
|
61
|
+
healthcheck:
|
|
62
|
+
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
|
63
|
+
interval: 10s
|
|
64
|
+
timeout: 5s
|
|
65
|
+
retries: 10
|
|
66
|
+
restart: unless-stopped
|
|
40
67
|
<% } %>
|
|
41
68
|
|
|
42
69
|
volumes:
|