This commit is contained in:
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
.gitea
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
docker-compose.yml
|
||||||
|
nginx.conf
|
||||||
32
.gitea/workflows/build.yml
Normal file
32
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE: ${{ vars.REGISTRY_HOST }}/${{ gitea.repository_owner }}/catalyst
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ vars.REGISTRY_HOST }}
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE }}:latest
|
||||||
|
${{ env.IMAGE }}:${{ gitea.sha }}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
name: Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Write SSH key
|
|
||||||
run: |
|
|
||||||
echo "${{ secrets.DEPLOY_SSH_KEY }}" > /tmp/deploy_key
|
|
||||||
chmod 600 /tmp/deploy_key
|
|
||||||
|
|
||||||
- name: Sync files
|
|
||||||
run: |
|
|
||||||
rsync -avz --delete \
|
|
||||||
-e "ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no -p ${{ secrets.DEPLOY_SSH_PORT || 22 }}" \
|
|
||||||
--exclude='.git' \
|
|
||||||
--exclude='.gitea' \
|
|
||||||
--exclude='deploy' \
|
|
||||||
./ ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/srv/catalyst/
|
|
||||||
|
|
||||||
- name: Install dependencies and restart
|
|
||||||
run: |
|
|
||||||
ssh -i /tmp/deploy_key \
|
|
||||||
-o StrictHostKeyChecking=no \
|
|
||||||
-p ${{ secrets.DEPLOY_SSH_PORT || 22 }} \
|
|
||||||
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
|
|
||||||
"cd /srv/catalyst && npm ci && pm2 restart catalyst || pm2 start npm --name catalyst -- start"
|
|
||||||
|
|
||||||
- name: Cleanup
|
|
||||||
if: always()
|
|
||||||
run: rm -f /tmp/deploy_key
|
|
||||||
3
Dockerfile
Normal file
3
Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY . /usr/share/nginx/html
|
||||||
59
README.md
Normal file
59
README.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Catalyst
|
||||||
|
|
||||||
|
Instance registry for tracking infrastructure. Built as a single-page app — no backend, no framework.
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- **sql.js** — SQLite compiled to WebAssembly, runs entirely in the browser
|
||||||
|
- **localStorage** — persists the database between sessions
|
||||||
|
- **nginx** — serves the static files (via Docker)
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
index.html Entry point
|
||||||
|
css/app.css All styles
|
||||||
|
js/
|
||||||
|
config.js Service definitions and seed data
|
||||||
|
db.js Database layer (init, queries, mutations, persistence)
|
||||||
|
ui.js Rendering, modals, toast notifications
|
||||||
|
app.js Router and bootstrap
|
||||||
|
Dockerfile nginx:alpine image
|
||||||
|
nginx.conf SPA routing config (used by Dockerfile)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running locally
|
||||||
|
|
||||||
|
Requires a local HTTP server — `history.pushState` routing won't work over `file://`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx serve . --single
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open `http://localhost:3000`.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Push to `main`. Gitea Actions builds and pushes two image tags to your registry:
|
||||||
|
|
||||||
|
| Tag | Use |
|
||||||
|
|---|---|
|
||||||
|
| `:latest` | Always the current main |
|
||||||
|
| `:<sha>` | Pinned to a specific commit (use for rollbacks) |
|
||||||
|
|
||||||
|
Requires two things set in Gitea repository settings:
|
||||||
|
|
||||||
|
| Key | Type | Value |
|
||||||
|
|---|---|---|
|
||||||
|
| `REGISTRY_HOST` | Variable | your Gitea hostname |
|
||||||
|
| `GITEA_TOKEN` | Secret | token with `package:write` scope |
|
||||||
|
|
||||||
|
## Running the container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d -p 3000:80 <registry>/catalyst:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migrating to a real backend
|
||||||
|
|
||||||
|
All database operations are in `js/db.js`. The functions `getInstances`, `getInstance`, `createInstance`, `updateInstance`, and `deleteInstance` are the only things that would change — swap the `db.run()` / `db.exec()` calls for `fetch()` calls to your API and the rest of the app is untouched.
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Run once on a fresh server to prepare it for Catalyst deployments.
|
|
||||||
# Usage: bash setup.sh <deploy-public-key>
|
|
||||||
# Example: bash setup.sh "ssh-ed25519 AAAA... gitea-actions"
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
DEPLOY_KEY="${1:?usage: setup.sh <deploy-public-key>}"
|
|
||||||
|
|
||||||
echo "==> Installing Node.js"
|
|
||||||
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
|
|
||||||
apt-get install -y nodejs
|
|
||||||
|
|
||||||
echo "==> Installing PM2"
|
|
||||||
npm install -g pm2
|
|
||||||
pm2 startup systemd -u deploy --hp /home/deploy | tail -1 | bash
|
|
||||||
|
|
||||||
echo "==> Creating app directory"
|
|
||||||
mkdir -p /srv/catalyst
|
|
||||||
chown -R deploy:deploy /srv/catalyst
|
|
||||||
|
|
||||||
echo "==> Creating deploy user"
|
|
||||||
if ! id -u deploy &>/dev/null; then
|
|
||||||
useradd -m -s /bin/bash deploy
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEPLOY_SSH_DIR="/home/deploy/.ssh"
|
|
||||||
mkdir -p "$DEPLOY_SSH_DIR"
|
|
||||||
echo "$DEPLOY_KEY" >> "$DEPLOY_SSH_DIR/authorized_keys"
|
|
||||||
chmod 700 "$DEPLOY_SSH_DIR"
|
|
||||||
chmod 600 "$DEPLOY_SSH_DIR/authorized_keys"
|
|
||||||
chown -R deploy:deploy "$DEPLOY_SSH_DIR"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Done. Add these secrets to your Gitea repo:"
|
|
||||||
echo " DEPLOY_HOST → (your Tailscale IP or hostname)"
|
|
||||||
echo " DEPLOY_USER → deploy"
|
|
||||||
echo " DEPLOY_SSH_KEY → (the private key paired with the public key you provided)"
|
|
||||||
echo " DEPLOY_SSH_PORT → 22"
|
|
||||||
echo ""
|
|
||||||
echo "Catalyst will be served on port 3000."
|
|
||||||
19
nginx.conf
Normal file
19
nginx.conf
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# SPA fallback for client-side routing
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(css|js)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /index.html {
|
||||||
|
add_header Cache-Control "no-store";
|
||||||
|
}
|
||||||
|
}
|
||||||
10
package.json
10
package.json
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "catalyst",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "serve . --listen 3000 --single"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"serve": "^14.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user