Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ node_modules
dist
tmp
test-results
stats
docker
docs
art
dump
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,8 @@ stats
# no development docker compose
*-dev-docker-compose.yml

# no volume backups
docker/volumes/backup/*.gz

# no insights docs
docs/insights
3 changes: 0 additions & 3 deletions docker-build.sh

This file was deleted.

4 changes: 0 additions & 4 deletions docker-db-backup.sh

This file was deleted.

7 changes: 0 additions & 7 deletions docker-db-restore.sh

This file was deleted.

4 changes: 0 additions & 4 deletions docker-extract-backup.sh

This file was deleted.

4 changes: 0 additions & 4 deletions docker-run-maint.sh

This file was deleted.

File renamed without changes.
File renamed without changes.
14 changes: 14 additions & 0 deletions docker/build-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd)
PROJECT_DIR=$(readlink -f $SCRIPT_DIR/..)
DOCKER_FILE=$PROJECT_DIR/Dockerfile

# If not exist, falls back to environment
HOSTENV_FILE=$PROJECT_DIR/private/host-env.json

# Change these if building a non-demo image
export AUTHZ_URL=http://localhost:9010
export AUTHZ_CLIENT_ID=deadbeef-cafe-babe-feed-baadc0deface

docker buildx build --tag "jam-build" --secret id=jam-build,src=$HOSTENV_FILE --file $DOCKER_FILE $PROJECT_DIR --build-arg UID=`id -u` --build-arg GID=`id -g` --build-arg AUTHZ_URL --build-arg AUTHZ_CLIENT_ID
8 changes: 8 additions & 0 deletions docker/compose-container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh

SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd)
PROJECT_DIR=$(readlink -f $SCRIPT_DIR/..)
COMPOSE_FILE=$PROJECT_DIR/docker/docker-compose.yml
ENV_FILE=$PROJECT_DIR/docker/.env.dev

docker compose --file $COMPOSE_FILE --env-file $ENV_FILE up -d
7 changes: 7 additions & 0 deletions docker/db-backup-extract.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd)
COMPOSE_FILE=$SCRIPT_DIR/docker-compose.yml

# extract sql dumps to local folder
docker compose --file $COMPOSE_FILE run --rm backup-extract
8 changes: 8 additions & 0 deletions docker/db-backup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh

SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd)
# TODO: update with real environment
ENV_FILE=$SCRIPT_DIR/.env.dev
COMPOSE_FILE=$SCRIPT_DIR/docker-compose.yml

docker compose --file $COMPOSE_FILE --env-file $ENV_FILE run --rm backup
7 changes: 7 additions & 0 deletions docker/db-restore-insert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd)
COMPOSE_FILE=$SCRIPT_DIR/docker-compose.yml

# insert local sql dumps into container volume (for hydration)
docker compose --file $COMPOSE_FILE run --rm restore-insert
12 changes: 12 additions & 0 deletions docker/db-restore.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh

SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd)
# TODO: update with real environment
ENV_FILE=$SCRIPT_DIR/.env.dev
COMPOSE_FILE=$SCRIPT_DIR/docker-compose.yml

# Restore from latest backup
# docker compose --file $COMPOSE_FILE --env-file $ENV_FILE run --rm restore

# Restore from specific backup
docker compose --file $COMPOSE_FILE --env-file $ENV_FILE run --rm -e BACKUP_FILE=db-20251224-021601.tar.gz restore
88 changes: 53 additions & 35 deletions docker-compose.yml → docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ services:
# The app
jam-build:
build:
context: .
context: ../
dockerfile: ./Dockerfile
args:
UID: ${UID}
GID: ${GID}
Expand Down Expand Up @@ -43,7 +44,7 @@ services:

# The cache
cache:
image: redis:8.2.3
image: redis:8.4.0
restart: unless-stopped
ports:
- 6379:6379
Expand Down Expand Up @@ -99,10 +100,10 @@ services:
- 3306:3306
volumes:
- dbdata:/var/lib/mysql
- ./data/database/mariadb-ddl-init.sh:/docker-entrypoint-initdb.d/1.sh
- ./data/database/mariadb-ddl-tables.sql:/docker-entrypoint-initdb.d/2.sql
- ./data/database/mariadb-ddl-procedures.sql:/docker-entrypoint-initdb.d/3.sql
- ./data/database/mariadb-ddl-privileges.sql:/docker-entrypoint-initdb.d/4.sql
- ../data/database/mariadb-ddl-init.sh:/docker-entrypoint-initdb.d/1.sh
- ../data/database/mariadb-ddl-tables.sql:/docker-entrypoint-initdb.d/2.sql
- ../data/database/mariadb-ddl-procedures.sql:/docker-entrypoint-initdb.d/3.sql
- ../data/database/mariadb-ddl-privileges.sql:/docker-entrypoint-initdb.d/4.sql
healthcheck:
test: ["CMD-SHELL", "mariadb-admin -u root --password=$DB_ROOT_PASSWORD ping -h localhost"]
timeout: 20s
Expand All @@ -127,8 +128,8 @@ services:
profiles:
- tools

# Extract backups to local bind mount
extract-backup:
# Extract backups from container volume to local bind mount
backup-extract:
image: alpine:latest
volumes:
- backup:/source
Expand All @@ -138,66 +139,83 @@ services:
profiles:
- tools

# The database hydration command
hydrate:
# The database hydration command: Restore from dump in the backup volume
restore:
image: mariadb:12.1.2
environment:
DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
BACKUP_FILE: ${BACKUP_FILE:-latest} # Specify backup file or 'latest'
BACKUP_FILE: ${BACKUP_FILE}
depends_on:
mariadb:
condition: service_healthy
volumes:
- backup:/backup
networks:
- backend
entrypoint: ["/bin/sh"]
entrypoint: ["/bin/bash"]
command:
- "-c"
- |
set -e
echo "Starting database restore..."


# Update a local variable
BACKUP_FILE_="$$BACKUP_FILE" # can be unset or 'latest'

# Find the backup file
if [ "$BACKUP_FILE" = "latest" ]; then
BACKUP_FILE=$(ls -t /backup/db-*.tar.gz 2>/dev/null | head -n1)
if [ -z "$BACKUP_FILE" ]; then
if [ -z "$$BACKUP_FILE_" ] || [ "$$BACKUP_FILE_" = "latest" ]; then
BACKUP_FILE_=$(ls -t /backup/db-*.tar.gz 2>/dev/null | head -n1)
if [ -z "$$BACKUP_FILE_" ]; then
echo "Error: No backup files found in /backup/"
exit 1
fi
echo "Using latest backup: $BACKUP_FILE"
echo "Using latest backup: $$BACKUP_FILE_"
else
BACKUP_FILE="/backup/$BACKUP_FILE"
if [ ! -f "$BACKUP_FILE" ]; then
echo "Error: Backup file not found: $BACKUP_FILE"
BACKUP_FILE_="/backup/$$BACKUP_FILE_"
if [ ! -f "$$BACKUP_FILE_" ]; then
echo "Error: Backup file not found: $$BACKUP_FILE_"
exit 1
fi
fi

# Extract the backup
echo "Extracting backup..."
tar -xzf "$BACKUP_FILE" -C /backup
SQL_FILE=$(tar -tzf "$BACKUP_FILE" | head -n1)
tar -xzf "$$BACKUP_FILE_" -C /backup
SQL_FILE=$(tar -tzf "$$BACKUP_FILE_" | head -n1)

# Drop all databases except system databases
# 'foreign_key_checks = 0' to ignore cross db fk constraints
echo "Dropping existing databases..."
mariadb -h mariadb -u root --password="$DB_ROOT_PASSWORD" \
-e "SELECT CONCAT('DROP DATABASE IF EXISTS \`', schema_name, '\`;')
FROM information_schema.schemata
WHERE schema_name NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')" \
-sN | mariadb -h mariadb -u root --password="$DB_ROOT_PASSWORD"

{
echo "SET foreign_key_checks = 0;";
mariadb -h mariadb -u root --password="$DB_ROOT_PASSWORD" -sN \
-e "SELECT CONCAT('DROP DATABASE IF EXISTS \`', schema_name, '\`;')
FROM information_schema.schemata
WHERE schema_name NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys');"
} | mariadb -h mariadb -u root --password="$DB_ROOT_PASSWORD"

# Restore from backup
echo "Restoring databases from $SQL_FILE..."
mariadb -h mariadb -u root --password="$DB_ROOT_PASSWORD" < "/backup/$SQL_FILE"
echo "Restoring databases from $$SQL_FILE..."
mariadb -h mariadb -u root --password="$DB_ROOT_PASSWORD" < "/backup/$$SQL_FILE"

# Cleanup extracted SQL file
rm -f "/backup/$SQL_FILE"
rm -f "/backup/$$SQL_FILE"

echo "Database restore complete!"
profiles:
- tools

# Insert dumps from local bind mount to container volume for restore
restore-insert:
image: alpine:latest
volumes:
- backup:/dest
- ./volumes/backup:/source
entrypoint: ["/bin/sh"]
command: ["-c", "cp -av /source/* /dest/ && echo 'Dump files inserted into named backup volume'"]
profiles:
- tools

volumes:
dbdata:
backup:
Expand Down
9 changes: 9 additions & 0 deletions docker/run-maint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh

SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" &> /dev/null && pwd)
ENV_FILE=$SCRIPT_DIR/.env.dev
COMPOSE_FILE=$SCRIPT_DIR/docker-compose.yml

# Bring the app down for maintenance
# Start with a 2 hour maintenance window on a running container
docker compose --file $COMPOSE_FILE --env-file $ENV_FILE run --service-ports --name jam-build-maint jam-build --MAINTENANCE="`date -v+2H -u +'%a, %d %b %Y %H:%M:%S GMT'`"
File renamed without changes.
11 changes: 6 additions & 5 deletions docs/localsetup.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
Author: Alex Grant <alex@localnerve.com> (https://www.localnerve.com)
Date: December 11, 2025
Date: December 23, 2025
Title: Getting Started
---

Expand All @@ -13,8 +13,9 @@ Title: Getting Started
### Installation Steps
1. **Install Docker Desktop**: Download and install Docker Desktop from the official website.
2. **Run Docker Compose**:
- Execute `docker compose --env-file .env.dev up` to build and start the services. Wait for it to complete.
- Optionally, create your own `.env` file if needed.
- Execute `docker compose --file docker/docker-compose.yml --env-file docker/.env.dev up` to build and start the services.
- Or just execute `docker/compose-container.sh` to build and compose the demo.
- Optionally, create your own `.env` file.
- Local ports 3306, 5000, 6379, and 9010 must be free prior to service start.

### Configuration Steps
Expand Down Expand Up @@ -52,9 +53,9 @@ Title: Getting Started
```

#### Authorizer.dev Setup
- **Installation**: Use Docker to run and manage Authorizer.dev. A sample configuration file is provided in [**docker-authorizer-dev.yml**](/docker-authorizer-dev.yml) to run Authorizer.dev standalone using the local MariaDB instance.
- **Installation**: Use Docker to run and manage Authorizer.dev. A sample configuration file is provided in [**authorizer-dev.yml**](/docker/authorizer-dev.yml) to run Authorizer.dev standalone using the local MariaDB instance.

- Use [**docker-authorizer-dev.yml**](/docker-authorizer-dev.yml) to run authorizer.dev locally on port 9010 using the locally installed MariaDB instance.
- Use [**authorizer-dev.yml**](/docker/authorizer-dev.yml) to run authorizer.dev locally on port 9010 using the locally installed MariaDB instance.
- Generate `ADMIN_SECRET` and `CLIENT_ID` using local CLI tools like `uuidgen` and `openssl`.
- Choose any port, but the default settings in `package.json` scripts are easiest to use.

Expand Down
2 changes: 1 addition & 1 deletion docs/stats.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
Author: Alex Grant <alex@localnerve.com> (https://www.localnerve.com)
Date: August 30, 2025
Date: December 15, 2025
Title: Project Statistics
---

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jam-build",
"version": "2.8.3",
"version": "2.8.4",
"description": "An adventurous, scalable, fullstack web application reference project",
"main": "index.js",
"type": "module",
Expand Down