fossyl 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/index.js +231 -8
- package/dist/index.mjs +231 -8
- package/package.json +1 -1
package/README.md
ADDED
package/dist/index.js
CHANGED
|
@@ -76,7 +76,30 @@ async function promptForOptions(projectName) {
|
|
|
76
76
|
p.cancel("Operation cancelled.");
|
|
77
77
|
return null;
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
let dialect;
|
|
80
|
+
if (database === "kysely") {
|
|
81
|
+
dialect = await p.select({
|
|
82
|
+
message: "Database dialect:",
|
|
83
|
+
options: [
|
|
84
|
+
{ value: "sqlite", label: "SQLite", hint: "recommended - great for per-customer databases" },
|
|
85
|
+
{ value: "postgres", label: "PostgreSQL" },
|
|
86
|
+
{ value: "mysql", label: "MySQL" }
|
|
87
|
+
]
|
|
88
|
+
});
|
|
89
|
+
if (p.isCancel(dialect)) {
|
|
90
|
+
p.cancel("Operation cancelled.");
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const docker = await p.confirm({
|
|
95
|
+
message: "Include Docker setup?",
|
|
96
|
+
initialValue: true
|
|
97
|
+
});
|
|
98
|
+
if (p.isCancel(docker)) {
|
|
99
|
+
p.cancel("Operation cancelled.");
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return { name, server, validator, database, dialect, docker };
|
|
80
103
|
}
|
|
81
104
|
|
|
82
105
|
// src/scaffold.ts
|
|
@@ -105,8 +128,15 @@ function generatePackageJson(options) {
|
|
|
105
128
|
if (options.database === "kysely") {
|
|
106
129
|
dependencies["@fossyl/kysely"] = "^0.9.0";
|
|
107
130
|
dependencies["kysely"] = "^0.27.0";
|
|
108
|
-
|
|
109
|
-
|
|
131
|
+
if (options.dialect === "sqlite") {
|
|
132
|
+
dependencies["better-sqlite3"] = "^11.0.0";
|
|
133
|
+
devDependencies["@types/better-sqlite3"] = "^7.6.0";
|
|
134
|
+
} else if (options.dialect === "mysql") {
|
|
135
|
+
dependencies["mysql2"] = "^3.11.0";
|
|
136
|
+
} else {
|
|
137
|
+
dependencies["pg"] = "^8.13.0";
|
|
138
|
+
devDependencies["@types/pg"] = "^8.11.0";
|
|
139
|
+
}
|
|
110
140
|
}
|
|
111
141
|
const pkg = {
|
|
112
142
|
name: options.name === "." ? "my-fossyl-api" : options.name,
|
|
@@ -147,8 +177,17 @@ PORT=3000
|
|
|
147
177
|
if (options.database === "kysely") {
|
|
148
178
|
content += `
|
|
149
179
|
# Database
|
|
150
|
-
DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
|
151
180
|
`;
|
|
181
|
+
if (options.dialect === "sqlite") {
|
|
182
|
+
content += `DATABASE_PATH=./data/app.db
|
|
183
|
+
`;
|
|
184
|
+
} else if (options.dialect === "mysql") {
|
|
185
|
+
content += `DATABASE_URL=mysql://user:password@localhost:3306/mydb
|
|
186
|
+
`;
|
|
187
|
+
} else {
|
|
188
|
+
content += `DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
|
189
|
+
`;
|
|
190
|
+
}
|
|
152
191
|
}
|
|
153
192
|
return content;
|
|
154
193
|
}
|
|
@@ -394,7 +433,39 @@ export function startServer(routes: Route[], port: number): void {
|
|
|
394
433
|
}
|
|
395
434
|
|
|
396
435
|
// src/templates/database/kysely.ts
|
|
397
|
-
function generateKyselySetup() {
|
|
436
|
+
function generateKyselySetup(dialect = "postgres") {
|
|
437
|
+
if (dialect === "sqlite") {
|
|
438
|
+
return `import Database from 'better-sqlite3';
|
|
439
|
+
import { Kysely, SqliteDialect } from 'kysely';
|
|
440
|
+
import type { DB } from './types/db';
|
|
441
|
+
|
|
442
|
+
const databasePath = process.env.DATABASE_PATH || './data/app.db';
|
|
443
|
+
|
|
444
|
+
export const db = new Kysely<DB>({
|
|
445
|
+
dialect: new SqliteDialect({
|
|
446
|
+
database: new Database(databasePath),
|
|
447
|
+
}),
|
|
448
|
+
});
|
|
449
|
+
`;
|
|
450
|
+
}
|
|
451
|
+
if (dialect === "mysql") {
|
|
452
|
+
return `import { createPool } from 'mysql2';
|
|
453
|
+
import { Kysely, MysqlDialect } from 'kysely';
|
|
454
|
+
import type { DB } from './types/db';
|
|
455
|
+
|
|
456
|
+
const connectionString = process.env.DATABASE_URL;
|
|
457
|
+
|
|
458
|
+
if (!connectionString) {
|
|
459
|
+
throw new Error('DATABASE_URL environment variable is required');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export const db = new Kysely<DB>({
|
|
463
|
+
dialect: new MysqlDialect({
|
|
464
|
+
pool: createPool(connectionString),
|
|
465
|
+
}),
|
|
466
|
+
});
|
|
467
|
+
`;
|
|
468
|
+
}
|
|
398
469
|
return `import { Kysely, PostgresDialect } from 'kysely';
|
|
399
470
|
import { Pool } from 'pg';
|
|
400
471
|
import type { DB } from './types/db';
|
|
@@ -442,7 +513,53 @@ export const migrations = createMigrationProvider({
|
|
|
442
513
|
});
|
|
443
514
|
`;
|
|
444
515
|
}
|
|
445
|
-
function generatePingMigration() {
|
|
516
|
+
function generatePingMigration(dialect = "postgres") {
|
|
517
|
+
if (dialect === "sqlite") {
|
|
518
|
+
return `import { sql } from 'kysely';
|
|
519
|
+
import { defineMigration } from '@fossyl/kysely';
|
|
520
|
+
|
|
521
|
+
export const migration = defineMigration({
|
|
522
|
+
async up(db) {
|
|
523
|
+
await db.schema
|
|
524
|
+
.createTable('ping')
|
|
525
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
526
|
+
.addColumn('message', 'text', (col) => col.notNull())
|
|
527
|
+
.addColumn('created_by', 'text', (col) => col.notNull())
|
|
528
|
+
.addColumn('created_at', 'text', (col) =>
|
|
529
|
+
col.notNull().defaultTo(sql\`(datetime('now'))\`)
|
|
530
|
+
)
|
|
531
|
+
.execute();
|
|
532
|
+
},
|
|
533
|
+
|
|
534
|
+
async down(db) {
|
|
535
|
+
await db.schema.dropTable('ping').execute();
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
`;
|
|
539
|
+
}
|
|
540
|
+
if (dialect === "mysql") {
|
|
541
|
+
return `import { sql } from 'kysely';
|
|
542
|
+
import { defineMigration } from '@fossyl/kysely';
|
|
543
|
+
|
|
544
|
+
export const migration = defineMigration({
|
|
545
|
+
async up(db) {
|
|
546
|
+
await db.schema
|
|
547
|
+
.createTable('ping')
|
|
548
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
549
|
+
.addColumn('message', 'varchar(255)', (col) => col.notNull())
|
|
550
|
+
.addColumn('created_by', 'varchar(255)', (col) => col.notNull())
|
|
551
|
+
.addColumn('created_at', 'timestamp', (col) =>
|
|
552
|
+
col.notNull().defaultTo(sql\`CURRENT_TIMESTAMP\`)
|
|
553
|
+
)
|
|
554
|
+
.execute();
|
|
555
|
+
},
|
|
556
|
+
|
|
557
|
+
async down(db) {
|
|
558
|
+
await db.schema.dropTable('ping').execute();
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
`;
|
|
562
|
+
}
|
|
446
563
|
return `import { sql } from 'kysely';
|
|
447
564
|
import { defineMigration } from '@fossyl/kysely';
|
|
448
565
|
|
|
@@ -873,6 +990,98 @@ export async function remove(id: string): Promise<void> {
|
|
|
873
990
|
`;
|
|
874
991
|
}
|
|
875
992
|
|
|
993
|
+
// src/templates/docker.ts
|
|
994
|
+
function generateDockerfile() {
|
|
995
|
+
return `FROM node:20-alpine
|
|
996
|
+
WORKDIR /app
|
|
997
|
+
COPY package*.json pnpm-lock.yaml* ./
|
|
998
|
+
RUN corepack enable && pnpm install --frozen-lockfile
|
|
999
|
+
COPY . .
|
|
1000
|
+
RUN pnpm build
|
|
1001
|
+
EXPOSE 3000
|
|
1002
|
+
CMD ["node", "dist/index.js"]
|
|
1003
|
+
`;
|
|
1004
|
+
}
|
|
1005
|
+
function generateDockerignore() {
|
|
1006
|
+
return `node_modules
|
|
1007
|
+
dist
|
|
1008
|
+
*.log
|
|
1009
|
+
.env
|
|
1010
|
+
.git
|
|
1011
|
+
`;
|
|
1012
|
+
}
|
|
1013
|
+
function generateDockerCompose(dialect) {
|
|
1014
|
+
if (dialect === "sqlite") {
|
|
1015
|
+
return `services:
|
|
1016
|
+
app:
|
|
1017
|
+
build: .
|
|
1018
|
+
ports:
|
|
1019
|
+
- "\${PORT:-3000}:3000"
|
|
1020
|
+
volumes:
|
|
1021
|
+
- ./data:/app/data
|
|
1022
|
+
environment:
|
|
1023
|
+
- DATABASE_PATH=/app/data/app.db
|
|
1024
|
+
`;
|
|
1025
|
+
}
|
|
1026
|
+
if (dialect === "mysql") {
|
|
1027
|
+
return `services:
|
|
1028
|
+
app:
|
|
1029
|
+
build: .
|
|
1030
|
+
ports:
|
|
1031
|
+
- "\${PORT:-3000}:3000"
|
|
1032
|
+
depends_on:
|
|
1033
|
+
db:
|
|
1034
|
+
condition: service_healthy
|
|
1035
|
+
environment:
|
|
1036
|
+
- DATABASE_URL=mysql://fossyl:fossyl@db:3306/fossyl
|
|
1037
|
+
db:
|
|
1038
|
+
image: mysql:8
|
|
1039
|
+
environment:
|
|
1040
|
+
MYSQL_ROOT_PASSWORD: root
|
|
1041
|
+
MYSQL_USER: fossyl
|
|
1042
|
+
MYSQL_PASSWORD: fossyl
|
|
1043
|
+
MYSQL_DATABASE: fossyl
|
|
1044
|
+
volumes:
|
|
1045
|
+
- mysqldata:/var/lib/mysql
|
|
1046
|
+
healthcheck:
|
|
1047
|
+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
|
1048
|
+
interval: 5s
|
|
1049
|
+
timeout: 5s
|
|
1050
|
+
retries: 5
|
|
1051
|
+
|
|
1052
|
+
volumes:
|
|
1053
|
+
mysqldata:
|
|
1054
|
+
`;
|
|
1055
|
+
}
|
|
1056
|
+
return `services:
|
|
1057
|
+
app:
|
|
1058
|
+
build: .
|
|
1059
|
+
ports:
|
|
1060
|
+
- "\${PORT:-3000}:3000"
|
|
1061
|
+
depends_on:
|
|
1062
|
+
db:
|
|
1063
|
+
condition: service_healthy
|
|
1064
|
+
environment:
|
|
1065
|
+
- DATABASE_URL=postgres://fossyl:fossyl@db:5432/fossyl
|
|
1066
|
+
db:
|
|
1067
|
+
image: postgres:16-alpine
|
|
1068
|
+
environment:
|
|
1069
|
+
POSTGRES_USER: fossyl
|
|
1070
|
+
POSTGRES_PASSWORD: fossyl
|
|
1071
|
+
POSTGRES_DB: fossyl
|
|
1072
|
+
volumes:
|
|
1073
|
+
- pgdata:/var/lib/postgresql/data
|
|
1074
|
+
healthcheck:
|
|
1075
|
+
test: ["CMD-SHELL", "pg_isready -U fossyl"]
|
|
1076
|
+
interval: 5s
|
|
1077
|
+
timeout: 5s
|
|
1078
|
+
retries: 5
|
|
1079
|
+
|
|
1080
|
+
volumes:
|
|
1081
|
+
pgdata:
|
|
1082
|
+
`;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
876
1085
|
// src/scaffold.ts
|
|
877
1086
|
function generateFiles(options) {
|
|
878
1087
|
const files = [];
|
|
@@ -910,7 +1119,7 @@ function generateFiles(options) {
|
|
|
910
1119
|
if (options.database === "kysely") {
|
|
911
1120
|
files.push({
|
|
912
1121
|
path: "src/db.ts",
|
|
913
|
-
content: generateKyselySetup()
|
|
1122
|
+
content: generateKyselySetup(options.dialect)
|
|
914
1123
|
});
|
|
915
1124
|
files.push({
|
|
916
1125
|
path: "src/types/db.ts",
|
|
@@ -922,7 +1131,7 @@ function generateFiles(options) {
|
|
|
922
1131
|
});
|
|
923
1132
|
files.push({
|
|
924
1133
|
path: "src/migrations/001_create_ping.ts",
|
|
925
|
-
content: generatePingMigration()
|
|
1134
|
+
content: generatePingMigration(options.dialect)
|
|
926
1135
|
});
|
|
927
1136
|
} else {
|
|
928
1137
|
files.push({
|
|
@@ -957,6 +1166,20 @@ function generateFiles(options) {
|
|
|
957
1166
|
path: "src/features/ping/repo/ping.repo.ts",
|
|
958
1167
|
content: generatePingRepo(options)
|
|
959
1168
|
});
|
|
1169
|
+
if (options.docker) {
|
|
1170
|
+
files.push({
|
|
1171
|
+
path: "Dockerfile",
|
|
1172
|
+
content: generateDockerfile()
|
|
1173
|
+
});
|
|
1174
|
+
files.push({
|
|
1175
|
+
path: ".dockerignore",
|
|
1176
|
+
content: generateDockerignore()
|
|
1177
|
+
});
|
|
1178
|
+
files.push({
|
|
1179
|
+
path: "docker-compose.yml",
|
|
1180
|
+
content: generateDockerCompose(options.dialect)
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
960
1183
|
return files;
|
|
961
1184
|
}
|
|
962
1185
|
function writeFiles(projectPath, files) {
|
package/dist/index.mjs
CHANGED
|
@@ -52,7 +52,30 @@ async function promptForOptions(projectName) {
|
|
|
52
52
|
p.cancel("Operation cancelled.");
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
let dialect;
|
|
56
|
+
if (database === "kysely") {
|
|
57
|
+
dialect = await p.select({
|
|
58
|
+
message: "Database dialect:",
|
|
59
|
+
options: [
|
|
60
|
+
{ value: "sqlite", label: "SQLite", hint: "recommended - great for per-customer databases" },
|
|
61
|
+
{ value: "postgres", label: "PostgreSQL" },
|
|
62
|
+
{ value: "mysql", label: "MySQL" }
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
if (p.isCancel(dialect)) {
|
|
66
|
+
p.cancel("Operation cancelled.");
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const docker = await p.confirm({
|
|
71
|
+
message: "Include Docker setup?",
|
|
72
|
+
initialValue: true
|
|
73
|
+
});
|
|
74
|
+
if (p.isCancel(docker)) {
|
|
75
|
+
p.cancel("Operation cancelled.");
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return { name, server, validator, database, dialect, docker };
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
// src/scaffold.ts
|
|
@@ -81,8 +104,15 @@ function generatePackageJson(options) {
|
|
|
81
104
|
if (options.database === "kysely") {
|
|
82
105
|
dependencies["@fossyl/kysely"] = "^0.9.0";
|
|
83
106
|
dependencies["kysely"] = "^0.27.0";
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
if (options.dialect === "sqlite") {
|
|
108
|
+
dependencies["better-sqlite3"] = "^11.0.0";
|
|
109
|
+
devDependencies["@types/better-sqlite3"] = "^7.6.0";
|
|
110
|
+
} else if (options.dialect === "mysql") {
|
|
111
|
+
dependencies["mysql2"] = "^3.11.0";
|
|
112
|
+
} else {
|
|
113
|
+
dependencies["pg"] = "^8.13.0";
|
|
114
|
+
devDependencies["@types/pg"] = "^8.11.0";
|
|
115
|
+
}
|
|
86
116
|
}
|
|
87
117
|
const pkg = {
|
|
88
118
|
name: options.name === "." ? "my-fossyl-api" : options.name,
|
|
@@ -123,8 +153,17 @@ PORT=3000
|
|
|
123
153
|
if (options.database === "kysely") {
|
|
124
154
|
content += `
|
|
125
155
|
# Database
|
|
126
|
-
DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
|
127
156
|
`;
|
|
157
|
+
if (options.dialect === "sqlite") {
|
|
158
|
+
content += `DATABASE_PATH=./data/app.db
|
|
159
|
+
`;
|
|
160
|
+
} else if (options.dialect === "mysql") {
|
|
161
|
+
content += `DATABASE_URL=mysql://user:password@localhost:3306/mydb
|
|
162
|
+
`;
|
|
163
|
+
} else {
|
|
164
|
+
content += `DATABASE_URL=postgres://user:password@localhost:5432/mydb
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
128
167
|
}
|
|
129
168
|
return content;
|
|
130
169
|
}
|
|
@@ -370,7 +409,39 @@ export function startServer(routes: Route[], port: number): void {
|
|
|
370
409
|
}
|
|
371
410
|
|
|
372
411
|
// src/templates/database/kysely.ts
|
|
373
|
-
function generateKyselySetup() {
|
|
412
|
+
function generateKyselySetup(dialect = "postgres") {
|
|
413
|
+
if (dialect === "sqlite") {
|
|
414
|
+
return `import Database from 'better-sqlite3';
|
|
415
|
+
import { Kysely, SqliteDialect } from 'kysely';
|
|
416
|
+
import type { DB } from './types/db';
|
|
417
|
+
|
|
418
|
+
const databasePath = process.env.DATABASE_PATH || './data/app.db';
|
|
419
|
+
|
|
420
|
+
export const db = new Kysely<DB>({
|
|
421
|
+
dialect: new SqliteDialect({
|
|
422
|
+
database: new Database(databasePath),
|
|
423
|
+
}),
|
|
424
|
+
});
|
|
425
|
+
`;
|
|
426
|
+
}
|
|
427
|
+
if (dialect === "mysql") {
|
|
428
|
+
return `import { createPool } from 'mysql2';
|
|
429
|
+
import { Kysely, MysqlDialect } from 'kysely';
|
|
430
|
+
import type { DB } from './types/db';
|
|
431
|
+
|
|
432
|
+
const connectionString = process.env.DATABASE_URL;
|
|
433
|
+
|
|
434
|
+
if (!connectionString) {
|
|
435
|
+
throw new Error('DATABASE_URL environment variable is required');
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export const db = new Kysely<DB>({
|
|
439
|
+
dialect: new MysqlDialect({
|
|
440
|
+
pool: createPool(connectionString),
|
|
441
|
+
}),
|
|
442
|
+
});
|
|
443
|
+
`;
|
|
444
|
+
}
|
|
374
445
|
return `import { Kysely, PostgresDialect } from 'kysely';
|
|
375
446
|
import { Pool } from 'pg';
|
|
376
447
|
import type { DB } from './types/db';
|
|
@@ -418,7 +489,53 @@ export const migrations = createMigrationProvider({
|
|
|
418
489
|
});
|
|
419
490
|
`;
|
|
420
491
|
}
|
|
421
|
-
function generatePingMigration() {
|
|
492
|
+
function generatePingMigration(dialect = "postgres") {
|
|
493
|
+
if (dialect === "sqlite") {
|
|
494
|
+
return `import { sql } from 'kysely';
|
|
495
|
+
import { defineMigration } from '@fossyl/kysely';
|
|
496
|
+
|
|
497
|
+
export const migration = defineMigration({
|
|
498
|
+
async up(db) {
|
|
499
|
+
await db.schema
|
|
500
|
+
.createTable('ping')
|
|
501
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
502
|
+
.addColumn('message', 'text', (col) => col.notNull())
|
|
503
|
+
.addColumn('created_by', 'text', (col) => col.notNull())
|
|
504
|
+
.addColumn('created_at', 'text', (col) =>
|
|
505
|
+
col.notNull().defaultTo(sql\`(datetime('now'))\`)
|
|
506
|
+
)
|
|
507
|
+
.execute();
|
|
508
|
+
},
|
|
509
|
+
|
|
510
|
+
async down(db) {
|
|
511
|
+
await db.schema.dropTable('ping').execute();
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
516
|
+
if (dialect === "mysql") {
|
|
517
|
+
return `import { sql } from 'kysely';
|
|
518
|
+
import { defineMigration } from '@fossyl/kysely';
|
|
519
|
+
|
|
520
|
+
export const migration = defineMigration({
|
|
521
|
+
async up(db) {
|
|
522
|
+
await db.schema
|
|
523
|
+
.createTable('ping')
|
|
524
|
+
.addColumn('id', 'varchar(36)', (col) => col.primaryKey())
|
|
525
|
+
.addColumn('message', 'varchar(255)', (col) => col.notNull())
|
|
526
|
+
.addColumn('created_by', 'varchar(255)', (col) => col.notNull())
|
|
527
|
+
.addColumn('created_at', 'timestamp', (col) =>
|
|
528
|
+
col.notNull().defaultTo(sql\`CURRENT_TIMESTAMP\`)
|
|
529
|
+
)
|
|
530
|
+
.execute();
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
async down(db) {
|
|
534
|
+
await db.schema.dropTable('ping').execute();
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
`;
|
|
538
|
+
}
|
|
422
539
|
return `import { sql } from 'kysely';
|
|
423
540
|
import { defineMigration } from '@fossyl/kysely';
|
|
424
541
|
|
|
@@ -849,6 +966,98 @@ export async function remove(id: string): Promise<void> {
|
|
|
849
966
|
`;
|
|
850
967
|
}
|
|
851
968
|
|
|
969
|
+
// src/templates/docker.ts
|
|
970
|
+
function generateDockerfile() {
|
|
971
|
+
return `FROM node:20-alpine
|
|
972
|
+
WORKDIR /app
|
|
973
|
+
COPY package*.json pnpm-lock.yaml* ./
|
|
974
|
+
RUN corepack enable && pnpm install --frozen-lockfile
|
|
975
|
+
COPY . .
|
|
976
|
+
RUN pnpm build
|
|
977
|
+
EXPOSE 3000
|
|
978
|
+
CMD ["node", "dist/index.js"]
|
|
979
|
+
`;
|
|
980
|
+
}
|
|
981
|
+
function generateDockerignore() {
|
|
982
|
+
return `node_modules
|
|
983
|
+
dist
|
|
984
|
+
*.log
|
|
985
|
+
.env
|
|
986
|
+
.git
|
|
987
|
+
`;
|
|
988
|
+
}
|
|
989
|
+
function generateDockerCompose(dialect) {
|
|
990
|
+
if (dialect === "sqlite") {
|
|
991
|
+
return `services:
|
|
992
|
+
app:
|
|
993
|
+
build: .
|
|
994
|
+
ports:
|
|
995
|
+
- "\${PORT:-3000}:3000"
|
|
996
|
+
volumes:
|
|
997
|
+
- ./data:/app/data
|
|
998
|
+
environment:
|
|
999
|
+
- DATABASE_PATH=/app/data/app.db
|
|
1000
|
+
`;
|
|
1001
|
+
}
|
|
1002
|
+
if (dialect === "mysql") {
|
|
1003
|
+
return `services:
|
|
1004
|
+
app:
|
|
1005
|
+
build: .
|
|
1006
|
+
ports:
|
|
1007
|
+
- "\${PORT:-3000}:3000"
|
|
1008
|
+
depends_on:
|
|
1009
|
+
db:
|
|
1010
|
+
condition: service_healthy
|
|
1011
|
+
environment:
|
|
1012
|
+
- DATABASE_URL=mysql://fossyl:fossyl@db:3306/fossyl
|
|
1013
|
+
db:
|
|
1014
|
+
image: mysql:8
|
|
1015
|
+
environment:
|
|
1016
|
+
MYSQL_ROOT_PASSWORD: root
|
|
1017
|
+
MYSQL_USER: fossyl
|
|
1018
|
+
MYSQL_PASSWORD: fossyl
|
|
1019
|
+
MYSQL_DATABASE: fossyl
|
|
1020
|
+
volumes:
|
|
1021
|
+
- mysqldata:/var/lib/mysql
|
|
1022
|
+
healthcheck:
|
|
1023
|
+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
|
1024
|
+
interval: 5s
|
|
1025
|
+
timeout: 5s
|
|
1026
|
+
retries: 5
|
|
1027
|
+
|
|
1028
|
+
volumes:
|
|
1029
|
+
mysqldata:
|
|
1030
|
+
`;
|
|
1031
|
+
}
|
|
1032
|
+
return `services:
|
|
1033
|
+
app:
|
|
1034
|
+
build: .
|
|
1035
|
+
ports:
|
|
1036
|
+
- "\${PORT:-3000}:3000"
|
|
1037
|
+
depends_on:
|
|
1038
|
+
db:
|
|
1039
|
+
condition: service_healthy
|
|
1040
|
+
environment:
|
|
1041
|
+
- DATABASE_URL=postgres://fossyl:fossyl@db:5432/fossyl
|
|
1042
|
+
db:
|
|
1043
|
+
image: postgres:16-alpine
|
|
1044
|
+
environment:
|
|
1045
|
+
POSTGRES_USER: fossyl
|
|
1046
|
+
POSTGRES_PASSWORD: fossyl
|
|
1047
|
+
POSTGRES_DB: fossyl
|
|
1048
|
+
volumes:
|
|
1049
|
+
- pgdata:/var/lib/postgresql/data
|
|
1050
|
+
healthcheck:
|
|
1051
|
+
test: ["CMD-SHELL", "pg_isready -U fossyl"]
|
|
1052
|
+
interval: 5s
|
|
1053
|
+
timeout: 5s
|
|
1054
|
+
retries: 5
|
|
1055
|
+
|
|
1056
|
+
volumes:
|
|
1057
|
+
pgdata:
|
|
1058
|
+
`;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
852
1061
|
// src/scaffold.ts
|
|
853
1062
|
function generateFiles(options) {
|
|
854
1063
|
const files = [];
|
|
@@ -886,7 +1095,7 @@ function generateFiles(options) {
|
|
|
886
1095
|
if (options.database === "kysely") {
|
|
887
1096
|
files.push({
|
|
888
1097
|
path: "src/db.ts",
|
|
889
|
-
content: generateKyselySetup()
|
|
1098
|
+
content: generateKyselySetup(options.dialect)
|
|
890
1099
|
});
|
|
891
1100
|
files.push({
|
|
892
1101
|
path: "src/types/db.ts",
|
|
@@ -898,7 +1107,7 @@ function generateFiles(options) {
|
|
|
898
1107
|
});
|
|
899
1108
|
files.push({
|
|
900
1109
|
path: "src/migrations/001_create_ping.ts",
|
|
901
|
-
content: generatePingMigration()
|
|
1110
|
+
content: generatePingMigration(options.dialect)
|
|
902
1111
|
});
|
|
903
1112
|
} else {
|
|
904
1113
|
files.push({
|
|
@@ -933,6 +1142,20 @@ function generateFiles(options) {
|
|
|
933
1142
|
path: "src/features/ping/repo/ping.repo.ts",
|
|
934
1143
|
content: generatePingRepo(options)
|
|
935
1144
|
});
|
|
1145
|
+
if (options.docker) {
|
|
1146
|
+
files.push({
|
|
1147
|
+
path: "Dockerfile",
|
|
1148
|
+
content: generateDockerfile()
|
|
1149
|
+
});
|
|
1150
|
+
files.push({
|
|
1151
|
+
path: ".dockerignore",
|
|
1152
|
+
content: generateDockerignore()
|
|
1153
|
+
});
|
|
1154
|
+
files.push({
|
|
1155
|
+
path: "docker-compose.yml",
|
|
1156
|
+
content: generateDockerCompose(options.dialect)
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
936
1159
|
return files;
|
|
937
1160
|
}
|
|
938
1161
|
function writeFiles(projectPath, files) {
|