diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml new file mode 100644 index 0000000..093185e --- /dev/null +++ b/.gitea/workflows/docker.yml @@ -0,0 +1,53 @@ +name: Docker + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Derive image name and tags + id: meta + shell: bash + run: | + set -euo pipefail + SERVER_URL="${GITHUB_SERVER_URL:-${GITEA_SERVER_URL:-}}" + REPO="${GITHUB_REPOSITORY:-${GITEA_REPOSITORY:-}}" + SHA="${GITHUB_SHA:-${GITEA_SHA:-}}" + if [[ -z "${SERVER_URL}" || -z "${REPO}" || -z "${SHA}" ]]; then + echo "Missing SERVER_URL/REPO/SHA env vars." >&2; exit 1 + fi + HOST=$(echo "${SERVER_URL}" | sed 's|https://||;s|http://||') + IMAGE=$(echo "${HOST}/${REPO}" | tr '[:upper:]' '[:lower:]') + SHORT_SHA=$(echo "${SHA}" | cut -c1-7) + echo "host=${HOST}" >> "$GITHUB_OUTPUT" + echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" + echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT" + + - name: Log in to Gitea container registry + uses: docker/login-action@v3 + with: + registry: ${{ steps.meta.outputs.host }} + username: ${{ github.actor }} + # Create a Gitea PAT with packages:write scope and add it as a + # repository secret named TKNTKN (Settings → Secrets) + password: ${{ secrets.TKNTKN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + push: true + tags: | + ${{ steps.meta.outputs.image }}:latest + ${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.short_sha }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..043d05c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM node:22-alpine AS build +WORKDIR /app +# better-sqlite3 has a native addon with no prebuilt binary for this +# platform/Node combo yet — build it from source. +RUN apk add --no-cache python3 make g++ +COPY package*.json ./ +RUN npm install +COPY . . +RUN npm run build + +FROM node:22-alpine +WORKDIR /app +# Same native-addon rebuild needed for the production-only install below; +# the toolchain is purged again afterward to keep the runtime image lean. +RUN apk add --no-cache python3 make g++ +COPY package*.json ./ +RUN npm install --omit=dev && apk del python3 make g++ +COPY --from=build /app/dist ./dist + +# DATA_DIR (SQLite DB) is a mutable runtime volume — not baked into the +# image, so redeploys don't clobber captured articles. +ENV DATA_DIR=/app/data + +EXPOSE 3070 +CMD ["node", "dist/server/index.js"] diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..a2de1fc --- /dev/null +++ b/TESTING.md @@ -0,0 +1,55 @@ +# Trying stash locally + +## 1. Start the server + +```bash +cd ~/temp/stash +cp .env.example .env +npm install +npm run token -- my-laptop +``` + +`npm run token` prints a bearer token — copy it. Then: + +```bash +npm run dev:server +``` + +Leave that running (listens on `:3070` by default). + +## 2. Load the extension + +- Open `chrome://extensions` (or `edge://extensions` — anything Chromium-based) +- Toggle **Developer mode** on (top right) +- Click **Load unpacked**, select `~/temp/stash/extension` +- Click the puzzle-piece icon in the toolbar → pin **stash** so it's visible + +## 3. Configure it + +- Right-click the stash icon → **Options** (or it'll auto-open the first + time it can't find a token) +- Server URL: `http://127.0.0.1:3070` +- Token: paste what `npm run token` printed +- Save + +## 4. Try it + +- Go to any real article (a blog post, news article, etc.) +- Click the stash toolbar icon, or right-click the page → **Send to stash** +- You should get a browser notification confirming the send +- Visit `http://127.0.0.1:3070/?t=` in a tab — you'll see it + in the list, click through to the cleaned reader view + +## Notes + +- **Firefox is a bit different** — use + `about:debugging#/runtime/this-firefox` → "Load Temporary Add-on" → pick + `manifest.json`. Temporary add-ons vanish on browser restart, so + Chrome/Chromium's unpacked-extension loading (which persists) is the + easier one to actually live with day to day. +- **Notifications permission** — Chrome may prompt once to allow + notifications from the extension. Allow it, or you just won't get the + success/failure toast (capture still works either way). +- This is all local/loopback for now. Once a server is deployed + somewhere reachable, point the extension's Server URL at that instead + — nothing else changes. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0748e61 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + app: + image: ${IMAGE} + container_name: stash + restart: unless-stopped + pull_policy: always + ports: + - "${HOST_PORT:-3070}:3070" + volumes: + # Named volume, not a bind mount — the SQLite DB holding captured + # articles and tokens must survive redeploys. + - stash-data:/app/data + +volumes: + stash-data: