@insureco/cli 0.1.1 → 0.1.4

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 (84) hide show
  1. package/dist/commands/git.d.ts +30 -0
  2. package/dist/commands/git.d.ts.map +1 -0
  3. package/dist/commands/git.js +300 -0
  4. package/dist/commands/git.js.map +1 -0
  5. package/dist/commands/oauth.d.ts +33 -0
  6. package/dist/commands/oauth.d.ts.map +1 -0
  7. package/dist/commands/oauth.js +181 -0
  8. package/dist/commands/oauth.js.map +1 -0
  9. package/dist/commands/user.d.ts +13 -0
  10. package/dist/commands/user.d.ts.map +1 -0
  11. package/dist/commands/user.js +96 -0
  12. package/dist/commands/user.js.map +1 -0
  13. package/dist/index.js +142 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/bio.d.ts +13 -0
  16. package/dist/lib/bio.d.ts.map +1 -0
  17. package/dist/lib/bio.js +53 -0
  18. package/dist/lib/bio.js.map +1 -0
  19. package/dist/lib/builder.d.ts +17 -1
  20. package/dist/lib/builder.d.ts.map +1 -1
  21. package/dist/lib/builder.js +53 -2
  22. package/dist/lib/builder.js.map +1 -1
  23. package/dist/lib/config.js +1 -1
  24. package/dist/lib/config.js.map +1 -1
  25. package/dist/lib/forgejo.d.ts +149 -0
  26. package/dist/lib/forgejo.d.ts.map +1 -0
  27. package/dist/lib/forgejo.js +137 -0
  28. package/dist/lib/forgejo.js.map +1 -0
  29. package/dist/templates/api/.env.example +15 -0
  30. package/dist/templates/api/Dockerfile +45 -0
  31. package/dist/templates/api/README.md +85 -0
  32. package/dist/templates/api/catalog-info.yaml +51 -0
  33. package/dist/templates/api/helm/{{name}}/Chart.yaml +9 -0
  34. package/dist/templates/api/helm/{{name}}/templates/deployment.yaml +68 -0
  35. package/dist/templates/api/helm/{{name}}/templates/service.yaml +17 -0
  36. package/dist/templates/api/helm/{{name}}/values.yaml +55 -0
  37. package/dist/templates/api/package.json +33 -0
  38. package/dist/templates/api/src/index.ts +61 -0
  39. package/dist/templates/api/src/routes/example.ts +162 -0
  40. package/dist/templates/api/tsconfig.json +18 -0
  41. package/dist/templates/crosspod/README.md +87 -0
  42. package/dist/templates/crosspod/service-a/Dockerfile +27 -0
  43. package/dist/templates/crosspod/service-a/catalog-info.yaml +54 -0
  44. package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/Chart.yaml +6 -0
  45. package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/values.yaml +35 -0
  46. package/dist/templates/crosspod/service-a/package.json +29 -0
  47. package/dist/templates/crosspod/service-a/src/index.ts +89 -0
  48. package/dist/templates/crosspod/service-a/tsconfig.json +14 -0
  49. package/dist/templates/crosspod/service-b/Dockerfile +27 -0
  50. package/dist/templates/crosspod/service-b/catalog-info.yaml +27 -0
  51. package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/Chart.yaml +6 -0
  52. package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/values.yaml +38 -0
  53. package/dist/templates/crosspod/service-b/package.json +26 -0
  54. package/dist/templates/crosspod/service-b/src/index.ts +143 -0
  55. package/dist/templates/crosspod/service-b/tsconfig.json +14 -0
  56. package/dist/templates/nextjs/.env.example +11 -0
  57. package/dist/templates/nextjs/Dockerfile +51 -0
  58. package/dist/templates/nextjs/README.md +87 -0
  59. package/dist/templates/nextjs/catalog-info.yaml +16 -0
  60. package/dist/templates/nextjs/helm/{{name}}/Chart.yaml +9 -0
  61. package/dist/templates/nextjs/helm/{{name}}/templates/deployment.yaml +68 -0
  62. package/dist/templates/nextjs/helm/{{name}}/templates/service.yaml +17 -0
  63. package/dist/templates/nextjs/helm/{{name}}/values.yaml +51 -0
  64. package/dist/templates/nextjs/next.config.js +23 -0
  65. package/dist/templates/nextjs/package.json +29 -0
  66. package/dist/templates/nextjs/public/.gitkeep +0 -0
  67. package/dist/templates/nextjs/src/app/api/example/route.ts +63 -0
  68. package/dist/templates/nextjs/src/app/api/health/route.ts +10 -0
  69. package/dist/templates/nextjs/src/app/layout.tsx +18 -0
  70. package/dist/templates/nextjs/src/app/page.tsx +49 -0
  71. package/dist/templates/nextjs/tsconfig.json +26 -0
  72. package/dist/templates/worker/.env.example +13 -0
  73. package/dist/templates/worker/Dockerfile +26 -0
  74. package/dist/templates/worker/README.md +106 -0
  75. package/dist/templates/worker/catalog-info.yaml +19 -0
  76. package/dist/templates/worker/helm/{{name}}/Chart.yaml +9 -0
  77. package/dist/templates/worker/helm/{{name}}/templates/deployment.yaml +64 -0
  78. package/dist/templates/worker/helm/{{name}}/values.yaml +55 -0
  79. package/dist/templates/worker/package.json +26 -0
  80. package/dist/templates/worker/src/index.ts +185 -0
  81. package/dist/templates/worker/tsconfig.json +14 -0
  82. package/dist/types/index.d.ts +77 -0
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/package.json +3 -2
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{name}}-service-b",
3
+ "version": "0.1.0",
4
+ "description": "Consumer service - calls service-a internally",
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "dev": "tsx watch src/index.ts",
9
+ "start": "node dist/index.js",
10
+ "lint": "eslint src --ext .ts",
11
+ "typecheck": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "express": "^4.21.0",
15
+ "helmet": "^8.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/express": "^5.0.0",
19
+ "@types/node": "^22.0.0",
20
+ "tsx": "^4.0.0",
21
+ "typescript": "^5.7.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=20.0.0"
25
+ }
26
+ }
@@ -0,0 +1,143 @@
1
+ import express, { type Request, type Response, type NextFunction } from 'express'
2
+ import helmet from 'helmet'
3
+
4
+ const app = express()
5
+ const PORT = process.env.PORT || 3000
6
+ const SERVICE_NAME = '{{name}}-service-b'
7
+
8
+ // Service A URL - uses Kubernetes internal DNS
9
+ // Format: http://<service-name>.<namespace>.svc.cluster.local:<port>
10
+ // Simplified: http://<service-name>:<port> when in same namespace
11
+ const SERVICE_A_URL = process.env.SERVICE_A_URL || 'http://{{name}}-service-a:3000'
12
+
13
+ // Middleware
14
+ app.use(helmet())
15
+ app.use(express.json())
16
+
17
+ // Request logging
18
+ app.use((req: Request, _res: Response, next: NextFunction) => {
19
+ const timestamp = new Date().toISOString()
20
+ process.stdout.write(`[${timestamp}] ${req.method} ${req.path}\n`)
21
+ next()
22
+ })
23
+
24
+ // Health check
25
+ app.get('/health', (_req: Request, res: Response) => {
26
+ res.json({
27
+ status: 'healthy',
28
+ service: SERVICE_NAME,
29
+ timestamp: new Date().toISOString(),
30
+ })
31
+ })
32
+
33
+ // Helper function to call service-a
34
+ async function callServiceA(path: string): Promise<unknown> {
35
+ const url = `${SERVICE_A_URL}${path}`
36
+ process.stdout.write(`Calling service-a: ${url}\n`)
37
+
38
+ const response = await fetch(url, {
39
+ headers: {
40
+ 'Content-Type': 'application/json',
41
+ // Identify ourselves for logging/tracing
42
+ 'X-Service-Name': SERVICE_NAME,
43
+ // In production, include service credentials for auth: service routes
44
+ // 'Authorization': `Bearer ${process.env.SERVICE_TOKEN}`,
45
+ },
46
+ })
47
+
48
+ if (!response.ok) {
49
+ throw new Error(`Service A returned ${response.status}`)
50
+ }
51
+
52
+ return response.json()
53
+ }
54
+
55
+ // Public API that aggregates data from service-a
56
+ app.get('/api/data', async (_req: Request, res: Response) => {
57
+ try {
58
+ // Call service-a's internal API
59
+ const serviceAResponse = await callServiceA('/api/internal/items') as {
60
+ success: boolean
61
+ data: Array<{ id: string; name: string; value: number; source: string }>
62
+ meta: { source: string; timestamp: string }
63
+ }
64
+
65
+ // Process and transform the data
66
+ const processedData = {
67
+ items: serviceAResponse.data.map(item => ({
68
+ ...item,
69
+ processedBy: SERVICE_NAME,
70
+ displayValue: `$${item.value.toFixed(2)}`,
71
+ })),
72
+ totalValue: serviceAResponse.data.reduce((sum, item) => sum + item.value, 0),
73
+ }
74
+
75
+ res.json({
76
+ success: true,
77
+ data: processedData,
78
+ meta: {
79
+ aggregator: SERVICE_NAME,
80
+ source: serviceAResponse.meta.source,
81
+ timestamp: new Date().toISOString(),
82
+ },
83
+ })
84
+ } catch (error) {
85
+ process.stderr.write(`Error calling service-a: ${error}\n`)
86
+ res.status(502).json({
87
+ success: false,
88
+ error: {
89
+ code: 'UPSTREAM_ERROR',
90
+ message: 'Failed to retrieve data from upstream service',
91
+ },
92
+ })
93
+ }
94
+ })
95
+
96
+ // Get specific item with enrichment
97
+ app.get('/api/data/:id', async (req: Request, res: Response) => {
98
+ try {
99
+ const response = await callServiceA(`/api/internal/items/${req.params.id}`) as {
100
+ success: boolean
101
+ data: { id: string; name: string; value: number; source: string }
102
+ }
103
+
104
+ const enrichedItem = {
105
+ ...response.data,
106
+ processedBy: SERVICE_NAME,
107
+ displayValue: `$${response.data.value.toFixed(2)}`,
108
+ retrievedAt: new Date().toISOString(),
109
+ }
110
+
111
+ res.json({
112
+ success: true,
113
+ data: enrichedItem,
114
+ })
115
+ } catch (error) {
116
+ const err = error as Error
117
+ if (err.message.includes('404')) {
118
+ res.status(404).json({
119
+ success: false,
120
+ error: { code: 'NOT_FOUND', message: 'Item not found' },
121
+ })
122
+ return
123
+ }
124
+ res.status(502).json({
125
+ success: false,
126
+ error: { code: 'UPSTREAM_ERROR', message: 'Failed to retrieve item' },
127
+ })
128
+ }
129
+ })
130
+
131
+ // Error handler
132
+ app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
133
+ process.stderr.write(`Error: ${err.message}\n`)
134
+ res.status(500).json({
135
+ success: false,
136
+ error: { code: 'INTERNAL_ERROR', message: 'Internal error' },
137
+ })
138
+ })
139
+
140
+ app.listen(PORT, () => {
141
+ process.stdout.write(`${SERVICE_NAME} listening on port ${PORT}\n`)
142
+ process.stdout.write(`Configured to call service-a at: ${SERVICE_A_URL}\n`)
143
+ })
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src/**/*"],
13
+ "exclude": ["node_modules", "dist"]
14
+ }
@@ -0,0 +1,11 @@
1
+ # Next.js Configuration
2
+ NEXT_PUBLIC_APP_NAME={{name}}
3
+
4
+ # API Configuration
5
+ # NEXT_PUBLIC_API_URL=https://api.example.com
6
+
7
+ # Analytics (if needed)
8
+ # NEXT_PUBLIC_GA_ID=UA-XXXXXXXXX-X
9
+
10
+ # Feature Flags
11
+ # NEXT_PUBLIC_FEATURE_X_ENABLED=false
@@ -0,0 +1,51 @@
1
+ FROM node:22-alpine AS base
2
+
3
+ # Install dependencies only when needed
4
+ FROM base AS deps
5
+ RUN apk add --no-cache libc6-compat
6
+ WORKDIR /app
7
+
8
+ COPY package*.json ./
9
+ RUN npm ci
10
+
11
+ # Build the application
12
+ FROM base AS builder
13
+ WORKDIR /app
14
+ COPY --from=deps /app/node_modules ./node_modules
15
+ COPY . .
16
+
17
+ ENV NEXT_TELEMETRY_DISABLED 1
18
+
19
+ RUN npm run build
20
+
21
+ # Production image
22
+ FROM base AS runner
23
+ WORKDIR /app
24
+
25
+ ENV NODE_ENV production
26
+ ENV NEXT_TELEMETRY_DISABLED 1
27
+
28
+ RUN addgroup --system --gid 1001 nodejs
29
+ RUN adduser --system --uid 1001 nextjs
30
+
31
+ COPY --from=builder /app/public ./public
32
+
33
+ # Set correct permissions for prerender cache
34
+ RUN mkdir .next
35
+ RUN chown nextjs:nodejs .next
36
+
37
+ # Automatically leverage output traces to reduce image size
38
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
39
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
40
+
41
+ USER nextjs
42
+
43
+ EXPOSE 3000
44
+
45
+ ENV PORT 3000
46
+ ENV HOSTNAME "0.0.0.0"
47
+
48
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
49
+ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
50
+
51
+ CMD ["node", "server.js"]
@@ -0,0 +1,87 @@
1
+ # {{name}}
2
+
3
+ {{description}}
4
+
5
+ ## Getting Started
6
+
7
+ ### Prerequisites
8
+
9
+ - Node.js 20+
10
+ - npm or pnpm
11
+
12
+ ### Installation
13
+
14
+ ```bash
15
+ npm install
16
+ ```
17
+
18
+ ### Development
19
+
20
+ ```bash
21
+ npm run dev
22
+ ```
23
+
24
+ Open [http://localhost:3000](http://localhost:3000) in your browser.
25
+
26
+ ### Production Build
27
+
28
+ ```bash
29
+ npm run build
30
+ npm start
31
+ ```
32
+
33
+ ## Project Structure
34
+
35
+ ```
36
+ {{name}}/
37
+ ├── src/
38
+ │ └── app/
39
+ │ ├── layout.tsx # Root layout
40
+ │ ├── page.tsx # Home page
41
+ │ └── api/
42
+ │ ├── health/ # Health check endpoint
43
+ │ │ └── route.ts
44
+ │ └── example/ # Example API route
45
+ │ └── route.ts
46
+ ├── public/ # Static assets
47
+ ├── helm/
48
+ │ └── {{name}}/ # Kubernetes Helm chart
49
+ ├── next.config.js # Next.js configuration
50
+ ├── Dockerfile
51
+ └── catalog-info.yaml # Backstage service catalog
52
+ ```
53
+
54
+ ## API Routes
55
+
56
+ ### Health Check
57
+
58
+ ```
59
+ GET /api/health
60
+ ```
61
+
62
+ ### Example API
63
+
64
+ ```
65
+ GET /api/example # List items
66
+ POST /api/example # Create item (body: { name: string })
67
+ ```
68
+
69
+ ## Deployment
70
+
71
+ ### Using iec-cli
72
+
73
+ ```bash
74
+ # Link repository for auto-deploy
75
+ iec link
76
+
77
+ # Manual deploy to sandbox
78
+ iec deploy
79
+
80
+ # Deploy to production
81
+ iec deploy --prod
82
+ ```
83
+
84
+ ## Learn More
85
+
86
+ - [Next.js Documentation](https://nextjs.org/docs)
87
+ - [Tawa Platform Docs](https://docs.tawa.insureco.io)
@@ -0,0 +1,16 @@
1
+ apiVersion: backstage.io/v1alpha1
2
+ kind: Component
3
+ metadata:
4
+ name: {{name}}
5
+ description: {{description}}
6
+ tags:
7
+ - tawa
8
+ - frontend
9
+ - nextjs
10
+ annotations:
11
+ insureco.io/framework: nextjs
12
+ insureco.io/language: typescript
13
+ spec:
14
+ type: service
15
+ lifecycle: experimental
16
+ owner: team-platform
@@ -0,0 +1,9 @@
1
+ apiVersion: v2
2
+ name: {{name}}
3
+ description: Helm chart for {{name}} (Next.js)
4
+ type: application
5
+ version: 0.1.0
6
+ appVersion: "0.1.0"
7
+ maintainers:
8
+ - name: team-platform
9
+ email: platform@insureco.io
@@ -0,0 +1,68 @@
1
+ apiVersion: apps/v1
2
+ kind: Deployment
3
+ metadata:
4
+ name: {{ .Release.Name }}
5
+ labels:
6
+ app: {{ .Release.Name }}
7
+ app.kubernetes.io/name: {{ .Release.Name }}
8
+ app.kubernetes.io/instance: {{ .Release.Name }}
9
+ spec:
10
+ replicas: {{ .Values.replicaCount }}
11
+ selector:
12
+ matchLabels:
13
+ app: {{ .Release.Name }}
14
+ template:
15
+ metadata:
16
+ labels:
17
+ app: {{ .Release.Name }}
18
+ app.kubernetes.io/name: {{ .Release.Name }}
19
+ app.kubernetes.io/instance: {{ .Release.Name }}
20
+ spec:
21
+ {{- with .Values.imagePullSecrets }}
22
+ imagePullSecrets:
23
+ {{- toYaml . | nindent 8 }}
24
+ {{- end }}
25
+ containers:
26
+ - name: {{ .Chart.Name }}
27
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
28
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
29
+ ports:
30
+ - name: http
31
+ containerPort: {{ .Values.service.port }}
32
+ protocol: TCP
33
+ envFrom:
34
+ - configMapRef:
35
+ name: {{ .Release.Name }}-config
36
+ optional: true
37
+ - secretRef:
38
+ name: {{ .Release.Name }}-secrets
39
+ optional: true
40
+ {{- with .Values.env }}
41
+ env:
42
+ {{- range $key, $value := . }}
43
+ - name: {{ $key }}
44
+ value: {{ $value | quote }}
45
+ {{- end }}
46
+ {{- end }}
47
+ {{- with .Values.livenessProbe }}
48
+ livenessProbe:
49
+ {{- toYaml . | nindent 12 }}
50
+ {{- end }}
51
+ {{- with .Values.readinessProbe }}
52
+ readinessProbe:
53
+ {{- toYaml . | nindent 12 }}
54
+ {{- end }}
55
+ resources:
56
+ {{- toYaml .Values.resources | nindent 12 }}
57
+ {{- with .Values.nodeSelector }}
58
+ nodeSelector:
59
+ {{- toYaml . | nindent 8 }}
60
+ {{- end }}
61
+ {{- with .Values.affinity }}
62
+ affinity:
63
+ {{- toYaml . | nindent 8 }}
64
+ {{- end }}
65
+ {{- with .Values.tolerations }}
66
+ tolerations:
67
+ {{- toYaml . | nindent 8 }}
68
+ {{- end }}
@@ -0,0 +1,17 @@
1
+ apiVersion: v1
2
+ kind: Service
3
+ metadata:
4
+ name: {{ .Release.Name }}
5
+ labels:
6
+ app: {{ .Release.Name }}
7
+ app.kubernetes.io/name: {{ .Release.Name }}
8
+ app.kubernetes.io/instance: {{ .Release.Name }}
9
+ spec:
10
+ type: {{ .Values.service.type }}
11
+ ports:
12
+ - port: {{ .Values.service.port }}
13
+ targetPort: http
14
+ protocol: TCP
15
+ name: http
16
+ selector:
17
+ app: {{ .Release.Name }}
@@ -0,0 +1,51 @@
1
+ replicaCount: 1
2
+
3
+ image:
4
+ repository: registry.digitalocean.com/insureco/{{name}}
5
+ tag: latest
6
+ pullPolicy: IfNotPresent
7
+
8
+ imagePullSecrets:
9
+ - name: do-registry
10
+
11
+ service:
12
+ type: ClusterIP
13
+ port: 3000
14
+
15
+ resources:
16
+ limits:
17
+ cpu: 500m
18
+ memory: 512Mi
19
+ requests:
20
+ cpu: 100m
21
+ memory: 128Mi
22
+
23
+ autoscaling:
24
+ enabled: false
25
+ minReplicas: 1
26
+ maxReplicas: 5
27
+ targetCPUUtilizationPercentage: 80
28
+
29
+ nodeSelector: {}
30
+
31
+ tolerations: []
32
+
33
+ affinity: {}
34
+
35
+ env: {}
36
+
37
+ secrets: {}
38
+
39
+ livenessProbe:
40
+ httpGet:
41
+ path: /api/health
42
+ port: 3000
43
+ initialDelaySeconds: 15
44
+ periodSeconds: 10
45
+
46
+ readinessProbe:
47
+ httpGet:
48
+ path: /api/health
49
+ port: 3000
50
+ initialDelaySeconds: 5
51
+ periodSeconds: 5
@@ -0,0 +1,23 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ output: 'standalone',
4
+ reactStrictMode: true,
5
+ poweredByHeader: false,
6
+
7
+ // Enable experimental features as needed
8
+ // experimental: {
9
+ // serverActions: true,
10
+ // },
11
+
12
+ // Configure allowed image domains
13
+ // images: {
14
+ // remotePatterns: [
15
+ // {
16
+ // protocol: 'https',
17
+ // hostname: '*.insureco.io',
18
+ // },
19
+ // ],
20
+ // },
21
+ }
22
+
23
+ module.exports = nextConfig
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.1.0",
4
+ "description": "{{description}}",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "next lint",
11
+ "typecheck": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "next": "^14.2.0",
15
+ "react": "^18.3.0",
16
+ "react-dom": "^18.3.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^22.0.0",
20
+ "@types/react": "^18.3.0",
21
+ "@types/react-dom": "^18.3.0",
22
+ "eslint": "^9.0.0",
23
+ "eslint-config-next": "^14.2.0",
24
+ "typescript": "^5.7.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=20.0.0"
28
+ }
29
+ }
File without changes
@@ -0,0 +1,63 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+
3
+ // In-memory storage for demo
4
+ const items: Map<string, { id: string; name: string; createdAt: string }> = new Map()
5
+
6
+ export async function GET() {
7
+ const itemList = Array.from(items.values())
8
+ return NextResponse.json({
9
+ success: true,
10
+ data: itemList,
11
+ meta: {
12
+ total: itemList.length,
13
+ timestamp: new Date().toISOString(),
14
+ },
15
+ })
16
+ }
17
+
18
+ export async function POST(request: NextRequest) {
19
+ try {
20
+ const body = await request.json()
21
+
22
+ if (!body.name || typeof body.name !== 'string') {
23
+ return NextResponse.json(
24
+ {
25
+ success: false,
26
+ error: {
27
+ code: 'VALIDATION_ERROR',
28
+ message: 'Name is required',
29
+ },
30
+ },
31
+ { status: 400 }
32
+ )
33
+ }
34
+
35
+ const id = Date.now().toString(36) + Math.random().toString(36).substring(2)
36
+ const item = {
37
+ id,
38
+ name: body.name,
39
+ createdAt: new Date().toISOString(),
40
+ }
41
+
42
+ items.set(id, item)
43
+
44
+ return NextResponse.json(
45
+ {
46
+ success: true,
47
+ data: item,
48
+ },
49
+ { status: 201 }
50
+ )
51
+ } catch {
52
+ return NextResponse.json(
53
+ {
54
+ success: false,
55
+ error: {
56
+ code: 'PARSE_ERROR',
57
+ message: 'Invalid JSON body',
58
+ },
59
+ },
60
+ { status: 400 }
61
+ )
62
+ }
63
+ }
@@ -0,0 +1,10 @@
1
+ import { NextResponse } from 'next/server'
2
+
3
+ export async function GET() {
4
+ return NextResponse.json({
5
+ status: 'healthy',
6
+ service: '{{name}}',
7
+ timestamp: new Date().toISOString(),
8
+ environment: process.env.NODE_ENV,
9
+ })
10
+ }
@@ -0,0 +1,18 @@
1
+ import type { Metadata } from 'next'
2
+
3
+ export const metadata: Metadata = {
4
+ title: '{{name}}',
5
+ description: '{{description}}',
6
+ }
7
+
8
+ export default function RootLayout({
9
+ children,
10
+ }: {
11
+ children: React.ReactNode
12
+ }) {
13
+ return (
14
+ <html lang="en">
15
+ <body>{children}</body>
16
+ </html>
17
+ )
18
+ }