Serve CMS at root and bootstrap Authentik in compose
This commit is contained in:
parent
5004627476
commit
4b4963101a
16
README.md
16
README.md
|
|
@ -19,7 +19,7 @@
|
|||
PORT=8210 npm run admin
|
||||
```
|
||||
|
||||
Админка будет доступна на `http://127.0.0.1:8210/admin/`, а подключенный сайт на `http://127.0.0.1:8210/`.
|
||||
Админка будет доступна на `http://127.0.0.1:8210/`. Подключенный сайт хранится как управляемый проект, но не является корневым UI этого сервиса.
|
||||
|
||||
## Запуск с Authentik
|
||||
|
||||
|
|
@ -35,16 +35,15 @@ PORT=8210 npm run admin
|
|||
|
||||
```bash
|
||||
docker compose --env-file infra/.env -f infra/docker-compose.yml up -d --build
|
||||
infra/scripts/bootstrap-authentik.sh
|
||||
```
|
||||
|
||||
Адреса:
|
||||
|
||||
- CMS: `http://cms.local.nodedc:8210/admin/`
|
||||
- CMS: `http://cms.local.nodedc:8210/`
|
||||
- Authentik login: `http://cms-auth.local.nodedc:8210/`
|
||||
- Authentik admin: `http://cms-auth-admin.local.nodedc:8210/if/admin/`
|
||||
|
||||
Bootstrap создает OIDC-приложение `dc-cms`, группы `dc-cms:*` и стартового администратора из `infra/.env`. После первичного входа временный пароль нужно заменить в Authentik.
|
||||
Compose-сервис `authentik-bootstrap` создает OIDC-приложение `dc-cms`, группы `dc-cms:*` и стартового администратора из `infra/.env`. После первичного входа временный пароль нужно заменить в Authentik; следующие deploy не перетрут пароль, пока `CMS_BOOTSTRAP_FORCE_PASSWORD=false`.
|
||||
|
||||
## Synology
|
||||
|
||||
|
|
@ -63,11 +62,4 @@ DNS-записи должны вести на внешний IP NAS. В Synology
|
|||
|
||||
Оба target-порта ведут в один Caddy контейнер внутри compose-проекта `dc-cms`; Caddy разводит запросы по Host.
|
||||
|
||||
Bootstrap Authentik on Synology:
|
||||
|
||||
```bash
|
||||
cd /volume1/docker/dc-cms/source/infra
|
||||
sudo env CMS_ENV_FILE=.env.synology sh ./scripts/bootstrap-authentik.sh
|
||||
```
|
||||
|
||||
Bootstrap выполняется так же, как в платформе NODE.DC: через `authentik-server ak shell < authentik/bootstrap-cms.py`. Ручной вход в worker, `VENV_PATH` и отдельный `apply_blueprint` не являются штатным сценарием.
|
||||
На Synology bootstrap Authentik выполняется автоматически внутри compose-проекта при `nodedc-deploy apply`. Ручной вход в worker, `VENV_PATH`, отдельный `apply_blueprint` и shell-скрипты не являются штатным сценарием.
|
||||
|
|
|
|||
|
|
@ -30,12 +30,13 @@ AUTHENTIK_BOOTSTRAP_EMAIL=dcctouch@gmail.com
|
|||
AUTHENTIK_BOOTSTRAP_PASSWORD=replace-with-temporary-password
|
||||
AUTHENTIK_BOOTSTRAP_TOKEN=replace-with-random-token
|
||||
|
||||
# CMS bootstrap user. Used by infra/scripts/bootstrap-authentik.sh.
|
||||
# CMS bootstrap user. Applied automatically by the authentik-bootstrap compose service.
|
||||
CMS_BOOTSTRAP_ADMIN_EMAIL=dcctouch@gmail.com
|
||||
CMS_BOOTSTRAP_ADMIN_PASSWORD=replace-with-temporary-password
|
||||
CMS_BOOTSTRAP_ADMIN_NAME=DCTOUCH CMS Admin
|
||||
CMS_BOOTSTRAP_FORCE_PASSWORD=false
|
||||
|
||||
# CMS OIDC application. bootstrap-authentik.sh creates the matching provider.
|
||||
# CMS OIDC application. The authentik-bootstrap compose service creates the matching provider.
|
||||
CMS_AUTH_ENABLED=true
|
||||
CMS_BASE_URL=http://cms.local.nodedc:8210
|
||||
CMS_OIDC_ISSUER=http://cms-auth.local.nodedc:8210/application/o/dc-cms/
|
||||
|
|
|
|||
|
|
@ -31,12 +31,13 @@ AUTHENTIK_BOOTSTRAP_EMAIL=dcctouch@gmail.com
|
|||
AUTHENTIK_BOOTSTRAP_PASSWORD=replace-with-temporary-password
|
||||
AUTHENTIK_BOOTSTRAP_TOKEN=replace-with-random-token
|
||||
|
||||
# CMS bootstrap user. Used by infra/scripts/bootstrap-authentik.sh.
|
||||
# CMS bootstrap user. Applied automatically by the authentik-bootstrap compose service.
|
||||
CMS_BOOTSTRAP_ADMIN_EMAIL=dcctouch@gmail.com
|
||||
CMS_BOOTSTRAP_ADMIN_PASSWORD=replace-with-temporary-password
|
||||
CMS_BOOTSTRAP_ADMIN_NAME=DCTOUCH CMS Admin
|
||||
CMS_BOOTSTRAP_FORCE_PASSWORD=false
|
||||
|
||||
# CMS OIDC application. bootstrap-authentik.sh creates the matching provider.
|
||||
# CMS OIDC application. The authentik-bootstrap compose service creates the matching provider.
|
||||
CMS_AUTH_ENABLED=true
|
||||
CMS_BASE_URL=https://cms.dcserve.ru
|
||||
CMS_OIDC_ISSUER=https://auth.dcserve.ru/application/o/dc-cms/
|
||||
|
|
|
|||
|
|
@ -63,6 +63,13 @@ def optional_env(name, default=""):
|
|||
return environ.get(name, default).strip()
|
||||
|
||||
|
||||
def bool_env(name, default=False):
|
||||
value = environ.get(name, "")
|
||||
if value == "":
|
||||
return default
|
||||
return value.strip().lower() in ("1", "true", "yes", "on")
|
||||
|
||||
|
||||
def ensure_group(name, is_superuser=False):
|
||||
group, _ = Group.objects.get_or_create(name=name)
|
||||
group.is_superuser = is_superuser
|
||||
|
|
@ -88,6 +95,7 @@ def ensure_admin_user(groups):
|
|||
user = User.objects.filter(email__iexact=admin_email).first() or User.objects.filter(
|
||||
username=admin_email
|
||||
).first()
|
||||
created = user is None
|
||||
if user is None:
|
||||
user = User(username=admin_email, email=admin_email, name=admin_email, type="internal")
|
||||
|
||||
|
|
@ -101,7 +109,7 @@ def ensure_admin_user(groups):
|
|||
environ.get("CMS_BOOTSTRAP_ADMIN_PASSWORD", "")
|
||||
or environ.get("AUTHENTIK_BOOTSTRAP_PASSWORD", "")
|
||||
)
|
||||
if admin_password:
|
||||
if admin_password and (created or bool_env("CMS_BOOTSTRAP_FORCE_PASSWORD")):
|
||||
user.set_password(admin_password)
|
||||
user.save()
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ services:
|
|||
- ../projects:/app/projects
|
||||
- ${CMS_NODEDC_SITE_PATH:-../../NODEDC_SITE}:/sites/nodedc
|
||||
depends_on:
|
||||
authentik-server:
|
||||
condition: service_started
|
||||
authentik-bootstrap:
|
||||
condition: service_completed_successfully
|
||||
networks:
|
||||
- cms-edge
|
||||
|
||||
|
|
@ -132,6 +132,47 @@ services:
|
|||
networks:
|
||||
- cms-edge
|
||||
|
||||
authentik-bootstrap:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2026.2.2}
|
||||
command:
|
||||
- ak
|
||||
- shell
|
||||
- -c
|
||||
- exec(open('/bootstrap/bootstrap-cms.py', encoding='utf-8').read())
|
||||
restart: "no"
|
||||
env_file:
|
||||
- ${CMS_SERVICE_ENV_FILE:-.env}
|
||||
environment:
|
||||
AUTHENTIK_POSTGRESQL__HOST: postgresql-authentik
|
||||
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
|
||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:?database password required}
|
||||
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
|
||||
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
|
||||
AUTHENTIK_ERROR_REPORTING__ENABLED: ${AUTHENTIK_ERROR_REPORTING__ENABLED:-false}
|
||||
CMS_AUTH_DOMAIN: ${CMS_AUTH_DOMAIN:-cms-auth.local.nodedc}
|
||||
CMS_BOOTSTRAP_ADMIN_EMAIL: ${CMS_BOOTSTRAP_ADMIN_EMAIL:-}
|
||||
CMS_BOOTSTRAP_ADMIN_PASSWORD: ${CMS_BOOTSTRAP_ADMIN_PASSWORD:-}
|
||||
CMS_BOOTSTRAP_ADMIN_NAME: ${CMS_BOOTSTRAP_ADMIN_NAME:-DCTOUCH CMS Admin}
|
||||
CMS_BOOTSTRAP_FORCE_PASSWORD: ${CMS_BOOTSTRAP_FORCE_PASSWORD:-false}
|
||||
CMS_OIDC_CLIENT_ID: ${CMS_OIDC_CLIENT_ID:?cms oidc client id required}
|
||||
CMS_OIDC_CLIENT_SECRET: ${CMS_OIDC_CLIENT_SECRET:?cms oidc client secret required}
|
||||
CMS_OIDC_REDIRECT_URI: ${CMS_OIDC_REDIRECT_URI:?cms oidc redirect uri required}
|
||||
CMS_OIDC_LOGGED_OUT_REDIRECT_URI: ${CMS_OIDC_LOGGED_OUT_REDIRECT_URI:?cms oidc logged out redirect uri required}
|
||||
CMS_BASE_URL: ${CMS_BASE_URL:?cms base url required}
|
||||
CMS_LOGOUT_URI: ${CMS_LOGOUT_URI:-}
|
||||
depends_on:
|
||||
authentik-server:
|
||||
condition: service_healthy
|
||||
authentik-worker:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- authentik-data:/data
|
||||
- authentik-certs:/certs
|
||||
- ./authentik/custom-templates:/templates:ro
|
||||
- ./authentik/bootstrap-cms.py:/bootstrap/bootstrap-cms.py:ro
|
||||
networks:
|
||||
- cms-edge
|
||||
|
||||
networks:
|
||||
cms-edge:
|
||||
|
||||
|
|
|
|||
|
|
@ -330,9 +330,9 @@ function callbackUri(req) {
|
|||
|
||||
function sanitizeReturnTo(value) {
|
||||
const rawValue = String(value || "").trim();
|
||||
if (!rawValue || rawValue.startsWith("//")) return "/admin/";
|
||||
if (/^https?:\/\//i.test(rawValue)) return "/admin/";
|
||||
return rawValue.startsWith("/") ? rawValue : "/admin/";
|
||||
if (!rawValue || rawValue.startsWith("//")) return "/";
|
||||
if (/^https?:\/\//i.test(rawValue)) return "/";
|
||||
return rawValue.startsWith("/") ? rawValue : "/";
|
||||
}
|
||||
|
||||
function sendRedirect(res, target, status = 302) {
|
||||
|
|
@ -710,7 +710,7 @@ async function handleAuthRoute(req, res, url) {
|
|||
}
|
||||
|
||||
if (req.method === "GET" && url.pathname === "/auth/logged-out") {
|
||||
sendRedirect(res, authLoginUrl(req, "/admin/"));
|
||||
sendRedirect(res, authLoginUrl(req, "/"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2026,11 +2026,12 @@ function parseMultipartUpload(req, bodyBuffer) {
|
|||
|
||||
function safePublicPath(urlPath) {
|
||||
const withoutQuery = decodeURIComponent(urlPath.split("?")[0]);
|
||||
const adminRootFiles = new Set(["/admin.css", "/admin.js", "/nodedc-logo.svg"]);
|
||||
const requestPath =
|
||||
withoutQuery === "/"
|
||||
? "/index.html"
|
||||
: withoutQuery === "/admin" || withoutQuery === "/admin/"
|
||||
withoutQuery === "/" || withoutQuery === "/admin" || withoutQuery === "/admin/"
|
||||
? "/admin/index.html"
|
||||
: adminRootFiles.has(withoutQuery)
|
||||
? `/admin${withoutQuery}`
|
||||
: withoutQuery;
|
||||
const normalized = normalize(requestPath).replace(/^(\.\.[/\\])+/, "");
|
||||
const isAdminAsset = normalized === "/admin/index.html" || normalized.startsWith("/admin/");
|
||||
|
|
@ -2283,7 +2284,6 @@ const server = createServer(async (req, res) => {
|
|||
});
|
||||
|
||||
server.listen(port, host, () => {
|
||||
console.log(`DC CMS admin: http://${host}:${port}/admin/`);
|
||||
console.log(`Project site: http://${host}:${port}/`);
|
||||
console.log(`DC CMS: http://${host}:${port}/`);
|
||||
console.log(`Project root: ${repoRoot}`);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue