@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.
- package/dist/commands/git.d.ts +30 -0
- package/dist/commands/git.d.ts.map +1 -0
- package/dist/commands/git.js +300 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/oauth.d.ts +33 -0
- package/dist/commands/oauth.d.ts.map +1 -0
- package/dist/commands/oauth.js +181 -0
- package/dist/commands/oauth.js.map +1 -0
- package/dist/commands/user.d.ts +13 -0
- package/dist/commands/user.d.ts.map +1 -0
- package/dist/commands/user.js +96 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/bio.d.ts +13 -0
- package/dist/lib/bio.d.ts.map +1 -0
- package/dist/lib/bio.js +53 -0
- package/dist/lib/bio.js.map +1 -0
- package/dist/lib/builder.d.ts +17 -1
- package/dist/lib/builder.d.ts.map +1 -1
- package/dist/lib/builder.js +53 -2
- package/dist/lib/builder.js.map +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/forgejo.d.ts +149 -0
- package/dist/lib/forgejo.d.ts.map +1 -0
- package/dist/lib/forgejo.js +137 -0
- package/dist/lib/forgejo.js.map +1 -0
- package/dist/templates/api/.env.example +15 -0
- package/dist/templates/api/Dockerfile +45 -0
- package/dist/templates/api/README.md +85 -0
- package/dist/templates/api/catalog-info.yaml +51 -0
- package/dist/templates/api/helm/{{name}}/Chart.yaml +9 -0
- package/dist/templates/api/helm/{{name}}/templates/deployment.yaml +68 -0
- package/dist/templates/api/helm/{{name}}/templates/service.yaml +17 -0
- package/dist/templates/api/helm/{{name}}/values.yaml +55 -0
- package/dist/templates/api/package.json +33 -0
- package/dist/templates/api/src/index.ts +61 -0
- package/dist/templates/api/src/routes/example.ts +162 -0
- package/dist/templates/api/tsconfig.json +18 -0
- package/dist/templates/crosspod/README.md +87 -0
- package/dist/templates/crosspod/service-a/Dockerfile +27 -0
- package/dist/templates/crosspod/service-a/catalog-info.yaml +54 -0
- package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/Chart.yaml +6 -0
- package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/values.yaml +35 -0
- package/dist/templates/crosspod/service-a/package.json +29 -0
- package/dist/templates/crosspod/service-a/src/index.ts +89 -0
- package/dist/templates/crosspod/service-a/tsconfig.json +14 -0
- package/dist/templates/crosspod/service-b/Dockerfile +27 -0
- package/dist/templates/crosspod/service-b/catalog-info.yaml +27 -0
- package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/Chart.yaml +6 -0
- package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/values.yaml +38 -0
- package/dist/templates/crosspod/service-b/package.json +26 -0
- package/dist/templates/crosspod/service-b/src/index.ts +143 -0
- package/dist/templates/crosspod/service-b/tsconfig.json +14 -0
- package/dist/templates/nextjs/.env.example +11 -0
- package/dist/templates/nextjs/Dockerfile +51 -0
- package/dist/templates/nextjs/README.md +87 -0
- package/dist/templates/nextjs/catalog-info.yaml +16 -0
- package/dist/templates/nextjs/helm/{{name}}/Chart.yaml +9 -0
- package/dist/templates/nextjs/helm/{{name}}/templates/deployment.yaml +68 -0
- package/dist/templates/nextjs/helm/{{name}}/templates/service.yaml +17 -0
- package/dist/templates/nextjs/helm/{{name}}/values.yaml +51 -0
- package/dist/templates/nextjs/next.config.js +23 -0
- package/dist/templates/nextjs/package.json +29 -0
- package/dist/templates/nextjs/public/.gitkeep +0 -0
- package/dist/templates/nextjs/src/app/api/example/route.ts +63 -0
- package/dist/templates/nextjs/src/app/api/health/route.ts +10 -0
- package/dist/templates/nextjs/src/app/layout.tsx +18 -0
- package/dist/templates/nextjs/src/app/page.tsx +49 -0
- package/dist/templates/nextjs/tsconfig.json +26 -0
- package/dist/templates/worker/.env.example +13 -0
- package/dist/templates/worker/Dockerfile +26 -0
- package/dist/templates/worker/README.md +106 -0
- package/dist/templates/worker/catalog-info.yaml +19 -0
- package/dist/templates/worker/helm/{{name}}/Chart.yaml +9 -0
- package/dist/templates/worker/helm/{{name}}/templates/deployment.yaml +64 -0
- package/dist/templates/worker/helm/{{name}}/values.yaml +55 -0
- package/dist/templates/worker/package.json +26 -0
- package/dist/templates/worker/src/index.ts +185 -0
- package/dist/templates/worker/tsconfig.json +14 -0
- package/dist/types/index.d.ts +77 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export default function Home() {
|
|
2
|
+
return (
|
|
3
|
+
<main style={{
|
|
4
|
+
display: 'flex',
|
|
5
|
+
flexDirection: 'column',
|
|
6
|
+
alignItems: 'center',
|
|
7
|
+
justifyContent: 'center',
|
|
8
|
+
minHeight: '100vh',
|
|
9
|
+
padding: '2rem',
|
|
10
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
11
|
+
}}>
|
|
12
|
+
<h1 style={{ fontSize: '3rem', marginBottom: '1rem' }}>
|
|
13
|
+
{{name}}
|
|
14
|
+
</h1>
|
|
15
|
+
<p style={{ fontSize: '1.25rem', color: '#666', marginBottom: '2rem' }}>
|
|
16
|
+
{{description}}
|
|
17
|
+
</p>
|
|
18
|
+
<div style={{ display: 'flex', gap: '1rem' }}>
|
|
19
|
+
<a
|
|
20
|
+
href="/api/health"
|
|
21
|
+
style={{
|
|
22
|
+
padding: '0.75rem 1.5rem',
|
|
23
|
+
backgroundColor: '#0070f3',
|
|
24
|
+
color: 'white',
|
|
25
|
+
borderRadius: '0.5rem',
|
|
26
|
+
textDecoration: 'none',
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
Health Check
|
|
30
|
+
</a>
|
|
31
|
+
<a
|
|
32
|
+
href="/api/example"
|
|
33
|
+
style={{
|
|
34
|
+
padding: '0.75rem 1.5rem',
|
|
35
|
+
backgroundColor: '#333',
|
|
36
|
+
color: 'white',
|
|
37
|
+
borderRadius: '0.5rem',
|
|
38
|
+
textDecoration: 'none',
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
Example API
|
|
42
|
+
</a>
|
|
43
|
+
</div>
|
|
44
|
+
<footer style={{ marginTop: '4rem', color: '#999' }}>
|
|
45
|
+
Deployed on Tawa Platform
|
|
46
|
+
</footer>
|
|
47
|
+
</main>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
4
|
+
"allowJs": true,
|
|
5
|
+
"skipLibCheck": true,
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"module": "esnext",
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"jsx": "preserve",
|
|
14
|
+
"incremental": true,
|
|
15
|
+
"plugins": [
|
|
16
|
+
{
|
|
17
|
+
"name": "next"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"paths": {
|
|
21
|
+
"@/*": ["./src/*"]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
25
|
+
"exclude": ["node_modules"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
|
|
16
|
+
RUN addgroup -g 1001 -S nodejs && \
|
|
17
|
+
adduser -S nodejs -u 1001 && \
|
|
18
|
+
chown -R nodejs:nodejs /app
|
|
19
|
+
|
|
20
|
+
USER nodejs
|
|
21
|
+
|
|
22
|
+
# Health check via file existence
|
|
23
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
24
|
+
CMD test -f /tmp/healthy || exit 1
|
|
25
|
+
|
|
26
|
+
CMD ["node", "dist/index.js"]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
A background job processor using BullMQ and Redis.
|
|
6
|
+
|
|
7
|
+
## Getting Started
|
|
8
|
+
|
|
9
|
+
### Prerequisites
|
|
10
|
+
|
|
11
|
+
- Node.js 20+
|
|
12
|
+
- Redis server
|
|
13
|
+
|
|
14
|
+
### Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Development
|
|
21
|
+
|
|
22
|
+
Start Redis locally:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
docker run -d -p 6379:6379 redis:alpine
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Run the worker:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run dev
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Job Types
|
|
35
|
+
|
|
36
|
+
The worker processes these job types:
|
|
37
|
+
|
|
38
|
+
- `email` - Send email notifications
|
|
39
|
+
- `report` - Generate reports
|
|
40
|
+
- `sync` - Synchronize data
|
|
41
|
+
- `cleanup` - Clean up resources
|
|
42
|
+
|
|
43
|
+
## Adding Jobs to the Queue
|
|
44
|
+
|
|
45
|
+
From another service:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { Queue } from 'bullmq'
|
|
49
|
+
|
|
50
|
+
const queue = new Queue('{{name}}-jobs', {
|
|
51
|
+
connection: {
|
|
52
|
+
host: 'redis',
|
|
53
|
+
port: 6379,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Add a job
|
|
58
|
+
await queue.add('my-job', {
|
|
59
|
+
type: 'email',
|
|
60
|
+
payload: {
|
|
61
|
+
to: 'user@example.com',
|
|
62
|
+
subject: 'Hello',
|
|
63
|
+
body: 'World',
|
|
64
|
+
},
|
|
65
|
+
priority: 5,
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Health Checks
|
|
70
|
+
|
|
71
|
+
The worker uses file-based health checks:
|
|
72
|
+
|
|
73
|
+
- Creates `/tmp/healthy` when ready
|
|
74
|
+
- Removes the file on error or shutdown
|
|
75
|
+
- Kubernetes checks file existence for liveness
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
Environment variables:
|
|
80
|
+
|
|
81
|
+
| Variable | Default | Description |
|
|
82
|
+
|----------|---------|-------------|
|
|
83
|
+
| `REDIS_HOST` | localhost | Redis server host |
|
|
84
|
+
| `REDIS_PORT` | 6379 | Redis server port |
|
|
85
|
+
| `REDIS_PASSWORD` | - | Redis password |
|
|
86
|
+
| `QUEUE_NAME` | {{name}}-jobs | Queue name |
|
|
87
|
+
| `WORKER_CONCURRENCY` | 5 | Max concurrent jobs |
|
|
88
|
+
|
|
89
|
+
## Deployment
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
iec link
|
|
93
|
+
iec deploy
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Project Structure
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
{{name}}/
|
|
100
|
+
├── src/
|
|
101
|
+
│ └── index.ts # Worker entry point
|
|
102
|
+
├── helm/
|
|
103
|
+
│ └── {{name}}/ # Kubernetes Helm chart
|
|
104
|
+
├── Dockerfile
|
|
105
|
+
└── catalog-info.yaml
|
|
106
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
apiVersion: backstage.io/v1alpha1
|
|
2
|
+
kind: Component
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{name}}
|
|
5
|
+
description: {{description}}
|
|
6
|
+
tags:
|
|
7
|
+
- tawa
|
|
8
|
+
- worker
|
|
9
|
+
- background-job
|
|
10
|
+
annotations:
|
|
11
|
+
insureco.io/framework: bullmq
|
|
12
|
+
insureco.io/language: typescript
|
|
13
|
+
insureco.io/type: worker
|
|
14
|
+
spec:
|
|
15
|
+
type: service
|
|
16
|
+
lifecycle: experimental
|
|
17
|
+
owner: team-platform
|
|
18
|
+
# Workers typically don't expose HTTP routes
|
|
19
|
+
# Health is monitored via TCP probe or file-based health check
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
envFrom:
|
|
30
|
+
- configMapRef:
|
|
31
|
+
name: {{ .Release.Name }}-config
|
|
32
|
+
optional: true
|
|
33
|
+
- secretRef:
|
|
34
|
+
name: {{ .Release.Name }}-secrets
|
|
35
|
+
optional: true
|
|
36
|
+
{{- with .Values.env }}
|
|
37
|
+
env:
|
|
38
|
+
{{- range $key, $value := . }}
|
|
39
|
+
- name: {{ $key }}
|
|
40
|
+
value: {{ $value | quote }}
|
|
41
|
+
{{- end }}
|
|
42
|
+
{{- end }}
|
|
43
|
+
{{- with .Values.livenessProbe }}
|
|
44
|
+
livenessProbe:
|
|
45
|
+
{{- toYaml . | nindent 12 }}
|
|
46
|
+
{{- end }}
|
|
47
|
+
{{- with .Values.readinessProbe }}
|
|
48
|
+
readinessProbe:
|
|
49
|
+
{{- toYaml . | nindent 12 }}
|
|
50
|
+
{{- end }}
|
|
51
|
+
resources:
|
|
52
|
+
{{- toYaml .Values.resources | nindent 12 }}
|
|
53
|
+
{{- with .Values.nodeSelector }}
|
|
54
|
+
nodeSelector:
|
|
55
|
+
{{- toYaml . | nindent 8 }}
|
|
56
|
+
{{- end }}
|
|
57
|
+
{{- with .Values.affinity }}
|
|
58
|
+
affinity:
|
|
59
|
+
{{- toYaml . | nindent 8 }}
|
|
60
|
+
{{- end }}
|
|
61
|
+
{{- with .Values.tolerations }}
|
|
62
|
+
tolerations:
|
|
63
|
+
{{- toYaml . | nindent 8 }}
|
|
64
|
+
{{- end }}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
# Workers don't expose HTTP services
|
|
12
|
+
service:
|
|
13
|
+
enabled: false
|
|
14
|
+
|
|
15
|
+
resources:
|
|
16
|
+
limits:
|
|
17
|
+
cpu: 500m
|
|
18
|
+
memory: 512Mi
|
|
19
|
+
requests:
|
|
20
|
+
cpu: 100m
|
|
21
|
+
memory: 128Mi
|
|
22
|
+
|
|
23
|
+
env:
|
|
24
|
+
REDIS_HOST: redis
|
|
25
|
+
REDIS_PORT: "6379"
|
|
26
|
+
QUEUE_NAME: {{name}}-jobs
|
|
27
|
+
WORKER_CONCURRENCY: "5"
|
|
28
|
+
|
|
29
|
+
secrets: {}
|
|
30
|
+
# REDIS_PASSWORD: your-redis-password
|
|
31
|
+
|
|
32
|
+
# File-based health check
|
|
33
|
+
livenessProbe:
|
|
34
|
+
exec:
|
|
35
|
+
command:
|
|
36
|
+
- test
|
|
37
|
+
- -f
|
|
38
|
+
- /tmp/healthy
|
|
39
|
+
initialDelaySeconds: 10
|
|
40
|
+
periodSeconds: 10
|
|
41
|
+
|
|
42
|
+
readinessProbe:
|
|
43
|
+
exec:
|
|
44
|
+
command:
|
|
45
|
+
- test
|
|
46
|
+
- -f
|
|
47
|
+
- /tmp/healthy
|
|
48
|
+
initialDelaySeconds: 5
|
|
49
|
+
periodSeconds: 5
|
|
50
|
+
|
|
51
|
+
nodeSelector: {}
|
|
52
|
+
|
|
53
|
+
tolerations: []
|
|
54
|
+
|
|
55
|
+
affinity: {}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "{{description}}",
|
|
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
|
+
"bullmq": "^5.0.0",
|
|
15
|
+
"ioredis": "^5.4.0",
|
|
16
|
+
"zod": "^3.24.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
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,185 @@
|
|
|
1
|
+
import { Worker, Queue, Job } from 'bullmq'
|
|
2
|
+
import { writeFile, unlink } from 'node:fs/promises'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
const SERVICE_NAME = '{{name}}'
|
|
6
|
+
const QUEUE_NAME = process.env.QUEUE_NAME || '{{name}}-jobs'
|
|
7
|
+
const HEALTH_FILE = '/tmp/healthy'
|
|
8
|
+
|
|
9
|
+
// Redis connection configuration
|
|
10
|
+
const redisConnection = {
|
|
11
|
+
host: process.env.REDIS_HOST || 'localhost',
|
|
12
|
+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
|
|
13
|
+
password: process.env.REDIS_PASSWORD,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Job data schema
|
|
17
|
+
const JobDataSchema = z.object({
|
|
18
|
+
type: z.enum(['email', 'report', 'sync', 'cleanup']),
|
|
19
|
+
payload: z.record(z.unknown()),
|
|
20
|
+
priority: z.number().int().min(1).max(10).optional().default(5),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
type JobData = z.infer<typeof JobDataSchema>
|
|
24
|
+
|
|
25
|
+
// Job result type
|
|
26
|
+
interface JobResult {
|
|
27
|
+
success: boolean
|
|
28
|
+
processedAt: string
|
|
29
|
+
duration: number
|
|
30
|
+
details?: Record<string, unknown>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Process different job types
|
|
34
|
+
async function processJob(job: Job<JobData>): Promise<JobResult> {
|
|
35
|
+
const startTime = Date.now()
|
|
36
|
+
const { type, payload } = job.data
|
|
37
|
+
|
|
38
|
+
process.stdout.write(`Processing ${type} job ${job.id}\n`)
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
switch (type) {
|
|
42
|
+
case 'email':
|
|
43
|
+
await processEmailJob(payload)
|
|
44
|
+
break
|
|
45
|
+
case 'report':
|
|
46
|
+
await processReportJob(payload)
|
|
47
|
+
break
|
|
48
|
+
case 'sync':
|
|
49
|
+
await processSyncJob(payload)
|
|
50
|
+
break
|
|
51
|
+
case 'cleanup':
|
|
52
|
+
await processCleanupJob(payload)
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const duration = Date.now() - startTime
|
|
57
|
+
process.stdout.write(`Completed ${type} job ${job.id} in ${duration}ms\n`)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
success: true,
|
|
61
|
+
processedAt: new Date().toISOString(),
|
|
62
|
+
duration,
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const err = error as Error
|
|
66
|
+
process.stderr.write(`Failed ${type} job ${job.id}: ${err.message}\n`)
|
|
67
|
+
throw error
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Example job processors
|
|
72
|
+
async function processEmailJob(payload: Record<string, unknown>): Promise<void> {
|
|
73
|
+
// Simulate email sending
|
|
74
|
+
await delay(100)
|
|
75
|
+
process.stdout.write(`Sent email to ${payload.to}\n`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function processReportJob(payload: Record<string, unknown>): Promise<void> {
|
|
79
|
+
// Simulate report generation
|
|
80
|
+
await delay(500)
|
|
81
|
+
process.stdout.write(`Generated report: ${payload.reportType}\n`)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function processSyncJob(payload: Record<string, unknown>): Promise<void> {
|
|
85
|
+
// Simulate data sync
|
|
86
|
+
await delay(200)
|
|
87
|
+
process.stdout.write(`Synced data from ${payload.source}\n`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function processCleanupJob(payload: Record<string, unknown>): Promise<void> {
|
|
91
|
+
// Simulate cleanup
|
|
92
|
+
await delay(50)
|
|
93
|
+
process.stdout.write(`Cleaned up ${payload.target}\n`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function delay(ms: number): Promise<void> {
|
|
97
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Health check via file
|
|
101
|
+
async function setHealthy(healthy: boolean): Promise<void> {
|
|
102
|
+
if (healthy) {
|
|
103
|
+
await writeFile(HEALTH_FILE, new Date().toISOString())
|
|
104
|
+
} else {
|
|
105
|
+
try {
|
|
106
|
+
await unlink(HEALTH_FILE)
|
|
107
|
+
} catch {
|
|
108
|
+
// File may not exist
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Main worker setup
|
|
114
|
+
async function main(): Promise<void> {
|
|
115
|
+
process.stdout.write(`Starting ${SERVICE_NAME} worker\n`)
|
|
116
|
+
process.stdout.write(`Connecting to Redis at ${redisConnection.host}:${redisConnection.port}\n`)
|
|
117
|
+
process.stdout.write(`Listening on queue: ${QUEUE_NAME}\n`)
|
|
118
|
+
|
|
119
|
+
// Create the worker
|
|
120
|
+
const worker = new Worker<JobData, JobResult>(
|
|
121
|
+
QUEUE_NAME,
|
|
122
|
+
async (job) => {
|
|
123
|
+
// Validate job data
|
|
124
|
+
const result = JobDataSchema.safeParse(job.data)
|
|
125
|
+
if (!result.success) {
|
|
126
|
+
throw new Error(`Invalid job data: ${result.error.message}`)
|
|
127
|
+
}
|
|
128
|
+
return processJob(job)
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
connection: redisConnection,
|
|
132
|
+
concurrency: parseInt(process.env.WORKER_CONCURRENCY || '5', 10),
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
// Event handlers
|
|
137
|
+
worker.on('ready', async () => {
|
|
138
|
+
process.stdout.write('Worker is ready and listening for jobs\n')
|
|
139
|
+
await setHealthy(true)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
worker.on('completed', (job, result) => {
|
|
143
|
+
process.stdout.write(`Job ${job.id} completed: ${JSON.stringify(result)}\n`)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
worker.on('failed', (job, error) => {
|
|
147
|
+
process.stderr.write(`Job ${job?.id} failed: ${error.message}\n`)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
worker.on('error', async (error) => {
|
|
151
|
+
process.stderr.write(`Worker error: ${error.message}\n`)
|
|
152
|
+
await setHealthy(false)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Graceful shutdown
|
|
156
|
+
async function shutdown(): Promise<void> {
|
|
157
|
+
process.stdout.write('Shutting down worker...\n')
|
|
158
|
+
await setHealthy(false)
|
|
159
|
+
await worker.close()
|
|
160
|
+
process.exit(0)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
process.on('SIGTERM', shutdown)
|
|
164
|
+
process.on('SIGINT', shutdown)
|
|
165
|
+
|
|
166
|
+
// Example: Create queue for adding jobs (usually done by another service)
|
|
167
|
+
const queue = new Queue<JobData>(QUEUE_NAME, { connection: redisConnection })
|
|
168
|
+
|
|
169
|
+
// Add a test job in development
|
|
170
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
171
|
+
await queue.add('test-job', {
|
|
172
|
+
type: 'email',
|
|
173
|
+
payload: { to: 'test@example.com', subject: 'Test' },
|
|
174
|
+
priority: 5,
|
|
175
|
+
})
|
|
176
|
+
process.stdout.write('Added test job to queue\n')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await queue.close()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main().catch((error) => {
|
|
183
|
+
process.stderr.write(`Fatal error: ${error.message}\n`)
|
|
184
|
+
process.exit(1)
|
|
185
|
+
})
|
|
@@ -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
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -452,4 +452,81 @@ export interface FixPreview {
|
|
|
452
452
|
}>;
|
|
453
453
|
commands: string[];
|
|
454
454
|
}
|
|
455
|
+
export interface OAuthClient {
|
|
456
|
+
clientId: string;
|
|
457
|
+
name: string;
|
|
458
|
+
description?: string;
|
|
459
|
+
redirectUris: string[];
|
|
460
|
+
allowedScopes: string[];
|
|
461
|
+
allowedGrantTypes?: string[];
|
|
462
|
+
isActive: boolean;
|
|
463
|
+
isConfidential?: boolean;
|
|
464
|
+
accessTokenTtl?: number;
|
|
465
|
+
refreshTokenTtl?: number;
|
|
466
|
+
createdAt: string;
|
|
467
|
+
updatedAt: string;
|
|
468
|
+
}
|
|
469
|
+
export interface CreateOAuthClientRequest {
|
|
470
|
+
name: string;
|
|
471
|
+
description?: string;
|
|
472
|
+
redirectUris: string[];
|
|
473
|
+
allowedScopes?: string[];
|
|
474
|
+
allowedGrantTypes?: string[];
|
|
475
|
+
isConfidential?: boolean;
|
|
476
|
+
accessTokenTtl?: number;
|
|
477
|
+
refreshTokenTtl?: number;
|
|
478
|
+
}
|
|
479
|
+
export interface CreateOAuthClientResponse {
|
|
480
|
+
clientId: string;
|
|
481
|
+
clientSecret: string;
|
|
482
|
+
name: string;
|
|
483
|
+
redirectUris: string[];
|
|
484
|
+
message: string;
|
|
485
|
+
}
|
|
486
|
+
export interface UpdateOAuthClientRequest {
|
|
487
|
+
name?: string;
|
|
488
|
+
description?: string;
|
|
489
|
+
redirectUris?: string[];
|
|
490
|
+
allowedScopes?: string[];
|
|
491
|
+
allowedGrantTypes?: string[];
|
|
492
|
+
isActive?: boolean;
|
|
493
|
+
accessTokenTtl?: number;
|
|
494
|
+
refreshTokenTtl?: number;
|
|
495
|
+
}
|
|
496
|
+
export interface RegenerateSecretResponse {
|
|
497
|
+
clientId: string;
|
|
498
|
+
clientSecret: string;
|
|
499
|
+
message: string;
|
|
500
|
+
}
|
|
501
|
+
export type UserType = 'employee' | 'agent' | 'client' | 'insured' | 'partner' | 'vendor';
|
|
502
|
+
export interface ProvisionUserRequest {
|
|
503
|
+
email: string;
|
|
504
|
+
firstName: string;
|
|
505
|
+
lastName: string;
|
|
506
|
+
password: string;
|
|
507
|
+
userType?: UserType;
|
|
508
|
+
modules?: string[];
|
|
509
|
+
organizationId?: string;
|
|
510
|
+
roleIds?: string[];
|
|
511
|
+
jobTitle?: string;
|
|
512
|
+
}
|
|
513
|
+
export interface ProvisionedUser {
|
|
514
|
+
bioId: string;
|
|
515
|
+
email: string;
|
|
516
|
+
firstName: string;
|
|
517
|
+
lastName: string;
|
|
518
|
+
displayName: string;
|
|
519
|
+
userType: string;
|
|
520
|
+
status: string;
|
|
521
|
+
enabled_modules: string[];
|
|
522
|
+
createdAt: string;
|
|
523
|
+
}
|
|
524
|
+
export interface UserSearchResult {
|
|
525
|
+
id: string;
|
|
526
|
+
email: string;
|
|
527
|
+
name: string;
|
|
528
|
+
firstName: string;
|
|
529
|
+
lastName: string;
|
|
530
|
+
status: string;
|
|
531
|
+
}
|
|
455
532
|
//# sourceMappingURL=index.d.ts.map
|