Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
af5d38e
feat: add integrations namespace for optional third-party head tags
codeGlaze Feb 26, 2026
7865ef6
feat: make CSP extensible for third-party integrations
codeGlaze Feb 26, 2026
667ae3c
fix: typos in classes/email, remove debug statement from views
codeGlaze Feb 26, 2026
53ad4d6
feat: add branding namespace for fork-neutral identity config
codeGlaze Feb 26, 2026
6f6bc12
refactor: wire routes, email, and splash page to branding namespace
codeGlaze Feb 26, 2026
9c852f8
refactor: wire privacy.clj to branding namespace
codeGlaze Feb 26, 2026
af228f1
feat: add client-side integration stubs (integrations.cljs)
codeGlaze Feb 26, 2026
0982c84
docs: add branding and integrations env vars to .env.example
codeGlaze Feb 26, 2026
69b3e24
fix: add PORT fallback (8890) in prod service map
codeGlaze Feb 26, 2026
4295ce0
refactor: add branding config bridge, user-tier/user-data stubs, neut…
codeGlaze Feb 26, 2026
458244b
fix: views_2.cljc branding reader conditionals + add track-character-…
codeGlaze Feb 26, 2026
79e736b
refactor: wire shared files to integrations hooks + field-limits
codeGlaze Feb 26, 2026
39ff156
fix: rename PDF Upsell section header to PDF Options Slot
codeGlaze Feb 26, 2026
fce75de
docs: add branding and integrations configuration guide
codeGlaze Feb 26, 2026
92528c8
fix: update integrations.cljs ns docstring to reflect minimal defaults
codeGlaze Feb 26, 2026
fd86faf
fix: hardening pass — email, privacy, social icons, config bridge stubs
codeGlaze Feb 26, 2026
69eafaa
refactor: move fork-customization files into fork/ subdirectory
codeGlaze Feb 26, 2026
62381b8
feat: email preferences — My Account toggle + JWT unsubscribe endpoint
codeGlaze Feb 26, 2026
426c0bd
feat: auto-detect Figwheel WebSocket URL for Codespaces
codeGlaze Feb 26, 2026
bd6d562
docs: add remote dev documentation to start.sh help and menu
codeGlaze Feb 26, 2026
49fee11
fix: guard API subscriptions against unauthenticated HTTP calls
codeGlaze Feb 26, 2026
2374557
fix: subscribe-outside-reactive-context warnings + folders auth guard
codeGlaze Feb 26, 2026
2560f39
fix: error notification email — throttle, scrub, and fix paren bug
codeGlaze Feb 26, 2026
be34a68
fix: code quality cleanup — prn→log, dead require, preferences re-read
codeGlaze Feb 26, 2026
00d970d
fix: dev-mode? boolean parsing — (boolean "false") is truthy in Clojure
codeGlaze Feb 26, 2026
856947a
docs: update all docs for fork/ reorg, dev-mode? fix, and new KB entries
codeGlaze Feb 26, 2026
bd7174c
fix: mobile header tab flyout — click handler was returning fn, not c…
codeGlaze Feb 26, 2026
89fe5e8
fix: clear loading overlay on forced login redirect
codeGlaze Feb 27, 2026
4ebc122
fix: loading counter instead of boolean — parallel HTTP calls no long…
codeGlaze Feb 27, 2026
12ba1e7
fix: PDFBox 3.x API migration for spell card generation
codeGlaze Feb 27, 2026
4fa1519
fix: verify-user-session checked wrong token path — stale sessions ca…
codeGlaze Mar 1, 2026
cda2d77
fix: share-links path-for paren bug + PDFBox 3.x loadPDF InputStream fix
codeGlaze Mar 1, 2026
6b39fc8
fix: build-date uses UTC in Codespaces — shows wrong date for CST builds
codeGlaze Mar 1, 2026
214517a
chore: merge docker-compose-build.yaml into docker-compose.yaml
codeGlaze Mar 1, 2026
5ea02b5
fix: CI Docker test used Docker Hub images instead of locally-built ones
codeGlaze Mar 1, 2026
3250210
chore: add .gitattributes, gitignore credentials, restore DinD
codeGlaze Mar 3, 2026
ffd7afb
chore: remove devcontainer.json from merge=ours — personal settings s…
codeGlaze Mar 3, 2026
f7e1973
feat: docker infrastructure — check, build, deploy, upgrade, secrets …
codeGlaze Mar 3, 2026
9125484
refactor: extract branch-specific code into fork/ overrides
codeGlaze Mar 3, 2026
16ae308
refactor: move PDF sheet style list into fork/integrations
codeGlaze Mar 4, 2026
e637ae6
sync shared infrastructure from dmv/hotfix-integrations
codeGlaze Mar 5, 2026
73a9246
fix: update remaining docker-setup.sh references to run
codeGlaze Mar 5, 2026
c220e55
fix: resolve shellcheck warnings in run script
codeGlaze Mar 5, 2026
71609dd
fix: CI — remove fork-incompatible PR comment, update password check
codeGlaze Mar 5, 2026
a1a6dae
fix: correct log labels — success for completions, not change
codeGlaze Mar 5, 2026
7d48215
fix: CI cleanup between setup steps — naked ./run creates H2 data
codeGlaze Mar 5, 2026
ba79f05
fix: sudo rm for H2 data — datomic user owns the files
codeGlaze Mar 5, 2026
5895f68
feat: first-class Docker Swarm & Portainer support
codeGlaze Mar 6, 2026
a4556c5
docs: update DOCKER.md with Swarm/Portainer workflow
codeGlaze Mar 6, 2026
62c8212
fix: CI shellcheck — add -x flag and suppress info-level findings
codeGlaze Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"features": {
"ghcr.io/devcontainers/features/sshd:1": {
"version": "latest"
}
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"forwardPorts": [8890, 3449, 4334],
"portsAttributes": {
Expand All @@ -17,7 +18,8 @@
},
"3449": {
"label": "Figwheel",
"onAutoForward": "silent"
"onAutoForward": "silent",
"visibility": "public"
},
"4334": {
"label": "Datomic",
Expand Down
61 changes: 55 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@
# cp .env.example .env
#
# Or run the setup script to generate .env with secure random values:
# ./docker-setup.sh
# ./run
# ============================================================================

# --- Application ---
PORT=8890
TZ=America/Chicago

# Image tag for docker-compose.yaml (pre-built images only, ignored by build compose)
# ORCPUB_TAG=release-v2.5.0.27
# Docker image names. Set these if you tag your own builds
# (e.g., docker build -t dmv:2.6.0.0 .) so compose/swarm finds them.
# Defaults: orcpub-app, orcpub-datomic
# ORCPUB_IMAGE=dmv:2.6.0.0
# DATOMIC_IMAGE=orcpub-datomic

# --- Datomic Database ---
# Datomic Pro with dev storage protocol (required for Java 21 support)
# ADMIN_PASSWORD secures the Datomic admin interface
# DATOMIC_PASSWORD is used by the application to connect to Datomic
# The password in DATOMIC_URL must match DATOMIC_PASSWORD
# DATOMIC_PASSWORD is used by both the transactor and the app to authenticate.
# The app reads it separately and appends ?password= to DATOMIC_URL at startup,
# so you don't need to embed the password in the URL.
# DATOMIC_URL should NOT contain ?password= (the app adds it from DATOMIC_PASSWORD).
# Old URLs with ?password= still work — the embedded password takes priority.
ADMIN_PASSWORD=change-me-admin
DATOMIC_PASSWORD=change-me-datomic
DATOMIC_URL=datomic:dev://datomic:4334/orcpub?password=change-me-datomic
DATOMIC_URL=datomic:dev://datomic:4334/orcpub

# --- Transactor Tuning ---
# These rarely need changing. See docker/transactor.properties.template.
Expand All @@ -43,6 +50,7 @@ CSP_POLICY=strict

# Dev mode: CSP violations are logged (Report-Only) instead of blocked,
# allowing Figwheel hot-reload scripts to execute.
# Must be the string "true" (case-insensitive). Any other value is treated as false.
DEV_MODE=true

# --- Plugins ---
Expand All @@ -54,6 +62,15 @@ DEV_MODE=true
# Defaults to project logs/ if unset
LOG_DIR=

# --- Remote Dev (optional) ---
# Figwheel WebSocket port for hot-reload (default: 3449)
# FIGWHEEL_PORT=3449

# Figwheel WebSocket URL override for remote environments (Gitpod, tunnels, etc.)
# Auto-detected for GitHub Codespaces — only set this for other remote setups.
# Example: wss://my-remote-host:3449/figwheel-connect
# FIGWHEEL_CONNECT_URL=

# --- Email (SMTP) ---
# Leave EMAIL_SERVER_URL empty to disable email functionality
EMAIL_SERVER_URL=
Expand All @@ -65,6 +82,38 @@ EMAIL_ERRORS_TO=
EMAIL_SSL=FALSE
EMAIL_TLS=FALSE

# --- Branding (optional) ---
# Override app identity for forks. All have sensible defaults in fork/branding.clj.
# APP_NAME=Dungeon Master's Vault
# APP_URL=https://www.dungeonmastersvault.com
# APP_LOGO_PATH=/image/dmv-logo.svg
# APP_OG_IMAGE=/image/dmv-box-logo.png
# APP_COPYRIGHT_HOLDER=Dungeon Master's Vault
# APP_COPYRIGHT_YEAR=2026
# APP_EMAIL_SENDER_NAME=Dungeon Master's Vault Team
# APP_PAGE_TITLE=Dungeon Master's Vault
# APP_TAGLINE=A D&D 5e character builder and resource compendium
# APP_SUPPORT_EMAIL=thDM@dungeonmastersvault.com
# APP_HELP_URL=https://www.dungeonmastersvault.com/help/

# --- Social Links (optional) ---
# Shown in app header. Leave empty to hide a link.
# APP_SOCIAL_PATREON=https://www.patreon.com/DungeonMastersVault
# APP_SOCIAL_FACEBOOK=https://www.facebook.com/groups/252484128656613/
# APP_SOCIAL_BLUESKY=
# APP_SOCIAL_TWITTER=https://twitter.com/thdmv
# APP_SOCIAL_REDDIT=
# APP_SOCIAL_DISCORD=

# --- Analytics & Ads (optional) ---
# Third-party integrations. Leave empty to disable.
# Server-side (fork/integrations.clj) loads SDK scripts; client-side (fork/integrations.cljs)
# provides in-app hooks. CSP domains are auto-derived from these values.
# MATOMO_URL=https://analytics.dungeonmastersvault.com
# MATOMO_SITE_ID=1
# ADSENSE_CLIENT=ca-pub-3202063096003962
# ADSENSE_SLOT=4970831358

# --- Initial Admin User (optional) ---
# Set these then run: ./docker-user.sh init
# Safe to run multiple times — duplicates are skipped.
Expand Down
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Fork override files — keep each branch's version during merges.
# DMV has real implementations, public/breaking has stubs.
src/clj/orcpub/fork/** merge=ours
src/cljc/orcpub/fork/** merge=ours
src/cljs/orcpub/fork/** merge=ours

# DMV branding assets — keep each branch's version during merges.
# These only exist on dmv/ and should never appear on public branches.
resources/public/image/*DM* merge=ours
resources/public/image/patron_button* merge=ours
resources/public/js/dungeonmastersvault* merge=ours
resources/public/ads.txt merge=ours
deploy/error/** merge=ours
deploy/maintenance/** merge=ours
73 changes: 0 additions & 73 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ on:

permissions:
contents: read
pull-requests: write
checks: write

jobs:
Expand Down Expand Up @@ -162,78 +161,6 @@ jobs:
exit 1
fi

- name: Post PR comment with results
if: github.event_name == 'pull_request' && always()
continue-on-error: true # Fork PRs get read-only GITHUB_TOKEN
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');

const lintOutput = fs.existsSync('lint-output.txt') ? fs.readFileSync('lint-output.txt', 'utf8') : 'No output';
const testOutput = fs.existsSync('test-output.txt') ? fs.readFileSync('test-output.txt', 'utf8') : 'No output';
const cljsOutput = fs.existsSync('cljs-output.txt') ? fs.readFileSync('cljs-output.txt', 'utf8') : 'No output';

const lintOk = '${{ steps.lint.outcome }}' === 'success';
const testOk = '${{ steps.test.outcome }}' === 'success';
const cljsOk = '${{ steps.cljs.outcome }}' === 'success';
const allOk = lintOk && testOk && cljsOk;
const stackLabel = '${{ needs.detect-stack.outputs.stack-label }}';

const testMatch = testOutput.match(/Ran (\d+) tests containing (\d+) assertions/);
const testSummary = testMatch ? `${testMatch[1]} tests, ${testMatch[2]} assertions` : 'See logs';

const status = allOk ? 'All checks passed' : 'Some checks failed';
const body = [
`## ${status}`,
'',
'| Check | Status | Details |',
'|-------|--------|---------|',
`| Lint | ${lintOk ? 'Pass' : 'Fail'} | ${lintOk ? 'No errors' : 'See workflow logs'} |`,
`| Tests | ${testOk ? 'Pass' : 'Fail'} | ${testOk ? testSummary : 'See workflow logs'} |`,
`| CLJS Build | ${cljsOk ? 'Pass' : 'Fail'} | ${cljsOk ? 'Compiled' : 'See workflow logs'} |`,
'',
`**Stack**: ${stackLabel}`,
'',
'<details>',
'<summary>Full test output</summary>',
'',
'```',
testOutput.slice(-2000),
'```',
'',
'</details>',
'',
'---',
`*[Workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})*`
].join('\n');

const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});

const botComment = comments.find(c =>
c.user.type === 'Bot' && (c.body.includes('All checks passed') || c.body.includes('Some checks failed'))
);

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}

- name: Upload artifacts on failure
if: failure()
uses: actions/upload-artifact@v4
Expand Down
59 changes: 42 additions & 17 deletions .github/workflows/docker-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# Java 8 (legacy): Pulls pre-built images from Docker Hub — skipped gracefully
# when images are unavailable.
# Java 21 (modern): Builds from source using docker-compose-build.yaml with
# Java 21 (modern): Builds from source using docker-compose.yaml (--build) with
# Datomic Pro and eclipse-temurin:21.

name: Docker Integration Test
Expand All @@ -14,7 +14,7 @@ on:
paths:
- 'docker/**'
- 'docker-compose*.yaml'
- 'docker-setup.sh'
- 'run'
- 'docker-user.sh'
- 'deploy/**'
- '.github/workflows/docker-integration.yml'
Expand All @@ -38,7 +38,7 @@ jobs:
if [ -f "dev.cljs.edn" ]; then
echo "Detected: Java 21 / Datomic Pro / figwheel-main → build from source"
echo "build-mode=build" >> $GITHUB_OUTPUT
echo "compose-file=docker-compose-build.yaml" >> $GITHUB_OUTPUT
echo "compose-file=docker-compose.yaml" >> $GITHUB_OUTPUT
echo "stack-label=Java 21 / Datomic Pro (build)" >> $GITHUB_OUTPUT
else
echo "Detected: Java 8 / Datomic Free / cljsbuild → pull pre-built"
Expand All @@ -65,23 +65,38 @@ jobs:
- name: Lint shell scripts
run: |
sudo apt-get update -qq && sudo apt-get install -y -qq shellcheck
shellcheck docker-setup.sh docker-user.sh
shellcheck -x --severity=warning run docker-user.sh scripts/swarm.sh

- name: Run docker-setup.sh --auto
- name: Run run --auto
run: |
./docker-setup.sh --auto
./run --auto
# Naked ./run runs full pipeline (setup→build→up). Tear down containers
# and wipe H2 data so CI's own build/start steps get a clean slate.
docker compose down -v 2>/dev/null || true
sudo rm -rf data/
echo "--- Generated .env (secrets redacted) ---"
sed 's/=.*/=***/' .env

- name: Validate .env password consistency
run: |
PW=$(grep '^DATOMIC_PASSWORD=' .env | cut -d= -f2)
URL_PW=$(grep '^DATOMIC_URL=' .env | sed 's/.*password=//')
if [ "$PW" != "$URL_PW" ]; then
echo "FAIL: DATOMIC_PASSWORD and DATOMIC_URL password mismatch"
exit 1
URL=$(grep '^DATOMIC_URL=' .env | cut -d= -f2-)
if echo "$URL" | grep -q 'password='; then
# Legacy format: password embedded in URL — must match DATOMIC_PASSWORD
URL_PW=$(echo "$URL" | sed 's/.*password=//')
if [ "$PW" != "$URL_PW" ]; then
echo "FAIL: DATOMIC_PASSWORD and DATOMIC_URL password mismatch"
exit 1
fi
echo "OK: Passwords match (embedded in URL)"
else
# New format: password separate — just verify both exist
if [ -z "$PW" ]; then
echo "FAIL: DATOMIC_PASSWORD not set"
exit 1
fi
echo "OK: DATOMIC_PASSWORD set, URL clean (password appended at runtime by config.clj)"
fi
echo "OK: Passwords match"

- name: Test — setup --force preserves existing values
run: |
Expand All @@ -91,7 +106,10 @@ jobs:
ORIG_SIG=$(grep '^SIGNATURE=' .env | cut -d= -f2)

# Re-run with --force --auto (should regenerate)
./docker-setup.sh --auto --force
# Tear down first — naked ./run creates containers + H2 data with old passwords
docker compose down -v 2>/dev/null || true
sudo rm -rf data/
./run --auto --force

# Verify .env was regenerated (new passwords, since --auto generates fresh ones)
NEW_ADMIN=$(grep '^ADMIN_PASSWORD=' .env | cut -d= -f2)
Expand All @@ -102,14 +120,17 @@ jobs:
grep -q '^SIGNATURE=' .env || { echo "FAIL: SIGNATURE missing"; exit 1; }
grep -q '^PORT=' .env || { echo "FAIL: PORT missing"; exit 1; }

# Re-check password consistency after --force
# Re-check password exists after --force
PW=$(grep '^DATOMIC_PASSWORD=' .env | cut -d= -f2)
URL_PW=$(grep '^DATOMIC_URL=' .env | sed 's/.*password=//')
if [ "$PW" != "$URL_PW" ]; then
echo "FAIL: Password mismatch after --force re-run"
if [ -z "$PW" ]; then
echo "FAIL: DATOMIC_PASSWORD missing after --force re-run"
exit 1
fi
echo "OK: --force regenerated .env with consistent passwords"
echo "OK: --force regenerated .env with DATOMIC_PASSWORD set"

# Clean up containers/data from --force pipeline run
docker compose down -v 2>/dev/null || true
sudo rm -rf data/

# ── Container image acquisition ─────────────────────────────
# Java 21: build from source (no pre-built images for new stack)
Expand All @@ -130,6 +151,10 @@ jobs:
echo "=== Building orcpub (app) ==="
docker build --target app -t orcpub-app -f docker/Dockerfile .
echo "=== Build complete ==="
# Set image env vars so compose uses locally-built images
# instead of pulling old Datomic Free images from Docker Hub
echo "ORCPUB_IMAGE=orcpub-app" >> "$GITHUB_ENV"
echo "DATOMIC_IMAGE=orcpub-datomic" >> "$GITHUB_ENV"

- name: Pull container images (Java 8)
id: pull
Expand Down
20 changes: 18 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# Ignore environment file (secrets/config)
# Ignore environment file and Docker secret files (passwords)
.env
.env.secrets.backup
/secrets/
docker-compose.secrets.yaml
docker-compose.swarm.yaml
docker-compose.swarm.yaml.backup.*
.env.portainer
.env.backup.*
transactor.properties.reference
.editorconfig
.gitattributes
/resources/public/css/compiled
/resources/public/js/compiled
/resources/*_backup.pdf
Expand Down Expand Up @@ -68,6 +75,15 @@ cljs-test-runner-out
# Claude Code local data (conversation history, credentials)
.claude-data/

# Agentic/AI tool files — belong in dotfiles or agents/ branch, not code branches
.claude/

# NewRelic (DMV-specific, contains license key)
newrelic*

# Transactor properties (may contain hardcoded passwords)
deploy/transactor.properties

# Ignore all log files in logs/
logs/

Expand Down
Loading
Loading