@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,61 @@
1
+ import express, { type Request, type Response, type NextFunction } from 'express'
2
+ import cors from 'cors'
3
+ import helmet from 'helmet'
4
+ import { itemsRouter } from './routes/example.js'
5
+
6
+ const app = express()
7
+ const PORT = process.env.PORT || 3000
8
+
9
+ // Middleware
10
+ app.use(helmet())
11
+ app.use(cors())
12
+ app.use(express.json())
13
+
14
+ // Request logging
15
+ app.use((req: Request, _res: Response, next: NextFunction) => {
16
+ const timestamp = new Date().toISOString()
17
+ process.stdout.write(`[${timestamp}] ${req.method} ${req.path}\n`)
18
+ next()
19
+ })
20
+
21
+ // Health check endpoint
22
+ app.get('/health', (_req: Request, res: Response) => {
23
+ res.json({
24
+ status: 'healthy',
25
+ service: '{{name}}',
26
+ timestamp: new Date().toISOString(),
27
+ uptime: process.uptime(),
28
+ })
29
+ })
30
+
31
+ // API routes
32
+ app.use('/api/items', itemsRouter)
33
+
34
+ // 404 handler
35
+ app.use((_req: Request, res: Response) => {
36
+ res.status(404).json({
37
+ success: false,
38
+ error: {
39
+ code: 'NOT_FOUND',
40
+ message: 'The requested resource was not found',
41
+ },
42
+ })
43
+ })
44
+
45
+ // Error handler
46
+ app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
47
+ process.stderr.write(`Error: ${err.message}\n`)
48
+ res.status(500).json({
49
+ success: false,
50
+ error: {
51
+ code: 'INTERNAL_ERROR',
52
+ message: process.env.NODE_ENV === 'production'
53
+ ? 'An internal error occurred'
54
+ : err.message,
55
+ },
56
+ })
57
+ })
58
+
59
+ app.listen(PORT, () => {
60
+ process.stdout.write(`{{name}} listening on port ${PORT}\n`)
61
+ })
@@ -0,0 +1,162 @@
1
+ import { Router, type Request, type Response } from 'express'
2
+ import { z } from 'zod'
3
+
4
+ const router = Router()
5
+
6
+ // In-memory storage for demo purposes
7
+ interface Item {
8
+ id: string
9
+ name: string
10
+ description: string
11
+ createdAt: string
12
+ updatedAt: string
13
+ }
14
+
15
+ const items: Map<string, Item> = new Map()
16
+
17
+ // Validation schemas
18
+ const CreateItemSchema = z.object({
19
+ name: z.string().min(1).max(100),
20
+ description: z.string().max(500).optional().default(''),
21
+ })
22
+
23
+ const UpdateItemSchema = z.object({
24
+ name: z.string().min(1).max(100).optional(),
25
+ description: z.string().max(500).optional(),
26
+ })
27
+
28
+ // Generate unique ID
29
+ function generateId(): string {
30
+ return Date.now().toString(36) + Math.random().toString(36).substring(2)
31
+ }
32
+
33
+ // List all items
34
+ router.get('/', (_req: Request, res: Response) => {
35
+ const itemList = Array.from(items.values())
36
+ res.json({
37
+ success: true,
38
+ data: itemList,
39
+ meta: {
40
+ total: itemList.length,
41
+ timestamp: new Date().toISOString(),
42
+ },
43
+ })
44
+ })
45
+
46
+ // Get single item
47
+ router.get('/:id', (req: Request, res: Response) => {
48
+ const item = items.get(req.params.id)
49
+
50
+ if (!item) {
51
+ res.status(404).json({
52
+ success: false,
53
+ error: {
54
+ code: 'NOT_FOUND',
55
+ message: 'Item not found',
56
+ },
57
+ })
58
+ return
59
+ }
60
+
61
+ res.json({
62
+ success: true,
63
+ data: item,
64
+ })
65
+ })
66
+
67
+ // Create item
68
+ router.post('/', (req: Request, res: Response) => {
69
+ const result = CreateItemSchema.safeParse(req.body)
70
+
71
+ if (!result.success) {
72
+ res.status(400).json({
73
+ success: false,
74
+ error: {
75
+ code: 'VALIDATION_ERROR',
76
+ message: 'Invalid request body',
77
+ details: result.error.flatten(),
78
+ },
79
+ })
80
+ return
81
+ }
82
+
83
+ const now = new Date().toISOString()
84
+ const item: Item = {
85
+ id: generateId(),
86
+ name: result.data.name,
87
+ description: result.data.description,
88
+ createdAt: now,
89
+ updatedAt: now,
90
+ }
91
+
92
+ items.set(item.id, item)
93
+
94
+ res.status(201).json({
95
+ success: true,
96
+ data: item,
97
+ })
98
+ })
99
+
100
+ // Update item
101
+ router.put('/:id', (req: Request, res: Response) => {
102
+ const existing = items.get(req.params.id)
103
+
104
+ if (!existing) {
105
+ res.status(404).json({
106
+ success: false,
107
+ error: {
108
+ code: 'NOT_FOUND',
109
+ message: 'Item not found',
110
+ },
111
+ })
112
+ return
113
+ }
114
+
115
+ const result = UpdateItemSchema.safeParse(req.body)
116
+
117
+ if (!result.success) {
118
+ res.status(400).json({
119
+ success: false,
120
+ error: {
121
+ code: 'VALIDATION_ERROR',
122
+ message: 'Invalid request body',
123
+ details: result.error.flatten(),
124
+ },
125
+ })
126
+ return
127
+ }
128
+
129
+ const updated: Item = {
130
+ ...existing,
131
+ name: result.data.name ?? existing.name,
132
+ description: result.data.description ?? existing.description,
133
+ updatedAt: new Date().toISOString(),
134
+ }
135
+
136
+ items.set(updated.id, updated)
137
+
138
+ res.json({
139
+ success: true,
140
+ data: updated,
141
+ })
142
+ })
143
+
144
+ // Delete item
145
+ router.delete('/:id', (req: Request, res: Response) => {
146
+ const existed = items.delete(req.params.id)
147
+
148
+ if (!existed) {
149
+ res.status(404).json({
150
+ success: false,
151
+ error: {
152
+ code: 'NOT_FOUND',
153
+ message: 'Item not found',
154
+ },
155
+ })
156
+ return
157
+ }
158
+
159
+ res.status(204).send()
160
+ })
161
+
162
+ export { router as itemsRouter }
@@ -0,0 +1,18 @@
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
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }
@@ -0,0 +1,87 @@
1
+ # {{name}} - Pod-to-Pod Communication Demo
2
+
3
+ This template demonstrates service-to-service (pod-to-pod) communication within the Tawa platform.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ ┌─────────────────┐
9
+ │ Client │
10
+ └────────┬────────┘
11
+
12
+ ┌────────▼────────┐
13
+ │ Janus API │
14
+ │ Gateway │
15
+ └────────┬────────┘
16
+
17
+ ┌───────────────────┼───────────────────┐
18
+ │ │ │
19
+ ┌────────▼────────┐ ┌────────▼────────┐ │
20
+ │ service-a │ │ service-b │ │
21
+ │ (Provider) │ │ (Consumer) │ │
22
+ │ │ │ │ │
23
+ │ Exposes data │ │ Calls service-a │ │
24
+ │ via REST API │ │ internally │ │
25
+ └─────────────────┘ └────────┬────────┘ │
26
+ │ │
27
+ ┌────────▼────────┐ │
28
+ │ Internal K8s │◄────────┘
29
+ │ Service DNS │
30
+ └─────────────────┘
31
+ ```
32
+
33
+ ## Services
34
+
35
+ ### service-a (Provider)
36
+
37
+ - Exposes data via REST API
38
+ - Routes configured with `auth: service` for internal-only access
39
+ - Accessible at `http://{{name}}-service-a:3000` within the cluster
40
+
41
+ ### service-b (Consumer)
42
+
43
+ - Calls service-a to retrieve and process data
44
+ - Uses internal Kubernetes DNS for service discovery
45
+ - Demonstrates service-to-service authentication
46
+
47
+ ## Pod-to-Pod Authentication
48
+
49
+ When `auth: service` is set on a route in `catalog-info.yaml`:
50
+
51
+ 1. Requests must include service credentials
52
+ 2. Janus validates the calling service identity
53
+ 3. Only registered services can access protected endpoints
54
+
55
+ ### Example catalog-info.yaml configuration:
56
+
57
+ ```yaml
58
+ spec:
59
+ routes:
60
+ - path: /api/internal/*
61
+ methods: [GET, POST]
62
+ auth: service # Only other services can call this
63
+ scopes:
64
+ - read:data
65
+ ```
66
+
67
+ ## Deployment
68
+
69
+ Deploy both services:
70
+
71
+ ```bash
72
+ cd {{name}}/service-a
73
+ iec link && iec deploy
74
+
75
+ cd ../service-b
76
+ iec link && iec deploy
77
+ ```
78
+
79
+ ## Testing
80
+
81
+ 1. Deploy both services to sandbox
82
+ 2. Call service-b's `/api/data` endpoint
83
+ 3. service-b will internally call service-a and return combined data
84
+
85
+ ```bash
86
+ curl https://{{name}}-service-b.sandbox.tawa.insureco.io/api/data
87
+ ```
@@ -0,0 +1,27 @@
1
+ FROM node:22-alpine AS builder
2
+ WORKDIR /app
3
+ COPY package*.json ./
4
+ RUN npm ci
5
+ COPY . .
6
+ RUN npm run build
7
+
8
+ FROM node:22-alpine
9
+ WORKDIR /app
10
+ COPY package*.json ./
11
+ RUN npm ci --production && npm cache clean --force
12
+ COPY --from=builder /app/dist ./dist
13
+
14
+ ENV NODE_ENV=production
15
+ ENV PORT=3000
16
+
17
+ RUN addgroup -g 1001 -S nodejs && \
18
+ adduser -S nodejs -u 1001 && \
19
+ chown -R nodejs:nodejs /app
20
+
21
+ USER nodejs
22
+ EXPOSE 3000
23
+
24
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
25
+ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
26
+
27
+ CMD ["node", "dist/index.js"]
@@ -0,0 +1,54 @@
1
+ apiVersion: backstage.io/v1alpha1
2
+ kind: Component
3
+ metadata:
4
+ name: {{name}}-service-a
5
+ description: Provider service for {{name}} - exposes internal data API
6
+ tags:
7
+ - tawa
8
+ - api
9
+ - provider
10
+ annotations:
11
+ insureco.io/framework: express
12
+ insureco.io/language: typescript
13
+ spec:
14
+ type: service
15
+ lifecycle: experimental
16
+ owner: team-platform
17
+ providesApis:
18
+ - {{name}}-service-a-api
19
+ routes:
20
+ - path: /health
21
+ methods: [GET]
22
+ auth: none
23
+ description: Health check endpoint
24
+ - path: /api/internal/*
25
+ methods: [GET, POST]
26
+ auth: service
27
+ scopes:
28
+ - read:data
29
+ - write:data
30
+ description: Internal API - service-to-service only
31
+ ---
32
+ apiVersion: backstage.io/v1alpha1
33
+ kind: API
34
+ metadata:
35
+ name: {{name}}-service-a-api
36
+ description: Internal data API for {{name}}
37
+ spec:
38
+ type: openapi
39
+ lifecycle: experimental
40
+ owner: team-platform
41
+ definition: |
42
+ openapi: 3.0.0
43
+ info:
44
+ title: {{name}} Service A API
45
+ version: 1.0.0
46
+ paths:
47
+ /api/internal/items:
48
+ get:
49
+ summary: Get all items (service-to-service only)
50
+ security:
51
+ - serviceAuth: []
52
+ responses:
53
+ '200':
54
+ description: List of items
@@ -0,0 +1,6 @@
1
+ apiVersion: v2
2
+ name: {{name}}-service-a
3
+ description: Provider service Helm chart
4
+ type: application
5
+ version: 0.1.0
6
+ appVersion: "0.1.0"
@@ -0,0 +1,35 @@
1
+ replicaCount: 1
2
+
3
+ image:
4
+ repository: registry.digitalocean.com/insureco/{{name}}-service-a
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: 200m
18
+ memory: 256Mi
19
+ requests:
20
+ cpu: 50m
21
+ memory: 64Mi
22
+
23
+ livenessProbe:
24
+ httpGet:
25
+ path: /health
26
+ port: 3000
27
+ initialDelaySeconds: 10
28
+ periodSeconds: 10
29
+
30
+ readinessProbe:
31
+ httpGet:
32
+ path: /health
33
+ port: 3000
34
+ initialDelaySeconds: 5
35
+ periodSeconds: 5
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "{{name}}-service-a",
3
+ "version": "0.1.0",
4
+ "description": "Provider service - exposes internal data API",
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
+ "cors": "^2.8.5",
16
+ "helmet": "^8.0.0",
17
+ "zod": "^3.24.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/cors": "^2.8.17",
21
+ "@types/express": "^5.0.0",
22
+ "@types/node": "^22.0.0",
23
+ "tsx": "^4.0.0",
24
+ "typescript": "^5.7.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=20.0.0"
28
+ }
29
+ }
@@ -0,0 +1,89 @@
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-a'
7
+
8
+ // Middleware
9
+ app.use(helmet())
10
+ app.use(express.json())
11
+
12
+ // Sample data store
13
+ interface Item {
14
+ id: string
15
+ name: string
16
+ value: number
17
+ source: string
18
+ }
19
+
20
+ const items: Item[] = [
21
+ { id: '1', name: 'Widget A', value: 100, source: SERVICE_NAME },
22
+ { id: '2', name: 'Widget B', value: 200, source: SERVICE_NAME },
23
+ { id: '3', name: 'Widget C', value: 300, source: SERVICE_NAME },
24
+ ]
25
+
26
+ // Request logging with caller identification
27
+ app.use((req: Request, _res: Response, next: NextFunction) => {
28
+ const caller = req.headers['x-service-name'] || 'unknown'
29
+ const timestamp = new Date().toISOString()
30
+ process.stdout.write(`[${timestamp}] ${req.method} ${req.path} (caller: ${caller})\n`)
31
+ next()
32
+ })
33
+
34
+ // Health check - public
35
+ app.get('/health', (_req: Request, res: Response) => {
36
+ res.json({
37
+ status: 'healthy',
38
+ service: SERVICE_NAME,
39
+ timestamp: new Date().toISOString(),
40
+ })
41
+ })
42
+
43
+ // Internal API - service-to-service only
44
+ // In production, Janus validates the auth: service requirement
45
+ app.get('/api/internal/items', (req: Request, res: Response) => {
46
+ // Log the service-to-service call
47
+ const callerService = req.headers['x-service-name'] as string
48
+ process.stdout.write(`Internal API called by: ${callerService || 'direct'}\n`)
49
+
50
+ res.json({
51
+ success: true,
52
+ data: items,
53
+ meta: {
54
+ source: SERVICE_NAME,
55
+ timestamp: new Date().toISOString(),
56
+ },
57
+ })
58
+ })
59
+
60
+ app.get('/api/internal/items/:id', (req: Request, res: Response) => {
61
+ const item = items.find(i => i.id === req.params.id)
62
+
63
+ if (!item) {
64
+ res.status(404).json({
65
+ success: false,
66
+ error: { code: 'NOT_FOUND', message: 'Item not found' },
67
+ })
68
+ return
69
+ }
70
+
71
+ res.json({
72
+ success: true,
73
+ data: item,
74
+ })
75
+ })
76
+
77
+ // Error handler
78
+ app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
79
+ process.stderr.write(`Error: ${err.message}\n`)
80
+ res.status(500).json({
81
+ success: false,
82
+ error: { code: 'INTERNAL_ERROR', message: 'Internal error' },
83
+ })
84
+ })
85
+
86
+ app.listen(PORT, () => {
87
+ process.stdout.write(`${SERVICE_NAME} listening on port ${PORT}\n`)
88
+ process.stdout.write(`This service exposes internal APIs for service-to-service communication\n`)
89
+ })
@@ -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,27 @@
1
+ FROM node:22-alpine AS builder
2
+ WORKDIR /app
3
+ COPY package*.json ./
4
+ RUN npm ci
5
+ COPY . .
6
+ RUN npm run build
7
+
8
+ FROM node:22-alpine
9
+ WORKDIR /app
10
+ COPY package*.json ./
11
+ RUN npm ci --production && npm cache clean --force
12
+ COPY --from=builder /app/dist ./dist
13
+
14
+ ENV NODE_ENV=production
15
+ ENV PORT=3000
16
+
17
+ RUN addgroup -g 1001 -S nodejs && \
18
+ adduser -S nodejs -u 1001 && \
19
+ chown -R nodejs:nodejs /app
20
+
21
+ USER nodejs
22
+ EXPOSE 3000
23
+
24
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
25
+ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
26
+
27
+ CMD ["node", "dist/index.js"]
@@ -0,0 +1,27 @@
1
+ apiVersion: backstage.io/v1alpha1
2
+ kind: Component
3
+ metadata:
4
+ name: {{name}}-service-b
5
+ description: Consumer service for {{name}} - calls service-a internally
6
+ tags:
7
+ - tawa
8
+ - api
9
+ - consumer
10
+ annotations:
11
+ insureco.io/framework: express
12
+ insureco.io/language: typescript
13
+ spec:
14
+ type: service
15
+ lifecycle: experimental
16
+ owner: team-platform
17
+ consumesApis:
18
+ - {{name}}-service-a-api
19
+ routes:
20
+ - path: /health
21
+ methods: [GET]
22
+ auth: none
23
+ description: Health check endpoint
24
+ - path: /api/*
25
+ methods: [GET, POST]
26
+ auth: required
27
+ description: Public API that aggregates data from service-a
@@ -0,0 +1,6 @@
1
+ apiVersion: v2
2
+ name: {{name}}-service-b
3
+ description: Consumer service Helm chart
4
+ type: application
5
+ version: 0.1.0
6
+ appVersion: "0.1.0"
@@ -0,0 +1,38 @@
1
+ replicaCount: 1
2
+
3
+ image:
4
+ repository: registry.digitalocean.com/insureco/{{name}}-service-b
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: 200m
18
+ memory: 256Mi
19
+ requests:
20
+ cpu: 50m
21
+ memory: 64Mi
22
+
23
+ env:
24
+ SERVICE_A_URL: "http://{{name}}-service-a:3000"
25
+
26
+ livenessProbe:
27
+ httpGet:
28
+ path: /health
29
+ port: 3000
30
+ initialDelaySeconds: 10
31
+ periodSeconds: 10
32
+
33
+ readinessProbe:
34
+ httpGet:
35
+ path: /health
36
+ port: 3000
37
+ initialDelaySeconds: 5
38
+ periodSeconds: 5