## Summary Closes #5. - Composer can **Send now** without a schedule time (Schedule and Save draft still available) - Account names wrap and show the platform id so a bad OAuth label is still readable - Social accounts page can rename the display name; re-auth keeps a custom name instead of overwriting it with a placeholder - Worker polls `dispatch_due` every 15s so scheduled campaigns and social posts fire without a manual command; local `docker compose up` now starts the worker ## Test plan - [ ] Open Social → Compose: confirm **Send now**, **Schedule**, and **Save draft** buttons (no combined Save / publish) - [ ] Send now with a connected account and caption → post queues immediately, no datetime required - [ ] Schedule with a datetime → post is scheduled; Schedule with a blank time → validation error - [ ] Composer account list shows full name plus external id, wrapping if the name is long - [ ] Social accounts → rename a messy label → new name shows in composer without reconnecting - [ ] Re-authorize an account that was renamed → custom name is kept - [ ] Rebuild and start compose (`docker compose up --build`) so the **worker** service is running - [ ] Schedule a social post ~2 minutes ahead → after due time, worker logs `Enqueued N due item(s).` and the post leaves Scheduled (published or failed) - [ ] `docker compose logs -f worker` shows `Polling due scheduled work every 15s.` Reviewed-on: #6
71 lines
1.7 KiB
Bash
Executable File
71 lines
1.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
cd /app/site
|
|
|
|
wait_for_database() {
|
|
if [[ -z "${DATABASE_URL:-}" && -z "${DB_HOST:-}" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
echo "Waiting for database..."
|
|
for _ in $(seq 1 30); do
|
|
if uv run python - <<'PY'
|
|
import os
|
|
import sys
|
|
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "monica_site.settings")
|
|
|
|
import django
|
|
from django.db import connections
|
|
from django.db.utils import OperationalError
|
|
|
|
django.setup()
|
|
|
|
try:
|
|
connections["default"].ensure_connection()
|
|
except OperationalError:
|
|
sys.exit(1)
|
|
PY
|
|
then
|
|
echo "Database is ready."
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
echo "Database did not become ready in time." >&2
|
|
exit 1
|
|
}
|
|
|
|
wait_for_database
|
|
|
|
uv run python manage.py migrate --noinput
|
|
|
|
# Email branding (and anything else that resolves static URLs) needs the
|
|
# ManifestStaticFilesStorage map. Web entrypoint already collectstatics;
|
|
# worker must too or hashed lookups raise ValueError.
|
|
uv run python manage.py collectstatic --noinput
|
|
|
|
interval="${DISPATCH_DUE_INTERVAL_SECONDS:-15}"
|
|
echo "Starting dispatch_due poller (every ${interval}s)."
|
|
uv run python manage.py dispatch_due --loop --interval "${interval}" &
|
|
dispatch_pid=$!
|
|
|
|
cleanup() {
|
|
echo "Stopping dispatch_due poller..."
|
|
kill "${dispatch_pid}" 2>/dev/null || true
|
|
wait "${dispatch_pid}" 2>/dev/null || true
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
# Dev uses ImmediateBackend: enqueue runs inline in dispatch_due. No dj-queue.
|
|
if [[ "${DJANGO_ENV:-}" == "dev" ]]; then
|
|
echo "Dev worker: dispatch_due loop publishes scheduled work."
|
|
wait "${dispatch_pid}"
|
|
exit $?
|
|
fi
|
|
|
|
# dj-queue supervisor (workers + dispatcher + scheduler). Run on ONE host only.
|
|
uv run python manage.py dj_queue
|