Add Dockerfile, K8s manifests, and ArgoCD app for OpenShift deployment

This commit is contained in:
Clawd
2026-02-26 03:11:56 +00:00
parent feda0fbb02
commit b00c8d3eb0
12 changed files with 160 additions and 6 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
node_modules/\nserver.log
node_modules/
server.log

11
Dockerfile Normal file
View File

@@ -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"]

20
bootstrap/argo-app.yaml Normal file
View File

@@ -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

View File

@@ -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
}
],

View File

@@ -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

47
manifests/deployment.yaml Normal file
View File

@@ -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

View File

@@ -0,0 +1,5 @@
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
name: clawrizon
namespace: clawrizon

4
manifests/namespace.yaml Normal file
View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: clawrizon

12
manifests/pvc.yaml Normal file
View File

@@ -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

16
manifests/route.yaml Normal file
View File

@@ -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

12
manifests/service.yaml Normal file
View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: clawrizon
namespace: clawrizon
spec:
selector:
app: clawrizon
ports:
- port: 3333
targetPort: 3333
protocol: TCP

View File

@@ -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);