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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/src/analyzer.js +410 -99
  3. package/src/generators/dotnet.js +1 -1
  4. package/src/generators/java.js +154 -97
  5. package/src/generators/node.js +213 -211
  6. package/src/templates/java-spring/partials/ApplicationSeeder.java.ejs +29 -7
  7. package/src/templates/java-spring/partials/AuthController.java.ejs +45 -14
  8. package/src/templates/java-spring/partials/Controller.java.ejs +25 -11
  9. package/src/templates/java-spring/partials/Dockerfile.ejs +25 -3
  10. package/src/templates/java-spring/partials/Entity.java.ejs +28 -3
  11. package/src/templates/java-spring/partials/JwtAuthFilter.java.ejs +41 -7
  12. package/src/templates/java-spring/partials/JwtService.java.ejs +47 -12
  13. package/src/templates/java-spring/partials/Repository.java.ejs +8 -1
  14. package/src/templates/java-spring/partials/Service.java.ejs +30 -6
  15. package/src/templates/java-spring/partials/User.java.ejs +26 -3
  16. package/src/templates/java-spring/partials/UserDetailsServiceImpl.java.ejs +10 -4
  17. package/src/templates/java-spring/partials/UserRepository.java.ejs +6 -0
  18. package/src/templates/java-spring/partials/docker-compose.yml.ejs +27 -5
  19. package/src/templates/node-ts-express/base/server.ts +63 -9
  20. package/src/templates/node-ts-express/base/tsconfig.json +19 -4
  21. package/src/templates/node-ts-express/partials/ApiDocs.ts.ejs +24 -9
  22. package/src/templates/node-ts-express/partials/App.test.ts.ejs +47 -27
  23. package/src/templates/node-ts-express/partials/Auth.controller.ts.ejs +68 -45
  24. package/src/templates/node-ts-express/partials/Auth.middleware.ts.ejs +45 -14
  25. package/src/templates/node-ts-express/partials/Auth.routes.ts.ejs +44 -5
  26. package/src/templates/node-ts-express/partials/Controller.ts.ejs +30 -16
  27. package/src/templates/node-ts-express/partials/Dockerfile.ejs +33 -11
  28. package/src/templates/node-ts-express/partials/Model.cs.ejs +38 -5
  29. package/src/templates/node-ts-express/partials/Model.ts.ejs +42 -12
  30. package/src/templates/node-ts-express/partials/PrismaController.ts.ejs +57 -23
  31. package/src/templates/node-ts-express/partials/PrismaSchema.prisma.ejs +33 -10
  32. package/src/templates/node-ts-express/partials/README.md.ejs +8 -10
  33. package/src/templates/node-ts-express/partials/Seeder.ts.ejs +99 -56
  34. package/src/templates/node-ts-express/partials/docker-compose.yml.ejs +30 -3
  35. package/src/templates/node-ts-express/partials/package.json.ejs +12 -7
  36. package/src/templates/node-ts-express/partials/routes.ts.ejs +31 -18
@@ -1,33 +1,55 @@
1
- # Auto-generated by create-backlist v5.0
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 dependencies
10
- RUN npm install --frozen-lockfile
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=dependencies /usr/src/app/node_modules ./node_modules
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
- COPY --from=dependencies /usr/src/app/node_modules ./node_modules
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
- <% if (dbType === 'prisma') { %>
28
- # Copy Prisma schema for runtime
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
- public Guid Id { get; set; }
9
+ [Key]
10
+ public Guid Id { get; set; } = Guid.NewGuid();
7
11
 
8
12
  <% model.fields.forEach(field => { %>
9
- <% let csharpType = 'string'; %>
10
- <% if (field.type === 'Number') csharpType = 'int'; %>
11
- <% if (field.type === 'Boolean') csharpType = 'bool'; %>
12
- public <%= csharpType %> <%= field.name %> { get; set; }
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
- <%= key %>: <%= schema[key].toLowerCase() %>;
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
- // Define the Mongoose Schema
12
- const <%= modelName %>Schema: Schema = new Schema({
13
- <% Object.keys(schema).forEach(key => { %>
14
- <%= key %>: {
15
- type: <%= schema[key] %>,
16
- // TODO: Add 'required', 'unique', etc. here
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
- }, { timestamps: true });
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.0 (Prisma Version)
1
+ // Auto-generated by create-backlist v5.1 (Prisma Version)
2
2
  import { Request, Response } from 'express';
3
- import { prisma } from '../server'; // Import the Prisma client instance
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 docs = await prisma.<%= modelName.toLowerCase() %>.findMany();
21
- res.status(200).json(docs);
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 { id } = req.params;
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
- if (!doc) return res.status(404).json({ message: 'Document not found' });
35
- res.status(200).json(doc);
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 { id } = req.params;
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
- res.status(200).json(doc);
50
- } catch (error) {
51
- res.status(500).json({ message: 'Error updating document', error });
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 { id } = req.params;
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
- res.status(200).json({ message: 'Document deleted successfully' });
63
- } catch (error) {
64
- res.status(500).json({ message: 'Error deleting document', error });
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.0
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" // User can change to "mysql", "sqlite", "sqlserver", etc.
8
+ provider = "<%= dbProvider || 'postgresql' %>"
9
9
  url = env("DATABASE_URL")
10
10
  }
11
11
 
12
- <%# Loop through each model identified by the analyzer %>
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
- <%# Loop through each field in the model %>
17
- <% model.fields.forEach(field => { %>
18
- <%# Map JS types to Prisma types. This is a basic mapping. %>
19
- <% let prismaType = 'String'; %>
20
- <% if (field.type === 'Number') prismaType = 'Int'; %>
21
- <% if (field.type === 'Boolean') prismaType = 'Boolean'; %>
22
- <%= field.name.padEnd(10) %> <%= prismaType %><%- field.isOptional ? '?' : '' %><%- field.isUnique ? ' @unique' : '' %>
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
- ## 🚀 Getting Started
5
+ ## Requirements
6
+ - Node.js 18+ (recommended)
7
+ - npm 9+
6
8
 
7
- 1. **Navigate to the directory:**
8
- ```bash
9
- cd <%= projectName %>
10
- ```
9
+ <% if (dbType === 'mongoose') { -%>
10
+ ## Database (MongoDB / Mongoose)
11
+ Set your Mongo connection string in `.env`:
11
12
 
12
- 2. **Run the development server:**
13
- ```bash
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 v4.0 on <%= new Date().toISOString() %>
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'; // For colorful console logs
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
- 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
- // --- Connect to DB ---
15
- const connectDB = async () => {
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 (err) {
24
- console.error(chalk.red(`Seeder DB Connection Error: ${err.message}`));
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
- // --- Import Data ---
30
- const importData = async () => {
64
+ async function importData() {
31
65
  try {
32
- // Clear existing data
33
- await User.deleteMany();
34
-
35
- const sampleUsers = [];
36
- const userCount = 10; // Number of sample users to create
37
-
38
- for (let i = 0; i < userCount; i++) {
39
- sampleUsers.push({
40
- name: faker.person.fullName(),
41
- email: faker.internet.email().toLowerCase(),
42
- password: 'password123', // All sample users will have the same password for easy testing
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
- await User.insertMany(sampleUsers);
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
- process.exit();
50
- } catch (error) {
51
- console.error(chalk.red(`Error with data import: ${error.message}`));
52
- process.exit(1);
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
- // --- Destroy Data ---
57
- const destroyData = async () => {
104
+ async function destroyData() {
58
105
  try {
59
- await User.deleteMany();
60
- // If you have other models, you can add them here for destruction
61
- // e.g., await Product.deleteMany();
62
-
106
+ <% uniq.forEach(m => { -%>
107
+ await <%= m.name %>.deleteMany();
108
+ <% }); -%>
63
109
  console.log(chalk.red.bold('🔥 Data Destroyed Successfully!'));
64
- process.exit();
65
- } catch (error) {
66
- console.error(chalk.red(`Error with data destruction: ${error.message}`));
67
- process.exit(1);
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
- // --- CLI Logic to run the seeder ---
72
- const runSeeder = async () => {
118
+ async function runSeeder() {
73
119
  await connectDB();
74
120
 
75
- // process.argv[2] will be '-d' if the script is run with `npm run destroy`
76
- if (process.argv[2] === '-d') {
77
- await destroyData();
78
- } else {
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.0
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
- - db
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:latest
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: