diff --git a/.gitignore b/.gitignore index 324f46a..bef9db6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/\nserver.log +node_modules/ +server.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..96bd6bc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM node:22-alpine +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev +COPY server.js ./ +COPY public/ ./public/ +RUN mkdir -p /app/data && chgrp -R 0 /app && chmod -R g=u /app +ENV DATA_DIR=/app/data +EXPOSE 3333 +USER 1001 +CMD ["node", "server.js"] diff --git a/bootstrap/argo-app.yaml b/bootstrap/argo-app.yaml new file mode 100644 index 0000000..ceaa82d --- /dev/null +++ b/bootstrap/argo-app.yaml @@ -0,0 +1,20 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: clawrizon + namespace: openshift-gitops +spec: + project: default + source: + repoURL: https://gitea.apilab.us/cscott/priority-horizon + targetRevision: main + path: manifests + destination: + server: https://kubernetes.default.svc + namespace: clawrizon + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=false diff --git a/data.json b/data.json index 86e4292..8ba571b 100644 --- a/data.json +++ b/data.json @@ -57,7 +57,7 @@ { "id": 10, "column": "now", - "text": "Clawd: Add service/route for this (git first)", + "text": "✅ Clawd: Git + Gitea done. Route next?", "order": 3 } ], diff --git a/manifests/buildconfig.yaml b/manifests/buildconfig.yaml new file mode 100644 index 0000000..8843a57 --- /dev/null +++ b/manifests/buildconfig.yaml @@ -0,0 +1,21 @@ +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: clawrizon + namespace: clawrizon +spec: + source: + type: Git + git: + uri: https://gitea.apilab.us/cscott/priority-horizon + ref: main + strategy: + type: Docker + dockerStrategy: + dockerfilePath: Dockerfile + output: + to: + kind: ImageStreamTag + name: clawrizon:latest + triggers: + - type: ConfigChange diff --git a/manifests/deployment.yaml b/manifests/deployment.yaml new file mode 100644 index 0000000..94c364e --- /dev/null +++ b/manifests/deployment.yaml @@ -0,0 +1,47 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clawrizon + namespace: clawrizon + labels: + app: clawrizon +spec: + replicas: 1 + selector: + matchLabels: + app: clawrizon + template: + metadata: + labels: + app: clawrizon + spec: + containers: + - name: clawrizon + image: image-registry.openshift-image-registry.svc:5000/clawrizon/clawrizon:latest + ports: + - containerPort: 3333 + env: + - name: DATA_DIR + value: /app/data + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + volumeMounts: + - name: data + mountPath: /app/data + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + volumes: + - name: data + persistentVolumeClaim: + claimName: clawrizon-data diff --git a/manifests/imagestream.yaml b/manifests/imagestream.yaml new file mode 100644 index 0000000..c951940 --- /dev/null +++ b/manifests/imagestream.yaml @@ -0,0 +1,5 @@ +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: clawrizon + namespace: clawrizon diff --git a/manifests/namespace.yaml b/manifests/namespace.yaml new file mode 100644 index 0000000..5f1c5e6 --- /dev/null +++ b/manifests/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: clawrizon diff --git a/manifests/pvc.yaml b/manifests/pvc.yaml new file mode 100644 index 0000000..4aa2e30 --- /dev/null +++ b/manifests/pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: clawrizon-data + namespace: clawrizon +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-nvme-retain + resources: + requests: + storage: 1Gi diff --git a/manifests/route.yaml b/manifests/route.yaml new file mode 100644 index 0000000..ff8bb72 --- /dev/null +++ b/manifests/route.yaml @@ -0,0 +1,16 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: clawrizon + namespace: clawrizon +spec: + host: clawrizon.apps.lab.apilab.us + to: + kind: Service + name: clawrizon + weight: 100 + port: + targetPort: 3333 + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect diff --git a/manifests/service.yaml b/manifests/service.yaml new file mode 100644 index 0000000..2d4e975 --- /dev/null +++ b/manifests/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: clawrizon + namespace: clawrizon +spec: + selector: + app: clawrizon + ports: + - port: 3333 + targetPort: 3333 + protocol: TCP diff --git a/server.js b/server.js index 7d6cb1f..6c4c281 100644 --- a/server.js +++ b/server.js @@ -3,7 +3,14 @@ const fs = require('fs'); const path = require('path'); const app = express(); -const DATA_FILE = path.join(__dirname, 'data.json'); +const DATA_DIR = process.env.DATA_DIR || __dirname; +const DATA_FILE = path.join(DATA_DIR, 'data.json'); + +// Seed data.json if it does not exist +if (!fs.existsSync(DATA_FILE)) { + fs.mkdirSync(DATA_DIR, { recursive: true }); + fs.writeFileSync(DATA_FILE, JSON.stringify({ notes: [], nextId: 1 }, null, 2)); +} app.use(express.json()); app.use(express.static(path.join(__dirname, 'public'))); @@ -42,7 +49,6 @@ app.patch('/api/notes/:id', (req, res) => { if (req.body.column !== undefined) note.column = req.body.column; if (req.body.order !== undefined) note.order = req.body.order; if (req.body.reorder) { - // reorder: [{id, order}, ...] req.body.reorder.forEach(r => { const n = data.notes.find(x => x.id === r.id); if (n) { n.order = r.order; if (r.column) n.column = r.column; } @@ -61,10 +67,9 @@ app.delete('/api/notes/:id', (req, res) => { res.json({ ok: true }); }); -// Bulk reorder endpoint app.put('/api/reorder', (req, res) => { const data = readData(); - const { items } = req.body; // [{id, column, order}] + const { items } = req.body; if (!items) return res.status(400).json({ error: 'items required' }); items.forEach(r => { const n = data.notes.find(x => x.id === r.id);