feat(auth): optional HTTP Basic Auth for the whole site
nginx includes auth_enabled.conf, (re)written at container start from BASIC_AUTH_USER/PASSWORD (htpasswd via apache2-utils). Both set => the site + /api require a shared login; unset => open (local dev not locked out). Set both on the Coolify web service to protect the deployment.
This commit is contained in:
@@ -6,6 +6,11 @@ COPY . .
|
||||
RUN pnpm build
|
||||
|
||||
FROM nginx:alpine
|
||||
# apache2-utils provides htpasswd; auth_enabled.conf is included by nginx.conf
|
||||
# and (re)written at start by the entrypoint hook — default empty = no auth.
|
||||
RUN apk add --no-cache apache2-utils && touch /etc/nginx/auth_enabled.conf
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY docker-entrypoint.d/40-basic-auth.sh /docker-entrypoint.d/40-basic-auth.sh
|
||||
RUN chmod +x /docker-entrypoint.d/40-basic-auth.sh
|
||||
EXPOSE 80
|
||||
|
||||
26
README.md
26
README.md
@@ -2,9 +2,14 @@
|
||||
|
||||
Deployed at https://photofetch.apoena.dev
|
||||
|
||||
Pick a **Napta project** and get a grid of the people staffed on it — each with
|
||||
their **Slack profile picture**, first name, last name, and role. People are
|
||||
matched between Napta and Slack by **email**.
|
||||
Pick one or more **Napta projects** and get a grid of the people staffed on each —
|
||||
every person shown with their **Slack profile picture**, first name, last name, and
|
||||
role. People are matched between Napta and Slack by **email**. Selected projects are
|
||||
shown as separate groups, and you can **export** everyone's photos as a zip with one
|
||||
folder per project (people without a Slack photo get a generated initials image).
|
||||
|
||||
See [`CONTEXT.md`](./CONTEXT.md) for the vocabulary and [`DESIGN.md`](./DESIGN.md)
|
||||
for the design decisions.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -40,14 +45,15 @@ cd backend && pnpm typecheck
|
||||
|
||||
Set on the Coolify app (or in `backend/.env` locally — see `backend/.env.example`):
|
||||
|
||||
| Var | Purpose |
|
||||
| ----------------- | --------------------------------------------------------- |
|
||||
| `NAPTA_API_TOKEN` | Napta API token (lists projects + their people) |
|
||||
| `NAPTA_BASE_URL` | Napta API base (default `https://app.napta.io/api/v1`) |
|
||||
| `SLACK_BOT_TOKEN` | Slack bot token, scopes `users:read` + `users:read.email` |
|
||||
| Var | Purpose |
|
||||
| ------------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `NAPTA_CLIENT_ID` / `NAPTA_CLIENT_SECRET` | Napta Auth0 M2M credentials (lists projects + staffing). Without **both** → demo fixtures. |
|
||||
| `NAPTA_BASE_URL` | Napta API base (default `https://app.napta.io/api/v1`) |
|
||||
| `SLACK_BOT_TOKEN` | Slack bot token, scopes `users:read` + `users:read.email`. Optional — without it, Members show initials. |
|
||||
| `BASIC_AUTH_USER` / `BASIC_AUTH_PASSWORD` | Set **both** (on the `web` service) to require a shared login for the whole site. Unset → open. |
|
||||
|
||||
Without **both** tokens the API serves demo fixtures and the UI shows a
|
||||
"demo data" banner.
|
||||
Without Napta credentials the API serves demo fixtures and the UI shows a
|
||||
"demo data" banner. `BASIC_AUTH_*` go on the **web** service; the rest on **api**.
|
||||
|
||||
## Deploy
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ services:
|
||||
# would collide with Coolify's own :80 / other apps on the host.
|
||||
expose:
|
||||
- "80"
|
||||
environment:
|
||||
# Set both on the Coolify app to require a shared login for the whole site.
|
||||
- BASIC_AUTH_USER=${BASIC_AUTH_USER:-}
|
||||
- BASIC_AUTH_PASSWORD=${BASIC_AUTH_PASSWORD:-}
|
||||
depends_on:
|
||||
- api
|
||||
|
||||
|
||||
16
docker-entrypoint.d/40-basic-auth.sh
Normal file
16
docker-entrypoint.d/40-basic-auth.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
# Toggle HTTP Basic Auth for the whole site from env at container start.
|
||||
# Runs via nginx:alpine's /docker-entrypoint.d/ hook, before nginx starts.
|
||||
set -e
|
||||
|
||||
if [ -n "$BASIC_AUTH_USER" ] && [ -n "$BASIC_AUTH_PASSWORD" ]; then
|
||||
htpasswd -bc /etc/nginx/.htpasswd "$BASIC_AUTH_USER" "$BASIC_AUTH_PASSWORD" >/dev/null 2>&1
|
||||
cat > /etc/nginx/auth_enabled.conf <<EOF
|
||||
auth_basic "photofetch";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
EOF
|
||||
echo "[entrypoint] basic auth ENABLED (user: $BASIC_AUTH_USER)"
|
||||
else
|
||||
: > /etc/nginx/auth_enabled.conf
|
||||
echo "[entrypoint] basic auth disabled (set BASIC_AUTH_USER + BASIC_AUTH_PASSWORD to enable)"
|
||||
fi
|
||||
@@ -4,6 +4,10 @@ server {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Optional HTTP Basic Auth for the whole site (incl. /api). Written at
|
||||
# container start from BASIC_AUTH_USER/PASSWORD; empty file => no auth.
|
||||
include /etc/nginx/auth_enabled.conf;
|
||||
|
||||
# Proxy API calls to the backend service (docker-compose service name "api").
|
||||
location /api/ {
|
||||
proxy_pass http://api:8000;
|
||||
|
||||
Reference in New Issue
Block a user