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