archforge-x 1.0.2 → 1.0.3
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/dist/cli/commands/sync.js +22 -0
- package/dist/cli/interactive.js +41 -3
- package/dist/core/architecture/schema.js +23 -2
- package/dist/core/architecture/validator.js +112 -0
- package/dist/generators/base.js +166 -0
- package/dist/generators/generator.js +35 -332
- package/dist/generators/go/gin.js +327 -0
- package/dist/generators/node/express.js +920 -0
- package/dist/generators/node/nestjs.js +770 -0
- package/dist/generators/node/nextjs.js +252 -0
- package/dist/generators/python/django.js +327 -0
- package/dist/generators/python/fastapi.js +309 -0
- package/dist/generators/registry.js +25 -0
- package/dist/index.js +10 -1
- package/package.json +3 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NextJSGenerator = void 0;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
class NextJSGenerator extends base_1.BaseGenerator {
|
|
6
|
+
getGeneratorName() {
|
|
7
|
+
return "Next.js";
|
|
8
|
+
}
|
|
9
|
+
async generateProjectStructure(root, arch, options) {
|
|
10
|
+
// Next.js is opinionated, so we'll stick to its standard structure but organized well.
|
|
11
|
+
// We can support "clean" by adding a separate folder for domain logic if requested.
|
|
12
|
+
this.generateCommonFiles(root, options);
|
|
13
|
+
this.generateSourceFiles(root, options);
|
|
14
|
+
}
|
|
15
|
+
generateCommonFiles(root, options) {
|
|
16
|
+
const projectName = options.projectName || "nextjs-app";
|
|
17
|
+
this.writeFile(root, "package.json", JSON.stringify({
|
|
18
|
+
name: projectName,
|
|
19
|
+
version: "0.1.0",
|
|
20
|
+
private: true,
|
|
21
|
+
scripts: {
|
|
22
|
+
"dev": "next dev",
|
|
23
|
+
"build": "next build",
|
|
24
|
+
"start": "next start",
|
|
25
|
+
"lint": "next lint"
|
|
26
|
+
},
|
|
27
|
+
dependencies: {
|
|
28
|
+
"react": "^18",
|
|
29
|
+
"react-dom": "^18",
|
|
30
|
+
"next": "14.0.0",
|
|
31
|
+
"pino": "^8.14.0",
|
|
32
|
+
"uuid": "^9.0.0"
|
|
33
|
+
},
|
|
34
|
+
devDependencies: {
|
|
35
|
+
"typescript": "^5",
|
|
36
|
+
"@types/node": "^20",
|
|
37
|
+
"@types/react": "^18",
|
|
38
|
+
"@types/react-dom": "^18",
|
|
39
|
+
"@types/uuid": "^9.0.2",
|
|
40
|
+
"eslint": "^8",
|
|
41
|
+
"eslint-config-next": "14.0.0",
|
|
42
|
+
"eslint-plugin-import": "^2.27.5",
|
|
43
|
+
"pino-pretty": "^10.0.0"
|
|
44
|
+
}
|
|
45
|
+
}, null, 2));
|
|
46
|
+
this.writeFile(root, "tsconfig.json", JSON.stringify({
|
|
47
|
+
compilerOptions: {
|
|
48
|
+
target: "es5",
|
|
49
|
+
lib: ["dom", "dom.iterable", "esnext"],
|
|
50
|
+
allowJs: true,
|
|
51
|
+
skipLibCheck: true,
|
|
52
|
+
strict: true,
|
|
53
|
+
noEmit: true,
|
|
54
|
+
esModuleInterop: true,
|
|
55
|
+
module: "esnext",
|
|
56
|
+
moduleResolution: "bundler",
|
|
57
|
+
resolveJsonModule: true,
|
|
58
|
+
isolatedModules: true,
|
|
59
|
+
jsx: "preserve",
|
|
60
|
+
incremental: true,
|
|
61
|
+
plugins: [{ name: "next" }],
|
|
62
|
+
paths: { "@/*": ["./src/*"] }
|
|
63
|
+
},
|
|
64
|
+
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
65
|
+
exclude: ["node_modules"]
|
|
66
|
+
}, null, 2));
|
|
67
|
+
// .eslintrc.json (Architecture Guardrails)
|
|
68
|
+
this.writeFile(root, ".eslintrc.json", JSON.stringify({
|
|
69
|
+
"extends": "next/core-web-vitals",
|
|
70
|
+
"plugins": ["import"],
|
|
71
|
+
"rules": {
|
|
72
|
+
"import/no-restricted-paths": ["error", {
|
|
73
|
+
"zones": [
|
|
74
|
+
{
|
|
75
|
+
"target": "./src/components",
|
|
76
|
+
"from": "./src/app",
|
|
77
|
+
"message": "Components cannot import from App (Pages)"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"target": "./src/lib",
|
|
81
|
+
"from": "./src/components",
|
|
82
|
+
"message": "Lib cannot import from Components"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"target": "./src/lib",
|
|
86
|
+
"from": "./src/app",
|
|
87
|
+
"message": "Lib cannot import from App (Pages)"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}]
|
|
91
|
+
}
|
|
92
|
+
}, null, 2));
|
|
93
|
+
this.writeFile(root, "next.config.js", `/** @type {import('next').NextConfig} */
|
|
94
|
+
const nextConfig = {}
|
|
95
|
+
module.exports = nextConfig
|
|
96
|
+
`);
|
|
97
|
+
this.writeFile(root, ".gitignore", `node_modules/\n.next/\n.env\n`);
|
|
98
|
+
this.writeFile(root, "next-env.d.ts", `/// <reference types="next" />
|
|
99
|
+
/// <reference types="next/image-types/global" />
|
|
100
|
+
|
|
101
|
+
// NOTE: This file should not be edited
|
|
102
|
+
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
103
|
+
`);
|
|
104
|
+
}
|
|
105
|
+
generateSourceFiles(root, options) {
|
|
106
|
+
// App Router structure
|
|
107
|
+
this.writeFile(root, "src/app/layout.tsx", `
|
|
108
|
+
import type { Metadata } from 'next'
|
|
109
|
+
|
|
110
|
+
export const metadata: Metadata = {
|
|
111
|
+
title: 'Create Next App',
|
|
112
|
+
description: 'Generated by create next app',
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export default function RootLayout({
|
|
116
|
+
children,
|
|
117
|
+
}: {
|
|
118
|
+
children: React.ReactNode
|
|
119
|
+
}) {
|
|
120
|
+
return (
|
|
121
|
+
<html lang="en">
|
|
122
|
+
<body>{children}</body>
|
|
123
|
+
</html>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
`);
|
|
127
|
+
this.writeFile(root, "src/app/page.tsx", `
|
|
128
|
+
export default function Home() {
|
|
129
|
+
return (
|
|
130
|
+
<main style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
|
|
131
|
+
<h1>Welcome to Next.js</h1>
|
|
132
|
+
<p>Generated by ArchForge X</p>
|
|
133
|
+
</main>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
`);
|
|
137
|
+
// API Route example
|
|
138
|
+
this.writeFile(root, "src/app/api/hello/route.ts", `
|
|
139
|
+
import { NextResponse } from 'next/server';
|
|
140
|
+
import logger from '@/lib/logger';
|
|
141
|
+
|
|
142
|
+
export async function GET() {
|
|
143
|
+
logger.info('Hello API called');
|
|
144
|
+
return NextResponse.json({ message: 'Hello from ArchForge X!' });
|
|
145
|
+
}
|
|
146
|
+
`);
|
|
147
|
+
// Logger Utility
|
|
148
|
+
this.writeFile(root, "src/lib/logger.ts", `
|
|
149
|
+
import pino from 'pino';
|
|
150
|
+
|
|
151
|
+
const logger = pino({
|
|
152
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
153
|
+
transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined,
|
|
154
|
+
base: {
|
|
155
|
+
env: process.env.NODE_ENV,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
export default logger;
|
|
160
|
+
`);
|
|
161
|
+
// Middleware for Correlation ID
|
|
162
|
+
this.writeFile(root, "src/middleware.ts", `
|
|
163
|
+
import { NextResponse } from 'next/server';
|
|
164
|
+
import type { NextRequest } from 'next/server';
|
|
165
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
166
|
+
|
|
167
|
+
export function middleware(request: NextRequest) {
|
|
168
|
+
const requestHeaders = new Headers(request.headers);
|
|
169
|
+
const correlationId = requestHeaders.get('x-correlation-id') || uuidv4();
|
|
170
|
+
requestHeaders.set('x-correlation-id', correlationId);
|
|
171
|
+
|
|
172
|
+
const response = NextResponse.next({
|
|
173
|
+
request: {
|
|
174
|
+
headers: requestHeaders,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
response.headers.set('x-correlation-id', correlationId);
|
|
179
|
+
return response;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const config = {
|
|
183
|
+
matcher: '/:path*',
|
|
184
|
+
};
|
|
185
|
+
`);
|
|
186
|
+
}
|
|
187
|
+
generateDocker(root, options) {
|
|
188
|
+
this.writeFile(root, "Dockerfile", `
|
|
189
|
+
FROM node:18-alpine AS base
|
|
190
|
+
|
|
191
|
+
FROM base AS deps
|
|
192
|
+
WORKDIR /app
|
|
193
|
+
COPY package.json package-lock.json* ./
|
|
194
|
+
RUN npm ci
|
|
195
|
+
|
|
196
|
+
FROM base AS builder
|
|
197
|
+
WORKDIR /app
|
|
198
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
199
|
+
COPY . .
|
|
200
|
+
RUN npm run build
|
|
201
|
+
|
|
202
|
+
FROM base AS runner
|
|
203
|
+
WORKDIR /app
|
|
204
|
+
ENV NODE_ENV production
|
|
205
|
+
COPY --from=builder /app/public ./public
|
|
206
|
+
COPY --from=builder /app/.next/standalone ./
|
|
207
|
+
COPY --from=builder /app/.next/static ./.next/static
|
|
208
|
+
|
|
209
|
+
EXPOSE 3000
|
|
210
|
+
ENV PORT 3000
|
|
211
|
+
CMD ["node", "server.js"]
|
|
212
|
+
`);
|
|
213
|
+
this.writeFile(root, "docker-compose.yml", `
|
|
214
|
+
version: '3.8'
|
|
215
|
+
services:
|
|
216
|
+
app:
|
|
217
|
+
build: .
|
|
218
|
+
ports:
|
|
219
|
+
- "3000:3000"
|
|
220
|
+
environment:
|
|
221
|
+
- NODE_ENV=production
|
|
222
|
+
restart: always
|
|
223
|
+
`);
|
|
224
|
+
}
|
|
225
|
+
generateCI(root, options) {
|
|
226
|
+
this.writeFile(root, ".github/workflows/ci.yml", `
|
|
227
|
+
name: CI
|
|
228
|
+
|
|
229
|
+
on:
|
|
230
|
+
push:
|
|
231
|
+
branches: [ main ]
|
|
232
|
+
pull_request:
|
|
233
|
+
branches: [ main ]
|
|
234
|
+
|
|
235
|
+
jobs:
|
|
236
|
+
build:
|
|
237
|
+
runs-on: ubuntu-latest
|
|
238
|
+
|
|
239
|
+
steps:
|
|
240
|
+
- uses: actions/checkout@v3
|
|
241
|
+
- name: Use Node.js 18.x
|
|
242
|
+
uses: actions/setup-node@v3
|
|
243
|
+
with:
|
|
244
|
+
node-version: 18.x
|
|
245
|
+
cache: 'npm'
|
|
246
|
+
- run: npm ci
|
|
247
|
+
- run: npm run lint
|
|
248
|
+
- run: npm run build
|
|
249
|
+
`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.NextJSGenerator = NextJSGenerator;
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DjangoGenerator = void 0;
|
|
4
|
+
const base_1 = require("../base");
|
|
5
|
+
class DjangoGenerator extends base_1.BaseGenerator {
|
|
6
|
+
getGeneratorName() {
|
|
7
|
+
return "Django";
|
|
8
|
+
}
|
|
9
|
+
async generateProjectStructure(root, arch, options) {
|
|
10
|
+
// Django structure is complex to generate file-by-file without the CLI,
|
|
11
|
+
// but we will generate a standard working structure.
|
|
12
|
+
const projectName = options.projectName || "django_project";
|
|
13
|
+
const appName = "core"; // Default app name
|
|
14
|
+
this.generateRequirements(root, options);
|
|
15
|
+
this.generateManagePy(root, projectName, options);
|
|
16
|
+
this.generateProjectConfig(root, projectName, options);
|
|
17
|
+
this.generateApp(root, appName);
|
|
18
|
+
}
|
|
19
|
+
generateRequirements(root, options) {
|
|
20
|
+
this.writeFile(root, "requirements.txt", `
|
|
21
|
+
Django>=4.2.0
|
|
22
|
+
djangorestframework>=3.14.0
|
|
23
|
+
python-dotenv>=1.0.0
|
|
24
|
+
import-linter>=1.10.0
|
|
25
|
+
structlog>=23.1.0
|
|
26
|
+
django-structlog>=5.1.0
|
|
27
|
+
dj-database-url>=2.1.0
|
|
28
|
+
${options.database === 'postgresql' ? 'psycopg2-binary>=2.9.0' : ''}
|
|
29
|
+
${options.database === 'mysql' ? 'mysqlclient>=2.2.0' : ''}
|
|
30
|
+
`.trim());
|
|
31
|
+
// .import-linter (Architecture Guardrails)
|
|
32
|
+
this.writeFile(root, ".import-linter", `
|
|
33
|
+
[importlinter]
|
|
34
|
+
root_package = core
|
|
35
|
+
|
|
36
|
+
[importlinter:contract:1]
|
|
37
|
+
name = Django App Boundaries
|
|
38
|
+
type = layers
|
|
39
|
+
layers =
|
|
40
|
+
views
|
|
41
|
+
models
|
|
42
|
+
containers = core
|
|
43
|
+
`.trim());
|
|
44
|
+
}
|
|
45
|
+
generateManagePy(root, projectName, options) {
|
|
46
|
+
this.writeFile(root, "manage.py", `
|
|
47
|
+
#!/usr/bin/env python
|
|
48
|
+
"""Django's command-line utility for administrative tasks."""
|
|
49
|
+
import os
|
|
50
|
+
import sys
|
|
51
|
+
|
|
52
|
+
def main():
|
|
53
|
+
"""Run administrative tasks."""
|
|
54
|
+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '${projectName}.settings')
|
|
55
|
+
try:
|
|
56
|
+
from django.core.management import execute_from_command_line
|
|
57
|
+
except ImportError as exc:
|
|
58
|
+
raise ImportError(
|
|
59
|
+
"Couldn't import Django. Are you sure it's installed and "
|
|
60
|
+
"available on your PYTHONPATH environment variable? Did you "
|
|
61
|
+
"forget to activate a virtual environment?"
|
|
62
|
+
) from exc
|
|
63
|
+
execute_from_command_line(sys.argv)
|
|
64
|
+
|
|
65
|
+
if __name__ == '__main__':
|
|
66
|
+
main()
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
generateProjectConfig(root, projectName, options) {
|
|
70
|
+
const configDir = `${projectName}`; // Django project folder usually has same name as project
|
|
71
|
+
this.createDir(root, configDir);
|
|
72
|
+
this.writeFile(root, `${configDir}/__init__.py`, "");
|
|
73
|
+
this.writeFile(root, `${configDir}/settings.py`, `
|
|
74
|
+
from pathlib import Path
|
|
75
|
+
|
|
76
|
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
77
|
+
SECRET_KEY = 'django-insecure-generated-key'
|
|
78
|
+
DEBUG = True
|
|
79
|
+
ALLOWED_HOSTS = []
|
|
80
|
+
|
|
81
|
+
INSTALLED_APPS = [
|
|
82
|
+
'django.contrib.admin',
|
|
83
|
+
'django.contrib.auth',
|
|
84
|
+
'django.contrib.contenttypes',
|
|
85
|
+
'django.contrib.sessions',
|
|
86
|
+
'django.contrib.messages',
|
|
87
|
+
'django.contrib.staticfiles',
|
|
88
|
+
'rest_framework',
|
|
89
|
+
'core',
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
MIDDLEWARE = [
|
|
93
|
+
'django.middleware.security.SecurityMiddleware',
|
|
94
|
+
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
95
|
+
'django.middleware.common.CommonMiddleware',
|
|
96
|
+
'django.middleware.csrf.CsrfViewMiddleware',
|
|
97
|
+
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
98
|
+
'django.contrib.messages.middleware.MessageMiddleware',
|
|
99
|
+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
ROOT_URLCONF = '${projectName}.urls'
|
|
103
|
+
|
|
104
|
+
TEMPLATES = [
|
|
105
|
+
{
|
|
106
|
+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
107
|
+
'DIRS': [],
|
|
108
|
+
'APP_DIRS': True,
|
|
109
|
+
'OPTIONS': {
|
|
110
|
+
'context_processors': [
|
|
111
|
+
'django.template.context_processors.debug',
|
|
112
|
+
'django.template.context_processors.request',
|
|
113
|
+
'django.contrib.auth.context_processors.auth',
|
|
114
|
+
'django.contrib.messages.context_processors.messages',
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
WSGI_APPLICATION = '${projectName}.wsgi.application'
|
|
121
|
+
|
|
122
|
+
import os
|
|
123
|
+
import dj_database_url
|
|
124
|
+
|
|
125
|
+
DATABASES = {
|
|
126
|
+
'default': dj_database_url.config(
|
|
127
|
+
default=os.getenv('DATABASE_URL', 'sqlite:///' + str(BASE_DIR / 'db.sqlite3'))
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
AUTH_PASSWORD_VALIDATORS = []
|
|
132
|
+
LANGUAGE_CODE = 'en-us'
|
|
133
|
+
TIME_ZONE = 'UTC'
|
|
134
|
+
USE_I18N = True
|
|
135
|
+
USE_TZ = True
|
|
136
|
+
STATIC_URL = 'static/'
|
|
137
|
+
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
138
|
+
|
|
139
|
+
# Logging Configuration
|
|
140
|
+
import structlog
|
|
141
|
+
|
|
142
|
+
LOGGING = {
|
|
143
|
+
"version": 1,
|
|
144
|
+
"disable_existing_loggers": False,
|
|
145
|
+
"formatters": {
|
|
146
|
+
"json_formatter": {
|
|
147
|
+
"()": structlog.stdlib.ProcessorFormatter,
|
|
148
|
+
"processor": structlog.processors.JSONRenderer(),
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
"handlers": {
|
|
152
|
+
"console": {
|
|
153
|
+
"class": "logging.StreamHandler",
|
|
154
|
+
"formatter": "json_formatter",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
"root": {
|
|
158
|
+
"handlers": ["console"],
|
|
159
|
+
"level": "INFO",
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
structlog.configure(
|
|
164
|
+
processors=[
|
|
165
|
+
structlog.contextvars.merge_contextvars,
|
|
166
|
+
structlog.stdlib.filter_by_level,
|
|
167
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
168
|
+
structlog.stdlib.add_logger_name,
|
|
169
|
+
structlog.stdlib.add_log_level,
|
|
170
|
+
structlog.stdlib.PositionalArgumentsFormatter(),
|
|
171
|
+
structlog.processors.StackInfoRenderer(),
|
|
172
|
+
structlog.processors.format_exc_info,
|
|
173
|
+
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
|
174
|
+
],
|
|
175
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
176
|
+
cache_logger_on_first_use=True,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
MIDDLEWARE.insert(0, 'django_structlog.middlewares.RequestMiddleware')
|
|
180
|
+
`);
|
|
181
|
+
this.writeFile(root, `${configDir}/urls.py`, `
|
|
182
|
+
from django.contrib import admin
|
|
183
|
+
from django.urls import path, include
|
|
184
|
+
|
|
185
|
+
urlpatterns = [
|
|
186
|
+
path('admin/', admin.site.urls),
|
|
187
|
+
path('api/', include('core.urls')),
|
|
188
|
+
]
|
|
189
|
+
`);
|
|
190
|
+
this.writeFile(root, `${configDir}/wsgi.py`, `
|
|
191
|
+
import os
|
|
192
|
+
from django.core.wsgi import get_wsgi_application
|
|
193
|
+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '${projectName}.settings')
|
|
194
|
+
application = get_wsgi_application()
|
|
195
|
+
`);
|
|
196
|
+
this.writeFile(root, `${configDir}/asgi.py`, `
|
|
197
|
+
import os
|
|
198
|
+
from django.core.asgi import get_asgi_application
|
|
199
|
+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '${projectName}.settings')
|
|
200
|
+
application = get_asgi_application()
|
|
201
|
+
`);
|
|
202
|
+
}
|
|
203
|
+
generateApp(root, appName) {
|
|
204
|
+
this.createDir(root, appName);
|
|
205
|
+
this.writeFile(root, `${appName}/__init__.py`, "");
|
|
206
|
+
this.writeFile(root, `${appName}/admin.py`, "from django.contrib import admin\n# Register your models here.");
|
|
207
|
+
this.writeFile(root, `${appName}/apps.py`, `
|
|
208
|
+
from django.apps import AppConfig
|
|
209
|
+
class CoreConfig(AppConfig):
|
|
210
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
|
211
|
+
name = '${appName}'
|
|
212
|
+
`);
|
|
213
|
+
this.writeFile(root, `${appName}/models.py`, "from django.db import models\n# Create your models here.");
|
|
214
|
+
this.writeFile(root, `${appName}/tests.py`, "from django.test import TestCase\n# Create your tests here.");
|
|
215
|
+
this.writeFile(root, `${appName}/views.py`, `
|
|
216
|
+
from rest_framework.decorators import api_view
|
|
217
|
+
from rest_framework.response import Response
|
|
218
|
+
|
|
219
|
+
@api_view(['GET'])
|
|
220
|
+
def hello_world(request):
|
|
221
|
+
return Response({"message": "Hello from Django generated by ArchForge X"})
|
|
222
|
+
`);
|
|
223
|
+
this.writeFile(root, `${appName}/urls.py`, `
|
|
224
|
+
from django.urls import path
|
|
225
|
+
from . import views
|
|
226
|
+
|
|
227
|
+
urlpatterns = [
|
|
228
|
+
path('hello/', views.hello_world),
|
|
229
|
+
]
|
|
230
|
+
`);
|
|
231
|
+
}
|
|
232
|
+
generateDocker(root, options) {
|
|
233
|
+
const dbService = options.database === "postgresql" ? `
|
|
234
|
+
db:
|
|
235
|
+
image: postgres:15-alpine
|
|
236
|
+
environment:
|
|
237
|
+
- POSTGRES_USER=user
|
|
238
|
+
- POSTGRES_PASSWORD=password
|
|
239
|
+
- POSTGRES_DB=appdb
|
|
240
|
+
ports:
|
|
241
|
+
- "5432:5432"
|
|
242
|
+
` : options.database === "mysql" ? `
|
|
243
|
+
db:
|
|
244
|
+
image: mysql:8
|
|
245
|
+
environment:
|
|
246
|
+
- MYSQL_ROOT_PASSWORD=password
|
|
247
|
+
- MYSQL_DATABASE=appdb
|
|
248
|
+
ports:
|
|
249
|
+
- "3306:3306"
|
|
250
|
+
` : "";
|
|
251
|
+
const dbUrl = options.database === "postgresql" ? "postgres://user:password@db:5432/appdb" :
|
|
252
|
+
options.database === "mysql" ? "mysql://root:password@db:3306/appdb" :
|
|
253
|
+
"sqlite:///db.sqlite3";
|
|
254
|
+
this.writeFile(root, "Dockerfile", `
|
|
255
|
+
FROM python:3.11-slim
|
|
256
|
+
|
|
257
|
+
WORKDIR /app
|
|
258
|
+
|
|
259
|
+
COPY requirements.txt .
|
|
260
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
261
|
+
|
|
262
|
+
COPY . .
|
|
263
|
+
|
|
264
|
+
EXPOSE 8000
|
|
265
|
+
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
|
266
|
+
`);
|
|
267
|
+
this.writeFile(root, "docker-compose.yml", `
|
|
268
|
+
version: '3.8'
|
|
269
|
+
services:
|
|
270
|
+
app:
|
|
271
|
+
build: .
|
|
272
|
+
ports:
|
|
273
|
+
- "8000:8000"
|
|
274
|
+
environment:
|
|
275
|
+
- DEBUG=0
|
|
276
|
+
- SECRET_KEY=production-secret
|
|
277
|
+
- DATABASE_URL=${dbUrl}
|
|
278
|
+
depends_on:
|
|
279
|
+
${dbService ? "- db" : ""}
|
|
280
|
+
restart: always
|
|
281
|
+
${dbService}
|
|
282
|
+
`);
|
|
283
|
+
}
|
|
284
|
+
generateCI(root, options) {
|
|
285
|
+
this.writeFile(root, ".github/workflows/ci.yml", `
|
|
286
|
+
name: CI
|
|
287
|
+
|
|
288
|
+
on:
|
|
289
|
+
push:
|
|
290
|
+
branches: [ main ]
|
|
291
|
+
pull_request:
|
|
292
|
+
branches: [ main ]
|
|
293
|
+
|
|
294
|
+
jobs:
|
|
295
|
+
test:
|
|
296
|
+
runs-on: ubuntu-latest
|
|
297
|
+
|
|
298
|
+
steps:
|
|
299
|
+
- uses: actions/checkout@v3
|
|
300
|
+
- name: Set up Python 3.11
|
|
301
|
+
uses: actions/setup-python@v4
|
|
302
|
+
with:
|
|
303
|
+
python-version: "3.11"
|
|
304
|
+
cache: 'pip'
|
|
305
|
+
- name: Install dependencies
|
|
306
|
+
run: |
|
|
307
|
+
python -m pip install --upgrade pip
|
|
308
|
+
pip install -r requirements.txt
|
|
309
|
+
pip install flake8
|
|
310
|
+
- name: Lint with flake8
|
|
311
|
+
run: |
|
|
312
|
+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
313
|
+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
|
314
|
+
- name: Architecture Sync & Validation
|
|
315
|
+
run: |
|
|
316
|
+
# In a real scenario, archforge would be installed
|
|
317
|
+
echo "Running archforge sync..."
|
|
318
|
+
- name: Check Architecture Boundaries
|
|
319
|
+
run: |
|
|
320
|
+
lint-imports
|
|
321
|
+
- name: Run Tests
|
|
322
|
+
run: |
|
|
323
|
+
python manage.py test
|
|
324
|
+
`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
exports.DjangoGenerator = DjangoGenerator;
|