forgestack-os-cli 0.1.0 → 0.1.1

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.
@@ -1,377 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs-extra';
3
- import { StackConfig } from '../types';
4
-
5
- export async function generateCommon(config: StackConfig, targetDir: string) {
6
- // Generate root package.json
7
- const rootPackageJson = {
8
- name: config.projectName,
9
- version: '0.1.0',
10
- private: true,
11
- workspaces: ['frontend', 'backend'],
12
- scripts: {
13
- dev: 'npm run dev --workspace=frontend & npm run dev --workspace=backend',
14
- 'dev:frontend': 'npm run dev --workspace=frontend',
15
- 'dev:backend': 'npm run dev --workspace=backend',
16
- build: 'npm run build --workspaces',
17
- test: 'npm run test --workspaces',
18
- },
19
- author: 'Generated by ForgeStack OS',
20
- license: 'MIT',
21
- };
22
-
23
- await fs.writeJSON(path.join(targetDir, 'package.json'), rootPackageJson, { spaces: 2 });
24
-
25
- // Generate .gitignore
26
- const gitignore = `# Dependencies
27
- node_modules/
28
- .pnp
29
- .pnp.js
30
-
31
- # Testing
32
- coverage/
33
-
34
- # Production
35
- build/
36
- dist/
37
- .next/
38
- out/
39
-
40
- # Environment
41
- .env
42
- .env.local
43
- .env.*.local
44
-
45
- # Logs
46
- logs/
47
- *.log
48
- npm-debug.log*
49
- yarn-debug.log*
50
- yarn-error.log*
51
-
52
- # OS
53
- .DS_Store
54
- Thumbs.db
55
-
56
- # IDE
57
- .vscode/
58
- .idea/
59
- *.swp
60
- *.swo
61
-
62
- # Database
63
- *.db
64
- *.sqlite
65
- prisma/migrations/
66
-
67
- # Docker
68
- docker-compose.override.yml
69
- `;
70
-
71
- await fs.writeFile(path.join(targetDir, '.gitignore'), gitignore);
72
-
73
- // Generate README.md
74
- const readme = generateReadme(config);
75
- await fs.writeFile(path.join(targetDir, 'README.md'), readme);
76
-
77
- // Generate .env.example
78
- const envExample = generateEnvExample(config);
79
- await fs.writeFile(path.join(targetDir, '.env.example'), envExample);
80
- }
81
-
82
- function generateReadme(config: StackConfig): string {
83
- return `# ${config.projectName}
84
-
85
- Generated by **ForgeStack OS** - One platform. Any stack. Production-ready.
86
-
87
- ## Stack
88
-
89
- - **Frontend:** ${config.frontend}
90
- - **Backend:** ${config.backend}
91
- - **Auth:** ${config.auth}
92
- - **Database:** ${config.database}
93
- - **API Style:** ${config.apiStyle}
94
- - **Docker:** ${config.docker ? 'Yes' : 'No'}
95
- - **Multi-Tenant:** ${config.multiTenant ? 'Yes' : 'No'}
96
-
97
- ## Getting Started
98
-
99
- ### Prerequisites
100
-
101
- - Node.js 18+ (or Bun if using Bun backend)
102
- ${config.backend === 'go-fiber' ? '- Go 1.21+' : ''}
103
- ${config.docker ? '- Docker and Docker Compose' : ''}
104
- ${config.database === 'postgresql' ? '- PostgreSQL (or use Docker)' : ''}
105
- ${config.database === 'mongodb' ? '- MongoDB (or use Docker)' : ''}
106
-
107
- ### Installation
108
-
109
- \`\`\`bash
110
- # Install dependencies
111
- npm install
112
-
113
- # Copy environment variables
114
- cp .env.example .env
115
-
116
- # Update .env with your configuration
117
- \`\`\`
118
-
119
- ### Database Setup
120
-
121
- ${getDatabaseSetupInstructions(config)}
122
-
123
- ### Development
124
-
125
- \`\`\`bash
126
- # Run both frontend and backend
127
- npm run dev
128
-
129
- # Or run separately
130
- npm run dev:frontend
131
- npm run dev:backend
132
- \`\`\`
133
-
134
- ${config.docker ? `### Docker
135
-
136
- \`\`\`bash
137
- # Start all services
138
- docker-compose up
139
-
140
- # Start in detached mode
141
- docker-compose up -d
142
-
143
- # Stop services
144
- docker-compose down
145
- \`\`\`
146
- ` : ''}
147
-
148
- ## Project Structure
149
-
150
- \`\`\`
151
- ${config.projectName}/
152
- ├── frontend/ # ${config.frontend} application
153
- ├── backend/ # ${config.backend} server
154
- ${config.docker ? '├── docker/ # Docker configuration' : ''}
155
- ├── .env.example # Environment variables template
156
- └── README.md # This file
157
- \`\`\`
158
-
159
- ## Authentication
160
-
161
- ${getAuthInstructions(config)}
162
-
163
- ${config.multiTenant ? `## Multi-Tenancy
164
-
165
- This application is configured with multi-tenancy support. Each request is automatically scoped to a tenant based on:
166
-
167
- ${getTenantScopingInfo(config)}
168
-
169
- All database queries include tenant isolation to ensure data security.
170
- ` : ''}
171
-
172
- ## Built With
173
-
174
- - [ForgeStack OS](https://github.com/halloffame12/forgestack-os) - The meta-platform that generated this project
175
- - Created by [Sumit Chauhan](https://github.com/halloffame12)
176
-
177
- ## License
178
-
179
- MIT
180
- `;
181
- }
182
-
183
- function getDatabaseSetupInstructions(config: StackConfig): string {
184
- switch (config.database) {
185
- case 'postgresql':
186
- return `\`\`\`bash
187
- # Using Docker
188
- docker-compose up -d postgres
189
-
190
- # Or install PostgreSQL locally and create a database
191
- createdb ${config.projectName}
192
-
193
- # Run migrations
194
- cd backend
195
- npx prisma migrate dev
196
- \`\`\``;
197
-
198
- case 'mongodb':
199
- return `\`\`\`bash
200
- # Using Docker
201
- docker-compose up -d mongodb
202
-
203
- # Or install MongoDB locally
204
- # No migrations needed - Mongoose handles schema
205
- \`\`\``;
206
-
207
- case 'mysql':
208
- return `\`\`\`bash
209
- # Using Docker
210
- docker-compose up -d mysql
211
-
212
- # Or install MySQL locally and create a database
213
- mysql -u root -p -e "CREATE DATABASE ${config.projectName};"
214
-
215
- # Run migrations
216
- cd backend
217
- npx prisma migrate dev
218
- \`\`\``;
219
-
220
- case 'sqlite':
221
- return `\`\`\`bash
222
- # SQLite is file-based, no setup needed
223
- # Database file will be created automatically
224
- cd backend
225
- npx prisma migrate dev
226
- \`\`\``;
227
-
228
- case 'supabase-db':
229
- return `\`\`\`bash
230
- # Create a Supabase project at https://supabase.com
231
- # Copy your connection string to .env
232
- # Run migrations through Supabase dashboard or CLI
233
- \`\`\``;
234
-
235
- default:
236
- return 'See backend/README.md for database setup instructions.';
237
- }
238
- }
239
-
240
- function getAuthInstructions(config: StackConfig): string {
241
- switch (config.auth) {
242
- case 'jwt':
243
- return `This project uses built-in JWT authentication. Users can register and login through the API endpoints:
244
-
245
- - \`POST /api/auth/register\` - Create a new account
246
- - \`POST /api/auth/login\` - Login and receive JWT token
247
- - Include the token in requests: \`Authorization: Bearer <token>\``;
248
-
249
- case 'clerk':
250
- return `This project uses Clerk for authentication.
251
-
252
- 1. Create a Clerk account at https://clerk.com
253
- 2. Create a new application
254
- 3. Copy your API keys to \`.env\`
255
- 4. Configure allowed redirect URLs in Clerk dashboard`;
256
-
257
- case 'supabase':
258
- return `This project uses Supabase Auth.
259
-
260
- 1. Create a Supabase project at https://supabase.com
261
- 2. Copy your project URL and anon key to \`.env\`
262
- 3. Configure auth providers in Supabase dashboard`;
263
-
264
- case 'authjs':
265
- return `This project uses Auth.js (NextAuth).
266
-
267
- 1. Configure providers in \`backend/src/auth/config.ts\`
268
- 2. Add provider credentials to \`.env\`
269
- 3. See https://authjs.dev for provider setup guides`;
270
-
271
- case 'firebase':
272
- return `This project uses Firebase Auth.
273
-
274
- 1. Create a Firebase project at https://firebase.google.com
275
- 2. Enable authentication methods
276
- 3. Copy your config to \`.env\``;
277
-
278
- default:
279
- return 'See documentation for authentication setup.';
280
- }
281
- }
282
-
283
- function getTenantScopingInfo(config: StackConfig): string {
284
- switch (config.auth) {
285
- case 'clerk':
286
- return '- Clerk organization ID is used as tenant ID\n- Organization membership determines access';
287
- case 'supabase':
288
- return '- Supabase user metadata contains tenant_id\n- Row Level Security (RLS) policies enforce isolation';
289
- case 'jwt':
290
- return '- JWT token contains tenant_id claim\n- Middleware extracts and validates tenant context';
291
- default:
292
- return '- Authentication provider includes tenant information\n- Middleware enforces tenant isolation';
293
- }
294
- }
295
-
296
- function generateEnvExample(config: StackConfig): string {
297
- let env = `# Database
298
- DATABASE_URL="${getDatabaseUrl(config)}"
299
-
300
- # Server
301
- PORT=3000
302
- NODE_ENV=development
303
-
304
- `;
305
-
306
- // Auth-specific variables
307
- switch (config.auth) {
308
- case 'jwt':
309
- env += `# JWT Authentication
310
- JWT_SECRET=your-super-secret-jwt-key-change-this
311
- JWT_EXPIRES_IN=7d
312
- `;
313
- break;
314
-
315
- case 'clerk':
316
- env += `# Clerk Authentication
317
- CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
318
- CLERK_SECRET_KEY=sk_test_xxxxx
319
- NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
320
- `;
321
- break;
322
-
323
- case 'supabase':
324
- env += `# Supabase
325
- SUPABASE_URL=https://xxxxx.supabase.co
326
- SUPABASE_ANON_KEY=xxxxx
327
- SUPABASE_SERVICE_ROLE_KEY=xxxxx
328
- NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
329
- NEXT_PUBLIC_SUPABASE_ANON_KEY=xxxxx
330
- `;
331
- break;
332
-
333
- case 'authjs':
334
- env += `# Auth.js
335
- NEXTAUTH_URL=http://localhost:3000
336
- NEXTAUTH_SECRET=your-secret-key-change-this
337
- GOOGLE_CLIENT_ID=xxxxx
338
- GOOGLE_CLIENT_SECRET=xxxxx
339
- `;
340
- break;
341
-
342
- case 'firebase':
343
- env += `# Firebase
344
- FIREBASE_API_KEY=xxxxx
345
- FIREBASE_AUTH_DOMAIN=xxxxx.firebaseapp.com
346
- FIREBASE_PROJECT_ID=xxxxx
347
- NEXT_PUBLIC_FIREBASE_API_KEY=xxxxx
348
- NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=xxxxx.firebaseapp.com
349
- NEXT_PUBLIC_FIREBASE_PROJECT_ID=xxxxx
350
- `;
351
- break;
352
- }
353
-
354
- // Frontend URL
355
- env += `\n# Frontend
356
- FRONTEND_URL=http://localhost:5173
357
- `;
358
-
359
- return env;
360
- }
361
-
362
- function getDatabaseUrl(config: StackConfig): string {
363
- switch (config.database) {
364
- case 'postgresql':
365
- return 'postgresql://user:password@localhost:5432/dbname';
366
- case 'mongodb':
367
- return 'mongodb://localhost:27017/dbname';
368
- case 'mysql':
369
- return 'mysql://user:password@localhost:3306/dbname';
370
- case 'sqlite':
371
- return 'file:./dev.db';
372
- case 'supabase-db':
373
- return 'postgresql://postgres:password@db.xxxxx.supabase.co:5432/postgres';
374
- default:
375
- return 'your-database-connection-string';
376
- }
377
- }
@@ -1,165 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs-extra';
3
- import { StackConfig } from '../types';
4
-
5
- export async function generateDatabase(config: StackConfig, backendDir: string) {
6
- switch (config.database) {
7
- case 'postgresql':
8
- case 'mysql':
9
- case 'sqlite':
10
- await generatePrisma(config, backendDir);
11
- break;
12
- case 'mongodb':
13
- await generateMongoose(config, backendDir);
14
- break;
15
- case 'supabase-db':
16
- await generateSupabase(config, backendDir);
17
- break;
18
- default:
19
- throw new Error(`Unsupported database: ${config.database}`);
20
- }
21
- }
22
-
23
- async function generatePrisma(config: StackConfig, backendDir: string) {
24
- const prismaDir = path.join(backendDir, 'prisma');
25
- await fs.ensureDir(prismaDir);
26
-
27
- // Prisma schema
28
- const schema = getPrismaSchema(config);
29
- await fs.writeFile(path.join(prismaDir, 'schema.prisma'), schema);
30
-
31
- // Prisma client initialization
32
- const prismaClient = `import { PrismaClient } from '@prisma/client';
33
-
34
- const globalForPrisma = globalThis as unknown as {
35
- prisma: PrismaClient | undefined;
36
- };
37
-
38
- export const prisma =
39
- globalForPrisma.prisma ??
40
- new PrismaClient({
41
- log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
42
- });
43
-
44
- if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
45
-
46
- export default prisma;
47
- `;
48
-
49
- const libDir = path.join(backendDir, 'src', 'lib');
50
- await fs.ensureDir(libDir);
51
- await fs.writeFile(path.join(libDir, 'prisma.ts'), prismaClient);
52
- }
53
-
54
- function getPrismaSchema(config: StackConfig): string {
55
- const datasourceProvider = config.database === 'postgresql' ? 'postgresql'
56
- : config.database === 'mysql' ? 'mysql'
57
- : 'sqlite';
58
-
59
- return `generator client {
60
- provider = "prisma-client-js"
61
- }
62
-
63
- datasource db {
64
- provider = "${datasourceProvider}"
65
- url = env("DATABASE_URL")
66
- }
67
-
68
- ${config.multiTenant ? `model Tenant {
69
- id String @id @default(cuid())
70
- name String
71
- slug String @unique
72
- createdAt DateTime @default(now())
73
- updatedAt DateTime @updatedAt
74
- users User[]
75
- }
76
-
77
- ` : ''}model User {
78
- id String @id @default(cuid())
79
- email String @unique
80
- name String?
81
- password String? ${config.auth === 'jwt' ? '' : '// Only for JWT auth'}
82
- ${config.multiTenant ? 'tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id])\n ' : ''}createdAt DateTime @default(now())
83
- updatedAt DateTime @updatedAt
84
- }
85
- `;
86
- }
87
-
88
- async function generateMongoose(config: StackConfig, backendDir: string) {
89
- // Mongoose connection
90
- const mongooseConnection = `import mongoose from 'mongoose';
91
-
92
- const MONGODB_URI = process.env.DATABASE_URL || 'mongodb://localhost:27017/${config.projectName}';
93
-
94
- let isConnected = false;
95
-
96
- export async function connectDB() {
97
- if (isConnected) {
98
- return;
99
- }
100
-
101
- try {
102
- await mongoose.connect(MONGODB_URI);
103
- isConnected = true;
104
- console.log('✅ MongoDB connected');
105
- } catch (error) {
106
- console.error('❌ MongoDB connection error:', error);
107
- process.exit(1);
108
- }
109
- }
110
-
111
- export default mongoose;
112
- `;
113
-
114
- const libDir = path.join(backendDir, 'src', 'lib');
115
- await fs.ensureDir(libDir);
116
- await fs.writeFile(path.join(libDir, 'mongoose.ts'), mongooseConnection);
117
-
118
- // User model
119
- const modelsDir = path.join(backendDir, 'src', 'models');
120
- await fs.ensureDir(modelsDir);
121
-
122
- const userModel = `import mongoose, { Schema, Document } from 'mongoose';
123
-
124
- export interface IUser extends Document {
125
- email: string;
126
- name?: string;
127
- password?: string;
128
- ${config.multiTenant ? 'tenantId: string;' : ''}
129
- createdAt: Date;
130
- updatedAt: Date;
131
- }
132
-
133
- const UserSchema = new Schema<IUser>(
134
- {
135
- email: { type: String, required: true, unique: true },
136
- name: { type: String },
137
- password: { type: String },
138
- ${config.multiTenant ? 'tenantId: { type: String, required: true, index: true },' : ''}
139
- },
140
- {
141
- timestamps: true,
142
- }
143
- );
144
-
145
- export default mongoose.model<IUser>('User', UserSchema);
146
- `;
147
-
148
- await fs.writeFile(path.join(modelsDir, 'User.ts'), userModel);
149
- }
150
-
151
- async function generateSupabase(config: StackConfig, backendDir: string) {
152
- const supabaseClient = `import { createClient } from '@supabase/supabase-js';
153
-
154
- const supabaseUrl = process.env.SUPABASE_URL!;
155
- const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
156
-
157
- export const supabase = createClient(supabaseUrl, supabaseKey);
158
-
159
- export default supabase;
160
- `;
161
-
162
- const libDir = path.join(backendDir, 'src', 'lib');
163
- await fs.ensureDir(libDir);
164
- await fs.writeFile(path.join(libDir, 'supabase.ts'), supabaseClient);
165
- }
@@ -1,185 +0,0 @@
1
- import path from 'path';
2
- import fs from 'fs-extra';
3
- import { StackConfig } from '../types';
4
-
5
- export async function generateDocker(config: StackConfig, targetDir: string) {
6
- const dockerDir = path.join(targetDir, 'docker');
7
- await fs.ensureDir(dockerDir);
8
-
9
- // Frontend Dockerfile
10
- const frontendDockerfile = `FROM node:20-alpine AS builder
11
-
12
- WORKDIR /app
13
-
14
- COPY package*.json ./
15
- RUN npm install
16
-
17
- COPY . .
18
- RUN npm run build
19
-
20
- FROM nginx:alpine
21
-
22
- COPY --from=builder /app/dist /usr/share/nginx/html
23
- COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
24
-
25
- EXPOSE 80
26
-
27
- CMD ["nginx", "-g", "daemon off;"]
28
- `;
29
-
30
- await fs.writeFile(path.join(dockerDir, 'frontend.Dockerfile'), frontendDockerfile);
31
-
32
- // Backend Dockerfile
33
- const backendDockerfile = `FROM node:20-alpine
34
-
35
- WORKDIR /app
36
-
37
- COPY package*.json ./
38
- RUN npm install --only=production
39
-
40
- COPY . .
41
- RUN npm run build
42
-
43
- EXPOSE 3000
44
-
45
- CMD ["node", "dist/index.js"]
46
- `;
47
-
48
- await fs.writeFile(path.join(dockerDir, 'backend.Dockerfile'), backendDockerfile);
49
-
50
- // Nginx config for frontend
51
- const nginxConf = `server {
52
- listen 80;
53
- server_name localhost;
54
- root /usr/share/nginx/html;
55
- index index.html;
56
-
57
- location / {
58
- try_files $uri $uri/ /index.html;
59
- }
60
-
61
- location /api {
62
- proxy_pass http://backend:3000;
63
- proxy_http_version 1.1;
64
- proxy_set_header Upgrade $http_upgrade;
65
- proxy_set_header Connection 'upgrade';
66
- proxy_set_header Host $host;
67
- proxy_cache_bypass $http_upgrade;
68
- }
69
- }
70
- `;
71
-
72
- await fs.writeFile(path.join(dockerDir, 'nginx.conf'), nginxConf);
73
-
74
- // Docker Compose
75
- const dockerCompose = getDockerCompose(config);
76
- await fs.writeFile(path.join(targetDir, 'docker-compose.yml'), dockerCompose);
77
-
78
- // .dockerignore
79
- const dockerignore = `node_modules
80
- npm-debug.log
81
- .env
82
- .env.local
83
- .git
84
- .gitignore
85
- README.md
86
- .vscode
87
- .idea
88
- dist
89
- build
90
- coverage
91
- `;
92
-
93
- await fs.writeFile(path.join(targetDir, '.dockerignore'), dockerignore);
94
- }
95
-
96
- function getDockerCompose(config: StackConfig): string {
97
- let services = `version: '3.8'
98
-
99
- services:
100
- frontend:
101
- build:
102
- context: ./frontend
103
- dockerfile: ../docker/frontend.Dockerfile
104
- ports:
105
- - "80:80"
106
- depends_on:
107
- - backend
108
- environment:
109
- - VITE_API_URL=http://localhost:3000/api
110
-
111
- backend:
112
- build:
113
- context: ./backend
114
- dockerfile: ../docker/backend.Dockerfile
115
- ports:
116
- - "3000:3000"
117
- environment:
118
- - NODE_ENV=production
119
- - PORT=3000
120
- - DATABASE_URL=${config.database === 'postgresql' ? `postgresql://postgres:postgres@postgres:5432/${config.projectName}` : config.database === 'mongodb' ? `mongodb://mongo:mongo@mongodb:27017/${config.projectName}` : config.database === 'mysql' ? `mysql://root:mysql@mysql:3306/${config.projectName}` : '${DATABASE_URL}'}
121
- - JWT_SECRET=\${JWT_SECRET}
122
- depends_on:
123
- `;
124
-
125
- // Add database service
126
- if (config.database === 'postgresql') {
127
- services += ` - postgres
128
-
129
- postgres:
130
- image: postgres:16-alpine
131
- ports:
132
- - "5432:5432"
133
- environment:
134
- - POSTGRES_USER=postgres
135
- - POSTGRES_PASSWORD=postgres
136
- - POSTGRES_DB=${config.projectName}
137
- volumes:
138
- - postgres_data:/var/lib/postgresql/data
139
-
140
- volumes:
141
- postgres_data:
142
- `;
143
- } else if (config.database === 'mongodb') {
144
- services += ` - mongodb
145
-
146
- mongodb:
147
- image: mongo:7
148
- ports:
149
- - "27017:27017"
150
- environment:
151
- - MONGO_INITDB_ROOT_USERNAME=mongo
152
- - MONGO_INITDB_ROOT_PASSWORD=mongo
153
- - MONGO_INITDB_DATABASE=${config.projectName}
154
- volumes:
155
- - mongodb_data:/data/db
156
-
157
- volumes:
158
- mongodb_data:
159
- `;
160
- } else if (config.database === 'mysql') {
161
- services += ` - mysql
162
-
163
- mysql:
164
- image: mysql:8
165
- ports:
166
- - "3306:3306"
167
- environment:
168
- - MYSQL_ROOT_PASSWORD=mysql
169
- - MYSQL_DATABASE=${config.projectName}
170
- volumes:
171
- - mysql_data:/var/lib/mysql
172
-
173
- volumes:
174
- mysql_data:
175
- `;
176
- } else {
177
- services += ` # Add your database service here
178
-
179
- volumes:
180
- data:
181
- `;
182
- }
183
-
184
- return services;
185
- }