Compare commits

...

1 Commits

Author SHA1 Message Date
a4f92f5e30 Prep for monorepo use.
web: Update config.

Flesh out build.

Fix issue surrounding build.

Fix paths.

Update workspaces.

Fix build steps.

Apply linter. Temporarily remove problem rules.

Add ignorefile. Prep for formatting.

Lint website.

Lint web, repo packages.

Refine Prettier usage. Fix imports.

Tidy build.

Move node ignore files.

Remove unused.

Update job. Fix lint step.

Build before compiling.

Use root for paths.

Fix issues surrounding import references, types, package names.

Fix build paths.

Tidy.

Enforce prefix.

Apply prefixes to imports.

Enable linter, compiler, etc.

Fix references. Update names.

Mark optional.

Revise mounts. Fix build order.

Update package.json.

Ignore all docusaurus.

Fix paths, types.

Clean up build steps, names.

Fix paths.

website: Fix nested paragraphs build warning.

web: Enforce module resolution.

Use consistent LTS version.

Track Node version.

Use default resolution.

Test main entrypoint.

Fix Node v20 compatibility.

Add task names.

WIP: Fix styles.
2025-04-17 02:46:10 +02:00
232 changed files with 48213 additions and 54725 deletions

View File

@ -10,6 +10,9 @@ insert_final_newline = true
[*.html]
indent_size = 2
[schemas/*.json]
indent_size = 2
[*.{yaml,yml}]
indent_size = 2

View File

@ -28,9 +28,9 @@ runs:
- name: Setup node
uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
cache-dependency-path: package-lock.json
- name: Setup go
uses: actions/setup-go@v5
with:
@ -44,7 +44,7 @@ runs:
run: |
export PSQL_TAG=${{ inputs.postgresql_version }}
docker compose -f .github/actions/setup/docker-compose.yml up -d
cd web && npm ci
npm ci
- name: Generate config
shell: uv run python {0}
run: |

View File

@ -1,5 +1,5 @@
# Re-usable workflow for a single-architecture build
name: Single-arch Container build
name: "Single-arch Container build"
on:
workflow_call:
@ -42,7 +42,7 @@ jobs:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3.6.0
- uses: docker/setup-buildx-action@v3
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:
@ -64,12 +64,12 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: make empty clients
- name: Make empty clients
if: ${{ inputs.release }}
run: |
mkdir -p ./gen-ts-api
mkdir -p ./gen-go-api
- name: generate ts client
- name: Generate TypeScript API Client
if: ${{ !inputs.release }}
run: make gen-client-ts
- name: Build Docker Image

View File

@ -1,5 +1,5 @@
# Re-usable workflow for a multi-architecture build
name: Multi-arch container build
name: "Multi-arch container build"
on:
workflow_call:
@ -49,7 +49,7 @@ jobs:
shouldPush: ${{ steps.ev.outputs.shouldPush }}
steps:
- uses: actions/checkout@v4
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:
@ -69,7 +69,7 @@ jobs:
tag: ${{ fromJson(needs.get-tags.outputs.tags) }}
steps:
- uses: actions/checkout@v4
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:

View File

@ -1,4 +1,5 @@
name: authentik-api-py-publish
name: "Python API Publish"
on:
push:
branches: [main]
@ -7,6 +8,7 @@ on:
workflow_dispatch:
jobs:
build:
name: "Build and Publish"
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
permissions:
@ -30,7 +32,7 @@ jobs:
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
- name: Generate API Client
- name: Generate Python API Client
run: make gen-client-py
- name: Publish package
working-directory: gen-py-api/

View File

@ -1,4 +1,4 @@
name: authentik-api-ts-publish
name: "TypeScript API Publish"
on:
push:
branches: [main]
@ -7,6 +7,7 @@ on:
workflow_dispatch:
jobs:
build:
name: "Build and Publish"
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
@ -20,9 +21,9 @@ jobs:
token: ${{ steps.generate_token.outputs.token }}
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
registry-url: "https://registry.npmjs.org"
- name: Generate API Client
- name: Generate TypeScript API Client
run: make gen-client-ts
- name: Publish package
working-directory: gen-ts-api/

View File

@ -1,4 +1,4 @@
name: authentik-ci-aws-cfn
name: "authentik CI AWS CloudFormation"
on:
push:
@ -18,6 +18,7 @@ env:
jobs:
check-changes-applied:
name: "Check changes applied"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -36,6 +37,7 @@ jobs:
uv run make aws-cfn
git diff --exit-code
ci-aws-cfn-mark:
name: "CI AWS CloudFormation Mark"
if: always()
needs:
- check-changes-applied

View File

@ -1,5 +1,5 @@
---
name: authentik-ci-main-daily
name: "authentik CI Main Daily"
on:
workflow_dispatch:
@ -9,6 +9,7 @@ on:
jobs:
test-container:
name: "Test Container ${{ matrix.version }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false

View File

@ -1,5 +1,5 @@
---
name: authentik-ci-main
name: "authentik CI Main"
on:
push:
@ -19,6 +19,7 @@ env:
jobs:
lint:
name: "Lint"
strategy:
fail-fast: false
matrix:
@ -33,9 +34,10 @@ jobs:
- uses: actions/checkout@v4
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run job
- name: Run job ${{ matrix.job }}
run: uv run make ci-${{ matrix.job }}
test-migrations:
name: "Test Migrations"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -44,6 +46,7 @@ jobs:
- name: run migrations
run: uv run python -m lifecycle.migrate
test-make-seed:
name: "Test Make Seed"
runs-on: ubuntu-latest
steps:
- id: seed
@ -52,7 +55,7 @@ jobs:
outputs:
seed: ${{ steps.seed.outputs.seed }}
test-migrations-from-stable:
name: test-migrations-from-stable - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
name: "Test Migrations From Stable - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5"
runs-on: ubuntu-latest
timeout-minutes: 20
needs: test-make-seed
@ -67,7 +70,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: checkout stable
- name: Checkout Stable
run: |
# Copy current, latest config to local
# Temporarly comment the .github backup while migrating to uv
@ -84,9 +87,9 @@ jobs:
with:
postgresql_version: ${{ matrix.psql }}
continue-on-error: true
- name: run migrations to stable
- name: Run migrations to stable
run: poetry run python -m lifecycle.migrate
- name: checkout current code
- name: Checkout current code
run: |
set -x
git fetch
@ -97,10 +100,10 @@ jobs:
uses: ./.github/actions/setup
with:
postgresql_version: ${{ matrix.psql }}
- name: migrate to latest
- name: Migrate to latest
run: |
uv run python -m lifecycle.migrate
- name: run tests
- name: Run tests
env:
# Test in the main database that we just migrated from the previous stable version
AUTHENTIK_POSTGRESQL__TEST__NAME: authentik
@ -110,7 +113,7 @@ jobs:
run: |
uv run make ci-test
test-unittest:
name: test-unittest - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
name: "Unit tests - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5"
runs-on: ubuntu-latest
timeout-minutes: 20
needs: test-make-seed
@ -146,6 +149,7 @@ jobs:
file: unittest.xml
token: ${{ secrets.CODECOV_TOKEN }}
test-integration:
name: "Integration tests"
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
@ -154,7 +158,7 @@ jobs:
uses: ./.github/actions/setup
- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.12.0
- name: run integration
- name: Run integration
run: |
uv run coverage run manage.py test tests/integration
uv run coverage xml
@ -170,49 +174,50 @@ jobs:
file: unittest.xml
token: ${{ secrets.CODECOV_TOKEN }}
test-e2e:
name: test-e2e (${{ matrix.job.name }})
name: "Test E2E (${{ matrix.job.name }})"
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
job:
- name: proxy
- name: Proxy Provider
glob: tests/e2e/test_provider_proxy*
- name: oauth
- name: OAuth2 Provider
glob: tests/e2e/test_provider_oauth2* tests/e2e/test_source_oauth*
- name: oauth-oidc
- name: OIDC Provider
glob: tests/e2e/test_provider_oidc*
- name: saml
- name: SAML Provider
glob: tests/e2e/test_provider_saml* tests/e2e/test_source_saml*
- name: ldap
- name: LDAP Provider
glob: tests/e2e/test_provider_ldap* tests/e2e/test_source_ldap*
- name: radius
- name: RADIUS Provider
glob: tests/e2e/test_provider_radius*
- name: scim
- name: SCIM Source
glob: tests/e2e/test_source_scim*
- name: flows
- name: Flows
glob: tests/e2e/test_flows*
steps:
- uses: actions/checkout@v4
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Setup e2e env (chrome, etc)
- name: Setup E2E env (chrome, etc)
run: |
docker compose -f tests/e2e/docker-compose.yml up -d --quiet-pull
- id: cache-web
uses: actions/cache@v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**') }}
- name: prepare web ui
key: ${{ runner.os }}-web-${{ hashFiles('./package-lock.json', 'web/src/**') }}
- name: Prepare Web UI
if: steps.cache-web.outputs.cache-hit != 'true'
working-directory: web
run: |
npm ci
make -C .. gen-client-ts
npm run build
- name: run e2e
make gen-client-ts
npm run build -w @goauthentik/web
npm run typecheck
- name: Run E2E tests
run: |
uv run coverage run manage.py test ${{ matrix.job.glob }}
uv run coverage xml
@ -228,6 +233,7 @@ jobs:
file: unittest.xml
token: ${{ secrets.CODECOV_TOKEN }}
ci-core-mark:
name: "CI Core Mark"
if: always()
needs:
- lint
@ -242,6 +248,7 @@ jobs:
with:
jobs: ${{ toJSON(needs) }}
build:
name: "Build"
permissions:
# Needed to upload container images to ghcr.io
packages: write
@ -255,6 +262,7 @@ jobs:
image_name: ghcr.io/goauthentik/dev-server
release: false
pr-comment:
name: "PR Comment"
needs:
- build
runs-on: ubuntu-latest
@ -267,7 +275,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:

View File

@ -1,5 +1,5 @@
---
name: authentik-ci-outpost
name: "authentik CI Outpost"
on:
push:
@ -14,6 +14,7 @@ on:
jobs:
lint-golint:
name: "Lint Go"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -26,7 +27,7 @@ jobs:
mkdir -p web/dist
mkdir -p website/help
touch web/dist/test website/help/test
- name: Generate API
- name: Generate Go API Client
run: make gen-client-go
- name: golangci-lint
uses: golangci/golangci-lint-action@v7
@ -35,6 +36,7 @@ jobs:
args: --timeout 5000s --verbose
skip-cache: true
test-unittest:
name: "Unit Test Go"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -43,12 +45,13 @@ jobs:
go-version-file: "go.mod"
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Generate API
- name: Generate Go API Client
run: make gen-client-go
- name: Go unittests
run: |
go test -timeout 0 -v -race -coverprofile=coverage.out -covermode=atomic -cover ./...
ci-outpost-mark:
name: "CI Outpost Mark"
if: always()
needs:
- lint-golint
@ -59,6 +62,7 @@ jobs:
with:
jobs: ${{ toJSON(needs) }}
build-container:
name: "Build Container"
timeout-minutes: 120
needs:
- ci-outpost-mark
@ -85,7 +89,7 @@ jobs:
uses: docker/setup-qemu-action@v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:
@ -99,7 +103,7 @@ jobs:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate API
- name: Generate Go API Client
run: make gen-client-go
- name: Build Docker Image
id: push
@ -122,6 +126,7 @@ jobs:
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
build-binary:
name: "Build Binary"
timeout-minutes: 120
needs:
- ci-outpost-mark
@ -140,21 +145,22 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "npm"
cache-dependency-path: package-lock.json
- name: Install Node.js dependencies
run: npm ci
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Generate API
- name: Generate Go API Client
run: make gen-client-go
- name: Build web
working-directory: web/
run: |
npm ci
npm run build-proxy
npm run build-proxy -w @goauthentik/web
- name: Build outpost
run: |
set -x

View File

@ -1,4 +1,4 @@
name: authentik-ci-web
name: CI Web UI
on:
push:
@ -13,54 +13,50 @@ on:
jobs:
lint:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
command:
- lint
- lint:lockfile
- tsc
- prettier-check
project:
- web
include:
- command: tsc
project: web
- command: lit-analyse
project: web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: ${{ matrix.project }}/package.json
cache: "npm"
cache-dependency-path: ${{ matrix.project }}/package-lock.json
- working-directory: ${{ matrix.project }}/
run: |
npm ci
- name: Generate API
run: make gen-client-ts
- name: Lint
working-directory: ${{ matrix.project }}/
run: npm run ${{ matrix.command }}
build:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
cache-dependency-path: package-lock.json
- name: Install Node.js dependencies
run: npm ci
- name: Generate API
- name: Generate TypeScript API
run: make gen-client-ts
- name: Build
run: |
npm run build -w @goauthentik/web
- name: Type check
run: |
npm run typecheck
- name: Lint
run: |
npm run lint -w @goauthentik/web
npm run lint:lockfile -w @goauthentik/web
npm run lit-analyse -w @goauthentik/web
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "npm"
cache-dependency-path: package-lock.json
- name: Install Node.js dependencies
run: npm ci
- name: Generate TypeScript API
run: make gen-client-ts
- name: build
working-directory: web/
run: npm run build
run: |
npm run build -w @goauthentik/web
npm run typecheck
ci-web-mark:
name: CI Web Mark
if: always()
needs:
- build
@ -71,6 +67,7 @@ jobs:
with:
jobs: ${{ toJSON(needs) }}
test:
name: Test
needs:
- ci-web-mark
runs-on: ubuntu-latest
@ -78,13 +75,12 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
cache-dependency-path: package-lock.json
- name: Install Node.js dependencies
run: npm ci
- name: Generate API
- name: Generate TypeScript API
run: make gen-client-ts
- name: test
working-directory: web/
run: npm run test || exit 0
- name: Test Web UI
run: npm run test -w @goauthentik/web || exit 0

View File

@ -1,4 +1,4 @@
name: authentik-ci-website
name: CI Docs Website
on:
push:
@ -13,55 +13,59 @@ on:
jobs:
lint:
name: "Lint"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
command:
- lint:lockfile
- prettier-check
steps:
- uses: actions/checkout@v4
- working-directory: website/
run: npm ci
- name: Lint
working-directory: website/
run: npm run ${{ matrix.command }}
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "npm"
cache-dependency-path: package-lock.json
- name: Install Node.js dependencies
run: |
npm ci
- name: Generate TypeScript API
run: make gen-client-ts
- name: Lint Docs
run: |
npm run lint:prettier:check
npm run lint:lockfile -w @goauthentik/docs
test:
name: "Test Docs"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
run: npm ci
- name: test
working-directory: website/
run: npm test
cache-dependency-path: package-lock.json
- name: Install Node.js dependencies
run: |
npm ci
- name: Generate TypeScript API
run: make gen-client-ts
- name: Test Docs
run: |
npm run test -w @goauthentik/docs
build:
name: "Build Docs"
runs-on: ubuntu-latest
name: ${{ matrix.job }}
strategy:
fail-fast: false
matrix:
job:
- build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: website/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
cache-dependency-path: package-lock.json
- name: Install Node.js dependencies
run: npm ci
- name: build
working-directory: website/
run: npm run ${{ matrix.job }}
- name: Build
run: |
npm run build -w @goauthentik/docs
ci-website-mark:
name: "CI Website Mark"
if: always()
needs:
- lint

View File

@ -10,7 +10,7 @@ on:
jobs:
analyze:
name: Analyze
name: "Analyze"
runs-on: ubuntu-latest
permissions:
actions: read

View File

@ -1,4 +1,4 @@
name: authentik-gen-update-webauthn-mds
name: "authentik CI Update WebAuthn MDS"
on:
workflow_dispatch:
schedule:
@ -11,6 +11,7 @@ env:
jobs:
build:
name: "Update WebAuthn MDS"
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:

View File

@ -1,6 +1,6 @@
---
# See https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
name: Cleanup cache after PR is closed
name: "Post-PR Closed Cache Cleanup"
on:
pull_request:
types:
@ -12,6 +12,7 @@ permissions:
jobs:
cleanup:
name: "Cleanup Cache"
runs-on: ubuntu-latest
steps:
- name: Check out code

View File

@ -1,4 +1,4 @@
name: ghcr-retention
name: "authentik GHCR Retention Policy"
on:
# schedule:
@ -8,7 +8,7 @@ on:
jobs:
clean-ghcr:
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
name: Delete old unused container images
name: "Delete old unused container images"
runs-on: ubuntu-latest
steps:
- id: generate_token

View File

@ -1,5 +1,5 @@
---
name: authentik-compress-images
name: "authentik CI Image Compression"
on:
push:
@ -20,7 +20,7 @@ on:
jobs:
compress:
name: compress
name: "Compress Docker images"
runs-on: ubuntu-latest
# Don't run on forks. Token will not be available. Will run on main and open a PR anyway
if: |

View File

@ -1,4 +1,4 @@
name: authentik-publish-source-docs
name: "authentik Publish Source Docs"
on:
push:
@ -12,6 +12,7 @@ env:
jobs:
publish-source-docs:
name: "Publish"
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
timeout-minutes: 120
@ -19,11 +20,11 @@ jobs:
- uses: actions/checkout@v4
- name: Setup authentik env
uses: ./.github/actions/setup
- name: generate docs
- name: Generate docs
run: |
uv run make migrate
uv run ak build_source_docs
- name: Publish
- name: Deploy to Netlify
uses: netlify/actions/cli@master
with:
args: deploy --dir=source_docs --prod

View File

@ -1,4 +1,4 @@
name: authentik-on-release-next-branch
name: "authentik on Release Next Branch"
on:
schedule:
@ -11,6 +11,7 @@ permissions:
jobs:
update-next:
name: "Update Next Branch"
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
environment: internal-production

View File

@ -1,5 +1,5 @@
---
name: authentik-on-release
name: "Release publish"
on:
release:
@ -7,6 +7,7 @@ on:
jobs:
build-server:
name: "Build server"
uses: ./.github/workflows/_reusable-docker-build.yaml
secrets: inherit
permissions:
@ -21,6 +22,7 @@ jobs:
registry_dockerhub: true
registry_ghcr: true
build-outpost:
name: "Build outpost"
runs-on: ubuntu-latest
permissions:
# Needed to upload container images to ghcr.io
@ -45,14 +47,14 @@ jobs:
uses: docker/setup-qemu-action@v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
with:
image-name: ghcr.io/goauthentik/${{ matrix.type }},beryju/authentik-${{ matrix.type }}
- name: make empty clients
- name: Make empty clients
run: |
mkdir -p ./gen-ts-api
mkdir -p ./gen-go-api
@ -85,6 +87,7 @@ jobs:
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
build-outpost-binary:
name: "Build outpost binary"
timeout-minutes: 120
runs-on: ubuntu-latest
permissions:
@ -106,14 +109,13 @@ jobs:
go-version-file: "go.mod"
- uses: actions/setup-node@v4
with:
node-version-file: web/package.json
node-version-file: package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
cache-dependency-path: package-lock.json
- name: Build web
working-directory: web/
run: |
npm ci
npm run build-proxy
npm run build-proxy -w @goauthentik/web
- name: Build outpost
run: |
set -x
@ -129,6 +131,7 @@ jobs:
asset_name: authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}
tag: ${{ github.ref }}
upload-aws-cfn-template:
name: "Upload AWS CloudFormation template"
permissions:
# Needed for AWS login
id-token: write
@ -150,6 +153,7 @@ jobs:
aws s3 cp --acl=public-read lifecycle/aws/template.yaml s3://authentik-cloudformation-templates/authentik.ecs.${{ github.ref }}.yaml
aws s3 cp --acl=public-read lifecycle/aws/template.yaml s3://authentik-cloudformation-templates/authentik.ecs.latest.yaml
test-release:
name: "Test release"
needs:
- build-server
- build-outpost
@ -166,6 +170,7 @@ jobs:
docker compose start postgresql redis
docker compose run -u root server test-all
sentry-release:
name: "Sentry release"
needs:
- build-server
- build-outpost
@ -173,7 +178,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:

View File

@ -1,5 +1,5 @@
---
name: authentik-on-tag
name: "authentik on Tag Release"
on:
push:
@ -8,7 +8,7 @@ on:
jobs:
build:
name: Create Release from Tag
name: "Create Release from Tag"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -20,7 +20,7 @@ jobs:
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: prepare variables
- name: Prepare variables
uses: ./.github/actions/docker-push-variables
id: ev
env:

View File

@ -1,13 +1,15 @@
name: "authentik-repo-mirror"
name: "authentik Repository Mirror"
on: [push, delete]
jobs:
to_internal:
name: "Mirror to internal repository"
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
name: "Checkout repository"
with:
fetch-depth: 0
- if: ${{ env.MIRROR_KEY != '' }}

View File

@ -1,4 +1,4 @@
name: "authentik-repo-stale"
name: "authentik Repository Stale Issues"
on:
schedule:
@ -11,6 +11,7 @@ permissions:
jobs:
stale:
name: "Stale Issues"
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
runs-on: ubuntu-latest
steps:

View File

@ -1,4 +1,4 @@
name: authentik-semgrep
name: "authentik CI Semgrep"
on:
workflow_dispatch: {}
pull_request: {}
@ -13,7 +13,7 @@ on:
- cron: '12 15 * * *'
jobs:
semgrep:
name: semgrep/ci
name: "semgrep/ci"
runs-on: ubuntu-latest
permissions:
contents: read

View File

@ -1,4 +1,4 @@
name: authentik-translation-advice
name: "authentik Translations Advice"
on:
pull_request:
@ -16,6 +16,7 @@ permissions:
jobs:
post-comment:
name: "Post Comment"
runs-on: ubuntu-latest
steps:
- name: Find Comment

View File

@ -1,5 +1,5 @@
---
name: authentik-translate-extract-compile
name: "authentik Extract & Compile Translations"
on:
schedule:
- cron: "0 0 * * *" # every day at midnight
@ -16,6 +16,7 @@ env:
jobs:
compile:
name: "Compile Translations"
runs-on: ubuntu-latest
steps:
- id: generate_token
@ -32,15 +33,20 @@ jobs:
if: ${{ github.event_name == 'pull_request' }}
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Generate API
- name: Generate TypeScript API
run: make gen-client-ts
- name: run extract
- name: Extract Translations
run: |
uv run make i18n-extract
- name: run compile
- name: Build Docs Site
run: npm run build-bundled -w @goauthentik/docs
- name: Build Web UI
run: npm run build -w @goauthentik/web
- name: Type check
run: npm run typecheck
- name: Compile Messages
run: |
uv run ak compilemessages
make web-check-compile
- name: Create Pull Request
if: ${{ github.event_name != 'pull_request' }}
uses: peter-evans/create-pull-request@v7

View File

@ -1,6 +1,6 @@
# Rename transifex pull requests to have a correct naming
# Also enables auto squash-merge
name: authentik-translation-transifex-rename
name: "authentik Translations Transifex PR Rename"
on:
pull_request:
@ -12,6 +12,7 @@ permissions:
jobs:
rename_pr:
name: "Rename PR"
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'transifex-integration[bot]'}}
steps:

23
.gitignore vendored
View File

@ -217,3 +217,26 @@ source_docs/
### Docker ###
docker-compose.override.yml
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
node_modules/
tsconfig.tsbuildinfo
# Wireit's cache
.wireit
custom-elements.json
### Development ###
.drafts

View File

@ -4,12 +4,16 @@
**/LICENSE
authentik/stages/**/*
authentik/sources/**/*
schemas/**/*
blueprints/**/*
## Build asset directories
coverage
dist
out
.docusaurus
.wireit
website/docs/developer-docs/api/**/*
## Environment
@ -32,14 +36,15 @@ coverage
# Templates
# TODO: Rename affected files to *.template.* or similar.
authentik/**/*.html
*.html
*.mdx
*.md
## Import order matters
poly.ts
src/locale-codes.ts
src/locales/
web/src/poly.ts
web/src/locale-codes.ts
web/src/locales/
# Storybook
storybook-static/

View File

@ -17,6 +17,6 @@
"ms-python.vscode-pylance",
"redhat.vscode-yaml",
"Tobermory.es6-string-html",
"unifiedjs.vscode-mdx",
"unifiedjs.vscode-mdx"
]
}

72
.vscode/settings.json vendored
View File

@ -16,7 +16,7 @@
],
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "index",
"typescript.tsdk": "./web/node_modules/typescript/lib",
"typescript.tsdk": "./node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"yaml.schemas": {
"./blueprints/schema.json": "blueprints/**/*.yaml"
@ -30,7 +30,71 @@
}
],
"go.testFlags": ["-count=1"],
"github-actions.workflows.pinned.workflows": [
".github/workflows/ci-main.yml"
]
"github-actions.workflows.pinned.workflows": [".github/workflows/ci-main.yml"],
"eslint.useFlatConfig": true,
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.mjs": "*.d.mts",
"*.cjs": "*.d.cts",
"package.json": "package-lock.json, yarn.lock, .yarnrc, .yarnrc.yml, .yarn, .nvmrc, .node-version",
"tsconfig.json": "tsconfig.*.json, jsconfig.json",
"Dockerfile": "*.Dockerfile"
},
"search.exclude": {
"**/node_modules": true,
"**/*.code-search": true,
"**/dist": true,
"**/out": true,
"**/package-lock.json": true
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[shellscript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[django-html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.removeUnusedImports": "explicit"
},
// We use Prettier for formatting, but specifying these settings
// will ensure that VS Code's IntelliSense doesn't autocomplete unformatted code.
"javascript.format.semicolons": "insert",
"typescript.format.semicolons": "insert",
"javascript.preferences.quoteStyle": "double",
"typescript.preferences.quoteStyle": "double",
"github.copilot.enable": {
"*": true,
"plaintext": true,
"markdown": true,
"scminput": false,
"csv": false,
"json": true,
"yaml": true
}
}

40
.vscode/tasks.json vendored
View File

@ -4,12 +4,7 @@
{
"label": "authentik/core: make",
"command": "uv",
"args": [
"run",
"make",
"lint-fix",
"lint"
],
"args": ["run", "make", "lint-fix", "lint"],
"presentation": {
"panel": "new"
},
@ -18,11 +13,7 @@
{
"label": "authentik/core: run",
"command": "uv",
"args": [
"run",
"ak",
"server"
],
"args": ["run", "ak", "server"],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -32,17 +23,13 @@
{
"label": "authentik/web: make",
"command": "make",
"args": [
"web"
],
"args": ["web"],
"group": "build"
},
{
"label": "authentik/web: watch",
"command": "make",
"args": [
"web-watch"
],
"args": ["web-watch"],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -52,26 +39,19 @@
{
"label": "authentik: install",
"command": "make",
"args": [
"install",
"-j4"
],
"args": ["install", "-j4"],
"group": "build"
},
{
"label": "authentik/website: make",
"command": "make",
"args": [
"website"
],
"args": ["website"],
"group": "build"
},
{
"label": "authentik/website: watch",
"command": "make",
"args": [
"website-watch"
],
"args": ["website-watch"],
"group": "build",
"presentation": {
"panel": "dedicated",
@ -81,11 +61,7 @@
{
"label": "authentik/api: generate",
"command": "uv",
"args": [
"run",
"make",
"gen"
],
"args": ["run", "make", "gen"],
"group": "build"
}
]

View File

@ -1,48 +1,31 @@
# syntax=docker/dockerfile:1
# Stage 1: Build website
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS website-builder
# Stage 1 Web UI and Documentation build
ENV NODE_ENV=production
WORKDIR /work/website
RUN --mount=type=bind,target=/work/website/package.json,src=./website/package.json \
--mount=type=bind,target=/work/website/package-lock.json,src=./website/package-lock.json \
--mount=type=cache,id=npm-website,sharing=shared,target=/root/.npm \
npm ci --include=dev
COPY ./website /work/website/
COPY ./blueprints /work/blueprints/
COPY ./schema.yml /work/
COPY ./SECURITY.md /work/
RUN npm run build-bundled
# Stage 2: Build webui
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS web-builder
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
ENV NODE_ENV=production
WORKDIR /work/web
WORKDIR /work
RUN --mount=type=bind,target=/work/web/package.json,src=./web/package.json \
--mount=type=bind,target=/work/web/package-lock.json,src=./web/package-lock.json \
--mount=type=bind,target=/work/web/packages/sfe/package.json,src=./web/packages/sfe/package.json \
--mount=type=bind,target=/work/web/scripts,src=./web/scripts \
--mount=type=cache,id=npm-web,sharing=shared,target=/root/.npm \
npm ci --include=dev
COPY ./package.json ./package.json
COPY ./package-lock.json ./package-lock.json
COPY ./packages ./packages
COPY ./web ./web
COPY ./website ./website
COPY ./package.json /work
COPY ./web /work/web/
COPY ./website /work/website/
COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
COPY ./gen-ts-api ./gen-ts-api
COPY ./blueprints ./blueprints
COPY ./schema.yml ./schema.yml
COPY ./SECURITY.md ./SECURITY.md
RUN npm run build
RUN --mount=type=cache,target=/root/.npm npm ci --include=dev
RUN npm run build-bundled -w @goauthentik/docs
RUN npm run build -w @goauthentik/web
# Stage 2: Build go proxy
# Stage 3: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS go-builder
ARG TARGETOS
@ -79,7 +62,8 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
CGO_ENABLED=1 GOFIPS140=latest GOARM="${TARGETVARIANT#v}" \
go build -o /go/authentik ./cmd/server
# Stage 4: MaxMind GeoIP
# Stage 3: MaxMind GeoIP
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.0 AS geoip
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
@ -93,9 +77,10 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
mkdir -p /usr/share/GeoIP && \
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Download uv
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.6.14 AS uv
# Stage 6: Base python image
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.12.10-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \
@ -109,7 +94,7 @@ WORKDIR /ak-root/
COPY --from=uv /uv /uvx /bin/
# Stage 7: Python dependencies
# Stage 6: Python dependencies
FROM python-base AS python-deps
ARG TARGETARCH
@ -144,7 +129,7 @@ RUN --mount=type=bind,target=pyproject.toml,src=pyproject.toml \
--mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev
# Stage 8: Run
# Stage 7: Run
FROM python-base AS final-image
ARG VERSION
@ -189,7 +174,7 @@ COPY --from=go-builder /go/authentik /bin/authentik
COPY --from=python-deps /ak-root/.venv /ak-root/.venv
COPY --from=web-builder /work/web/dist/ /web/dist/
COPY --from=web-builder /work/web/authentik/ /web/authentik/
COPY --from=website-builder /work/website/build/ /website/help/
COPY --from=web-builder /work/website/build/ /website/help/
COPY --from=geoip /usr/share/GeoIP /geoip
USER 1000

107
Makefile
View File

@ -36,6 +36,13 @@ test: ## Run the server tests and produce a coverage report (locally)
uv run coverage html
uv run coverage report
node-check-compile: ## Check and compile the TypeScript source code
npm run typecheck
node-lint-fix: ## Lint and automatically fix errors in the javascript source code
lint-codespell
npm run lint:fix
lint-fix: lint-codespell ## Lint and automatically fix errors in the python source code. Reports spelling errors.
uv run black $(PY_SOURCES)
uv run ruff check --fix $(PY_SOURCES)
@ -47,9 +54,6 @@ lint: ## Lint the python and golang sources
uv run bandit -c pyproject.toml -r $(PY_SOURCES)
golangci-lint run -v
core-install:
uv sync --frozen
migrate: ## Run the Authentik Django server's migrations
uv run python -m lifecycle.migrate
@ -72,7 +76,9 @@ core-i18n-extract:
--ignore website \
-l en
install: web-install website-install core-install ## Install all requires dependencies for `web`, `website` and `core`
install: ## Install all requires dependencies for `web`, `website` and `core`
npm ci
uv sync --frozen
dev-drop-db:
dropdb -U ${pg_user} -h ${pg_host} ${pg_name}
@ -94,6 +100,7 @@ gen-build: ## Extract the schema from the database
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
uv run ak make_blueprint_schema > blueprints/schema.json
AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
@ -101,19 +108,24 @@ gen-build: ## Extract the schema from the database
gen-changelog: ## (Release) generate the changelog based from the commits since the last tag
git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md
npx prettier --write changelog.md
gen-diff: ## (Release) generate the changelog diff between the current schema and the last tag
git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > old_schema.yml
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-diff:2.1.0-beta.8 \
--markdown /local/diff.md \
/local/old_schema.yml /local/schema.yml
rm old_schema.yml
sed -i 's/{/{/g' diff.md
sed -i 's/}/}/g' diff.md
npx prettier --write diff.md
gen-clean-ts: ## Remove generated API client for Typescript
@ -133,46 +145,57 @@ gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescri
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
-i /local/schema.yml \
-g typescript-fetch \
-o /local/${GEN_API_TS} \
-c /local/scripts/api-ts-config.yaml \
--input-spec /local/schema.yml \
--generator-name typescript-fetch \
--output /local/${GEN_API_TS} \
--config /local/scripts/api-ts-config.yaml \
--additional-properties=npmVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
mkdir -p web/node_modules/@goauthentik/api
cd ./${GEN_API_TS} && npm i
\cp -rf ./${GEN_API_TS}/* web/node_modules/@goauthentik/api
npm install
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
-i /local/schema.yml \
-g python \
-o /local/${GEN_API_PY} \
-c /local/scripts/api-py-config.yaml \
--input-spec /local/schema.yml \
--generator-name python \
--output /local/${GEN_API_PY} \
--config /local/scripts/api-py-config.yaml \
--additional-properties=packageVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
pip install ./${GEN_API_PY}
gen-client-go: gen-clean-go ## Build and install the authentik API for Golang
mkdir -p ./${GEN_API_GO} ./${GEN_API_GO}/templates
wget https://raw.githubusercontent.com/goauthentik/client-go/main/config.yaml -O ./${GEN_API_GO}/config.yaml
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/README.mustache -O ./${GEN_API_GO}/templates/README.mustache
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/go.mod.mustache -O ./${GEN_API_GO}/templates/go.mod.mustache
wget https://raw.githubusercontent.com/goauthentik/client-go/main/config.yaml \
-O ./${GEN_API_GO}/config.yaml
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/README.mustache \
-O ./${GEN_API_GO}/templates/README.mustache
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/go.mod.mustache \
-O ./${GEN_API_GO}/templates/go.mod.mustache
cp schema.yml ./${GEN_API_GO}/
docker run \
--rm -v ${PWD}/${GEN_API_GO}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-generator-cli:v6.5.0 generate \
-i /local/schema.yml \
-g go \
-o /local/ \
-c /local/config.yaml
--input-spec /local/schema.yml \
--generator-name go \
--output /local/ \
--config /local/config.yaml
go mod edit -replace goauthentik.io/api/v3=./${GEN_API_GO}
rm -rf ./${GEN_API_GO}/config.yaml ./${GEN_API_GO}/templates/
gen-dev-config: ## Generate a local development config file
@ -184,56 +207,38 @@ gen: gen-build gen-client-ts
## Web
#########################
web-build: web-install ## Build the Authentik UI
cd web && npm run build
web: web-lint-fix web-lint web-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web-install: ## Install the necessary libraries to build the Authentik UI
cd web && npm ci
web: web-lint-fix web-lint node-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web-test: ## Run tests for the Authentik UI
cd web && npm run test
npm run test -w @goauthentik/web
web-watch: ## Build and watch the Authentik UI for changes, updating automatically
rm -rf web/dist/
mkdir web/dist/
touch web/dist/.gitkeep
cd web && npm run watch
npm run watch -w @goauthentik/web
web-storybook-watch: ## Build and run the storybook documentation server
cd web && npm run storybook
npm run storybook -w @goauthentik/web
web-lint-fix:
cd web && npm run prettier
npm run prettier -w @goauthentik/web
web-lint:
cd web && npm run lint
cd web && npm run lit-analyse
web-check-compile:
cd web && npm run tsc
npm run lint -w @goauthentik/web
npm run lit-analyse -w @goauthentik/web
web-i18n-extract:
cd web && npm run extract-locales
npm run extract-locales -w @goauthentik/web
#########################
## Website
#########################
website: website-lint-fix website-build ## Automatically fix formatting issues in the Authentik website/docs source code, lint the code, and compile it
website-install:
cd website && npm ci
website-lint-fix: lint-codespell
cd website && npm run prettier
website: node-lint-fix website-build ## Automatically fix formatting issues in the Authentik website/docs source code, lint the code, and compile it
website-build:
cd website && npm run build
npm run build -w @goauthentik/docs
website-watch: ## Build and watch the documentation website, updating automatically
cd website && npm run watch
npm run watch -w @goauthentik/docs
#########################
## Docker

View File

@ -2,20 +2,22 @@
{% get_current_language as LANGUAGE_CODE %}
<script>
window.authentik = {
locale: "{{ LANGUAGE_CODE }}",
config: JSON.parse('{{ config_json|escapejs }}'),
brand: JSON.parse('{{ brand_json|escapejs }}'),
versionFamily: "{{ version_family }}",
versionSubdomain: "{{ version_subdomain }}",
build: "{{ build }}",
api: {
base: "{{ base_url }}",
relBase: "{{ base_url_rel }}",
},
};
window.authentik = {
locale: "{{ LANGUAGE_CODE }}",
config: JSON.parse("{{ config_json|escapejs }}" || "{}"),
brand: JSON.parse("{{ brand_json|escapejs }}" || "{}"),
versionFamily: "{{ version_family }}",
versionSubdomain: "{{ version_subdomain }}",
build: "{{ build }}",
api: {
base: "{{ base_url }}",
relBase: "{{ base_url_rel }}",
},
};
{% if messages %}
window.addEventListener("DOMContentLoaded", function () {
{% for message in messages %}
{% for message in messages %}
window.dispatchEvent(
new CustomEvent("ak-message", {
bubbles: true,
@ -26,6 +28,7 @@
},
}),
);
{% endfor %}
{% endfor %}
});
{% endif %}
</script>

View File

@ -2,31 +2,79 @@
{% load i18n %}
{% load authentik_core %}
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
{# Darkreader breaks the site regardless of theme as its not compatible with webcomponents, and we default to a dark theme based on preferred colour-scheme #}
<meta name="darkreader-lock">
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}">
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
<style>{{ brand.branding_custom_css }}</style>
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
{% block head %}
{% endblock %}
<meta name="sentry-trace" content="{{ sentry_trace }}" />
</head>
<body>
{% block body %}
{% endblock %}
{% block scripts %}
{% endblock %}
</body>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
{% comment %}
Darkreader breaks the site regardless of theme as its not compatible with webcomponents, and we
default to a dark theme based on preferred colour-scheme
{% endcomment %}
<meta name="darkreader-lock" />
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon_url }}" />
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}" />
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}" />
<style data-test-id="color-scheme">
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark light;
}
}
@media (prefers-color-scheme: light) {
:root {
color-scheme: light dark;
}
}
</style>
<style data-test-id="custom-branding-css">
{{ brand.branding_custom_css }}
</style>
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
<script
src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}"
type="module"
></script>
{% block head %}
{% endblock %}
<meta name="sentry-trace" content="{{ sentry_trace }}" />
</head>
<body>
{% block body %}{% endblock %}
{% block scripts %}{% endblock %}
<noscript>
<style>
body {
font-family: var(--ak-font-family-base), sans-serif;
}
</style>
<h1>
JavaScript is required to use
{% trans title|default:brand.branding_title %}
</h1>
<p>
Please enable JavaScript in your browser settings and reload the page. If you are using a
browser extension that blocks JavaScript, please disable it for this site.
</p>
</noscript>
</body>
</html>

View File

@ -4,14 +4,16 @@
{% block head %}
<script src="{% versioned_script 'dist/admin/AdminInterface-%v.js' %}" type="module"></script>
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
{% include "base/header_js.html" %}
{% endblock %}
{% block body %}
<ak-message-container></ak-message-container>
<ak-interface-admin>
<ak-loading></ak-loading>
<ak-loading></ak-loading>
</ak-interface-admin>
{% endblock %}

View File

@ -13,9 +13,14 @@
{% block card %}
<form method="POST" class="pf-c-form">
<p>{% trans message %}</p>
<a id="ak-back-home" href="{% url 'authentik_core:root-redirect' %}" class="pf-c-button pf-m-primary">
{% trans 'Go home' %}
</a>
<p>{% trans message %}</p>
<a
id="ak-back-home"
href="{% url 'authentik_core:root-redirect' %}"
class="pf-c-button pf-m-primary"
>
{% trans 'Go home' %}
</a>
</form>
{% endblock %}

View File

@ -4,14 +4,17 @@
{% block head %}
<script src="{% versioned_script 'dist/user/UserInterface-%v.js' %}" type="module"></script>
<meta name="theme-color" content="#1c1e21" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#1c1e21" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#1c1e21" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#1c1e21" media="(prefers-color-scheme: dark)" />
{% include "base/header_js.html" %}
{% endblock %}
{% block body %}
<ak-message-container></ak-message-container>
<ak-interface-user>
<ak-loading></ak-loading>
<ak-loading></ak-loading>
</ak-interface-user>
{% endblock %}

View File

@ -5,78 +5,82 @@
{% block head_before %}
<link rel="prefetch" href="{{ request.brand.branding_default_flow_background_url }}" />
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}" />
<link
rel="stylesheet"
type="text/css"
href="{% static 'dist/theme-dark.css' %}"
media="(prefers-color-scheme: dark)"
/>
{% include "base/header_js.html" %}
{% endblock %}
{% block head %}
<style>
:root {
<style data-test-id="base-full-root-styles">
:root {
--ak-flow-background: url("{{ request.brand.branding_default_flow_background_url }}");
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--lg: var(--ak-flow-background);
}
/* Form with user */
.form-control-static {
}
/* Form with user */
.form-control-static {
margin-top: var(--pf-global--spacer--sm);
display: flex;
align-items: center;
justify-content: space-between;
}
.form-control-static .avatar {
}
.form-control-static .avatar {
display: flex;
align-items: center;
}
.form-control-static img {
}
.form-control-static img {
margin-right: var(--pf-global--spacer--xs);
}
.form-control-static a {
}
.form-control-static a {
padding-top: var(--pf-global--spacer--xs);
padding-bottom: var(--pf-global--spacer--xs);
line-height: var(--pf-global--spacer--xl);
}
}
</style>
{% endblock %}
{% block body %}
<div class="pf-c-background-image">
</div>
<div class="pf-c-background-image"></div>
<ak-message-container></ak-message-container>
<div class="pf-c-login stacked">
<div class="ak-login-container">
<main class="pf-c-login__main">
<div class="pf-c-login__main-header pf-c-brand ak-brand">
<img src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
{% block card_title %}
{% endblock %}
</h1>
</header>
<div class="pf-c-login__main-body">
{% block card %}
{% endblock %}
</div>
</main>
<footer class="pf-c-login__footer">
<ul class="pf-c-list pf-m-inline">
{% for link in footer_links %}
<li>
<a href="{{ link.href }}">{{ link.name }}</a>
</li>
{% endfor %}
<li>
<span>
{% trans 'Powered by authentik' %}
</span>
</li>
</ul>
</footer>
</div>
<div class="ak-login-container">
<main class="pf-c-login__main">
<div class="pf-c-login__main-header pf-c-brand ak-brand">
<img src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
{% block card_title %}
{% endblock %}
</h1>
</header>
<div class="pf-c-login__main-body">
{% block card %}
{% endblock %}
</div>
</main>
<footer class="pf-c-login__footer">
<ul class="pf-c-list pf-m-inline">
{% for link in footer_links %}
<li>
<a href="{{ link.href }}">{{ link.name }}</a>
</li>
{% endfor %}
<li>
<span>
{% trans 'Powered by authentik' %}
</span>
</li>
</ul>
</footer>
</div>
</div>
{% endblock %}

View File

@ -2,53 +2,52 @@
{% load i18n %}
{% load authentik_core %}
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}">
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/sfe/bootstrap.min.css' %}">
<meta name="sentry-trace" content="{{ sentry_trace }}" />
{% include "base/header_js.html" %}
<style>
html,
body {
height: 100%;
}
body {
background-image: url("{{ flow.background_url }}");
background-repeat: no-repeat;
background-size: cover;
}
.card {
padding: 3rem;
}
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon_url }}" />
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}" />
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/sfe/bootstrap.min.css' %}" />
<meta name="sentry-trace" content="{{ sentry_trace }}" />
{% include "base/header_js.html" %}
<style>
html,
body {
height: 100%;
}
body {
background-image: url("{{ flow.background_url }}");
background-repeat: no-repeat;
background-size: cover;
}
.card {
padding: 3rem;
}
.form-signin {
max-width: 330px;
padding: 1rem;
}
.form-signin {
max-width: 330px;
padding: 1rem;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.brand-icon {
max-width: 100%;
}
</style>
</head>
<body class="d-flex align-items-center py-4 bg-body-tertiary">
<div class="card m-auto">
<main class="form-signin w-100 m-auto" id="flow-sfe-container">
</main>
<span class="mt-3 mb-0 text-muted text-center">{% trans 'Powered by authentik' %}</span>
</div>
<script src="{% static 'dist/sfe/index.js' %}"></script>
</body>
.form-signin .form-floating:focus-within {
z-index: 2;
}
.brand-icon {
max-width: 100%;
}
</style>
</head>
<body class="d-flex align-items-center py-4 bg-body-tertiary">
<div class="card m-auto">
<main class="form-signin w-100 m-auto" id="flow-sfe-container"></main>
<span class="mt-3 mb-0 text-muted text-center">{% trans 'Powered by authentik' %}</span>
</div>
<script src="{% static 'dist/sfe/index.js' %}"></script>
</body>
</html>

View File

@ -1,34 +1,40 @@
{% extends "base/skeleton.html" %}
{% load static %}
{% load authentik_core %}
{% block head_before %}
{{ block.super }}
<link rel="prefetch" href="{{ flow.background_url }}" />
{% if flow.compatibility_mode and not inspector %}
<script>ShadyDOM = { force: !navigator.webdriver };</script>
{% endif %}
{% include "base/header_js.html" %}
<script>
window.authentik.flow = {
"layout": "{{ flow.layout }}",
};
ShadyDOM = { force: !navigator.webdriver };
</script>
{% endif %}
{% include "base/header_js.html" %}
<script>
window.authentik.flow = {
layout: "{{ flow.layout }}",
};
</script>
{% endblock %}
{% block head %}
<script src="{% versioned_script 'dist/flow/FlowInterface-%v.js' %}" type="module"></script>
<style>
:root {
<style data-test-id="flow-root-styles">
:root {
--ak-flow-background: url("{{ flow.background_url }}");
}
}
</style>
{% endblock %}
{% block body %}
<ak-message-container></ak-message-container>
<ak-flow-executor flowSlug="{{ flow.slug }}">
<ak-loading></ak-loading>
<ak-loading></ak-loading>
</ak-flow-executor>
{% endblock %}

View File

@ -4,10 +4,13 @@
{% block head %}
<script src="{% versioned_script 'dist/rac/index-%v.js' %}" type="module"></script>
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<link rel="icon" href="{{ tenant.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon_url }}">
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
<link rel="icon" href="{{ tenant.branding_favicon_url }}" />
<link rel="shortcut icon" href="{{ tenant.branding_favicon_url }}" />
{% include "base/header_js.html" %}
{% endblock %}

10
eslint.config.mjs Normal file
View File

@ -0,0 +1,10 @@
import { createESLintPackageConfig } from "@goauthentik/eslint-config";
// @ts-check
/**
* ESLint configuration for authentik's monorepo.
*/
const ESLintConfig = createESLintPackageConfig();
export default ESLintConfig;

View File

@ -1,15 +1,34 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<title>{{.Title}}</title>
<link rel="shortcut icon" type="image/png" href="/outpost.goauthentik.io/static/dist/assets/icons/icon.png">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/patternfly.min.css">
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/authentik.css">
<link rel="prefetch" href="/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg" />
<style>
<link
rel="shortcut icon"
type="image/png"
href="/outpost.goauthentik.io/static/dist/assets/icons/icon.png"
/>
<link
rel="stylesheet"
type="text/css"
href="/outpost.goauthentik.io/static/dist/patternfly.min.css"
/>
<link
rel="stylesheet"
type="text/css"
href="/outpost.goauthentik.io/static/dist/authentik.css"
/>
<link
rel="prefetch"
href="/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg"
/>
<style data-test-id="outpost-error-root-styles">
.pf-c-background-image::before {
--ak-flow-background: url("/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg");
}
@ -24,13 +43,15 @@
</style>
</head>
<body>
<div class="pf-c-background-image">
</div>
<div class="pf-c-background-image"></div>
<div class="pf-c-login stacked">
<div class="ak-login-container">
<main class="pf-c-login__main">
<div class="pf-c-login__main-header pf-c-brand ak-brand">
<img src="/outpost.goauthentik.io/static/dist/assets/icons/icon_left_brand.svg" alt="authentik Logo" />
<img
src="/outpost.goauthentik.io/static/dist/assets/icons/icon_left_brand.svg"
alt="authentik Logo"
/>
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
@ -47,9 +68,7 @@
<footer class="pf-c-login__footer">
<ul class="pf-c-list pf-m-inline">
<li>
<span>
Powered by authentik
</span>
<span> Powered by authentik </span>
</li>
</ul>
</footer>

View File

@ -1,16 +1,16 @@
{
"name": "@goauthentik/lifecycle-aws",
"version": "0.0.0",
"private": true,
"license": "MIT",
"private": true,
"scripts": {
"aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml"
},
"engines": {
"node": ">=20"
},
"devDependencies": {
"aws-cdk": "^2.1007.0",
"cross-env": "^7.0.3"
},
"engines": {
"node": ">=20.11"
}
}

44519
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,50 @@
{
"name": "@goauthentik/authentik",
"name": "@goauthentik/universe",
"version": "2025.2.4",
"private": true
"description": "Monorepo for authentik.",
"private": true,
"scripts": {
"lint": "run-s lint:prettier:check lint:eslint:check",
"lint:eslint:check": "eslint .",
"lint:eslint:fix": "eslint --fix .",
"lint:fix": "run-s lint:prettier:fix lint:eslint:fix",
"lint:prettier:check": "prettier --cache --check -u .",
"lint:prettier:fix": "prettier --cache --write -u .",
"typecheck": "NODE_OPTIONS=\"--max-old-space-size=3000\" tsc -b"
},
"dependencies": {
"@eslint/js": "^9.11.1",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"eslint": "^9.23.0",
"eslint-plugin-lit": "^2.0.0",
"eslint-plugin-wc": "^3.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.5.3",
"prettier-plugin-django-alpine": "^1.3.0",
"prettier-plugin-packagejson": "^2.5.10",
"typescript": "^5.8.2",
"typescript-eslint": "^8.29.0",
"zx": "^8.4.1"
},
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.24.0",
"@esbuild/linux-amd64": "^0.18.11",
"@esbuild/linux-arm64": "^0.24.0",
"@rollup/rollup-darwin-arm64": "4.23.0",
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
"@rollup/rollup-linux-x64-gnu": "4.23.0"
},
"engines": {
"node": ">=20.11"
},
"workspaces": [
"gen-ts-api",
"web",
"web/packages/*",
"website",
"packages/*"
],
"prettier": "@goauthentik/prettier-config"
}

View File

@ -18,9 +18,7 @@
}
.badge--support-community {
--ifm-badge-background-color: var(
--ifm-color-secondary-contrast-foreground
);
--ifm-badge-background-color: var(--ifm-color-secondary-contrast-foreground);
--ifm-badge-border-color: var(--ifm-color-secondary-dark);
--ifm-badge-color: var(--ifm-color-secondary-contrast-background);
}

View File

@ -1,12 +1,12 @@
:root {
--ifm-font-family-base:
RedHatVF, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell,
Noto Sans, sans-serif, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
RedHatVF, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans,
sans-serif, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--ifm-font-family-monospace:
RedHatMonoVF, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
RedHatMonoVF, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
--ifm-heading-font-family: RedHatDisplayVF, var(--ifm-font-family-base);

View File

@ -7,11 +7,7 @@
}
.homepage_hero__subtitle p {
font-size: clamp(
1.125rem,
0.9946rem + 0.6522vi,
1.5rem
); /* Adjust font as page scales */
font-size: clamp(1.125rem, 0.9946rem + 0.6522vi, 1.5rem); /* Adjust font as page scales */
max-width: 28ch; /* Apply a maximum to keep everything in the box */
text-wrap: balance; /* Prevent widows, orphans, and runts. Doesn't work in Safari */
}

View File

@ -122,12 +122,8 @@
@media (min-width: 999px) {
border-inline-start: 1px solid var(--ifm-hover-overlay);
margin-inline-start: calc(
var(--ifm-navbar-item-padding-horizontal) / 2
);
padding-inline-start: calc(
var(--ifm-navbar-item-padding-horizontal) / 2
);
margin-inline-start: calc(var(--ifm-navbar-item-padding-horizontal) / 2);
padding-inline-start: calc(var(--ifm-navbar-item-padding-horizontal) / 2);
}
}
@ -151,19 +147,14 @@
hsl(236.84deg 34.55% 10.78%)
);
--docsearch-key-shadow:
inset 0 -2px 0 0 hsl(233.33deg 36% 24.51%),
inset 0 0 1px 1px hsl(232.11deg 34.86% 57.25%),
inset 0 -2px 0 0 hsl(233.33deg 36% 24.51%), inset 0 0 1px 1px hsl(232.11deg 34.86% 57.25%),
0 2px 2px 0 rgba(3, 4, 9, 0.3);
--docsearch-key-pressed-shadow:
inset 0 -2px 0 0 #282d55,
inset 0 0 1px 1px hsl(231.82deg 21.36% 40.39%),
inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px hsl(231.82deg 21.36% 40.39%),
0 1px 1px 0 hsl(230deg 50% 2.35% / 30.2%);
padding: var(--ifm-navbar-item-padding-vertical)
var(--ifm-navbar-item-padding-horizontal) !important;
padding-inline-end: calc(
var(--ifm-navbar-item-padding-horizontal) * 1.25
) !important;
padding: var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal) !important;
padding-inline-end: calc(var(--ifm-navbar-item-padding-horizontal) * 1.25) !important;
.DocSearch-Button-Placeholder {
font-family: var(--ifm-heading-font-family);

View File

@ -4,8 +4,8 @@
* @import { Config as DocusaurusConfig } from "@docusaurus/types"
* @import { UserThemeConfig } from "./theme.js"
*/
import { deepmerge } from "deepmerge-ts";
import { createThemeConfig } from "./theme.js";
//#region Types

View File

@ -4,7 +4,6 @@
* @import { UserThemeConfig as UserThemeConfigCommon } from "@docusaurus/theme-common";
* @import { UserThemeConfig as UserThemeConfigAlgolia } from "@docusaurus/theme-search-algolia";
*/
import { deepmerge } from "deepmerge-ts";
import { themes as prismThemes } from "prism-react-renderer";

View File

@ -1,13 +1,16 @@
/**
* @file
* Client-side observer for ESBuild events.
* @file Client-side observer for ESBuild events.
*
* @import { Message as ESBuildMessage } from "esbuild";
*/
import type { Message as ESBuildMessage } from "esbuild";
const logPrefix = "👷 [ESBuild]";
const log = console.debug.bind(console, logPrefix);
type BuildEventListener<Data = unknown> = (event: MessageEvent<Data>) => void;
/**
* @template {unknown} [Data=unknown]
* @typedef {(event: MessageEvent) => void} BuildEventListener
*/
/**
* A client-side watcher for ESBuild.
@ -17,13 +20,11 @@ type BuildEventListener<Data = unknown> = (event: MessageEvent<Data>) => void;
*
* ```ts
* if (process.env.NODE_ENV === "development" && process.env.WATCHER_URL) {
* const { ESBuildObserver } = await import("@goauthentik/common/client");
* const { ESBuildObserver } = await import("@goauthentik/esbuild-plugin-live-reload/client");
*
* new ESBuildObserver(process.env.WATCHER_URL);
* ESBuildObserver.initialize(process.env.WATCHER_URL);
* }
* ```
}
*/
export class ESBuildObserver extends EventSource {
/**
@ -58,15 +59,19 @@ export class ESBuildObserver extends EventSource {
/**
* The interval for the keep-alive check.
* @type {ReturnType<typeof setInterval> | undefined}
*/
#keepAliveInterval: ReturnType<typeof setInterval> | undefined;
#keepAliveInterval;
#trackActivity = () => {
this.lastUpdatedAt = Date.now();
this.alive = true;
};
#startListener: BuildEventListener = () => {
/**
* @type {BuildEventListener}
*/
#startListener = () => {
this.#trackActivity();
log("⏰ Build started...");
};
@ -82,13 +87,18 @@ export class ESBuildObserver extends EventSource {
}
};
#errorListener: BuildEventListener<string> = (event) => {
/**
* @type {BuildEventListener<string>}
*/
#errorListener = (event) => {
this.#trackActivity();
// eslint-disable-next-line no-console
console.group(logPrefix, "⛔️⛔️⛔️ Build error...");
const esbuildErrorMessages: ESBuildMessage[] = JSON.parse(event.data);
/**
* @type {ESBuildMessage[]}
*/
const esbuildErrorMessages = JSON.parse(event.data);
for (const error of esbuildErrorMessages) {
console.warn(error.text);
@ -101,11 +111,13 @@ export class ESBuildObserver extends EventSource {
}
}
// eslint-disable-next-line no-console
console.groupEnd();
};
#endListener: BuildEventListener = () => {
/**
* @type {BuildEventListener}
*/
#endListener = () => {
cancelAnimationFrame(this.#reloadFrameID);
this.#trackActivity();
@ -126,12 +138,32 @@ export class ESBuildObserver extends EventSource {
});
};
#keepAliveListener: BuildEventListener = () => {
/**
* @type {BuildEventListener}
*/
#keepAliveListener = () => {
this.#trackActivity();
log("🏓 Keep-alive");
};
constructor(url: string | URL) {
/**
* Initialize the ESBuild observer.
* This should be called once in your application.
*
* @param {string | URL} url
* @returns {ESBuildObserver}
*/
static initialize(url) {
const esbuildObserver = new ESBuildObserver(url);
return esbuildObserver;
}
/**
*
* @param {string | URL} url
*/
constructor(url) {
super(url);
this.addEventListener("esbuild:start", this.#startListener);

View File

@ -0,0 +1,2 @@
export * from "./client/index.js";
export * from "./plugin/index.js";

View File

@ -0,0 +1,46 @@
{
"name": "@goauthentik/esbuild-plugin-live-reload",
"version": "1.0.4",
"description": "ESBuild plugin to watch for file changes and trigger client-side reloads.",
"license": "MIT",
"private": true,
"main": "index.js",
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"import": "./index.js",
"types": "./out/index.d.ts"
},
"./client": {
"import": "./client/index.js",
"types": "./out/client/index.d.ts"
},
"./plugin": {
"import": "./plugin/index.js",
"types": "./out/plugin/index.d.ts"
}
},
"devDependencies": {
"@types/node": "^22.14.1",
"esbuild": "^0.25.0",
"typescript": "^5.6.2"
},
"peerDependencies": {
"esbuild": "^0.25.0"
},
"engines": {
"node": ">=20.11"
},
"types": "./out/index.d.ts",
"files": [
"./index.js",
"client/**/*",
"plugin/**/*",
"out/**/*"
],
"prettier": "@goauthentik/prettier-config",
"publishConfig": {
"access": "public"
}
}

View File

@ -1,5 +1,8 @@
import * as http from "http";
import path from "path";
/**
* @file Live reload plugin for ESBuild.
*/
import * as http from "node:http";
import * as path from "node:path";
/**
* Serializes a custom event to a text stream.
@ -22,7 +25,7 @@ export function serializeCustomEventToStream(event) {
* @typedef {Object} BuildObserverOptions
*
* @property {URL} serverURL
* @property {string} logPrefix
* @property {string} [logPrefix]
* @property {string} relativeRoot
*/
@ -32,8 +35,11 @@ export function serializeCustomEventToStream(event) {
* @param {BuildObserverOptions} options
* @returns {import('esbuild').Plugin}
*/
export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) {
const timerLabel = `[${logPrefix}] Build`;
export function liveReloadPlugin({ serverURL, logPrefix = "Build Observer", relativeRoot }) {
const timerLabel = `[${logPrefix}] 🏁`;
// eslint-disable-next-line no-console
const log = console.log.bind(console, `[${logPrefix}]`);
const endpoint = serverURL.pathname;
const dispatcher = new EventTarget();
@ -43,18 +49,18 @@ export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) {
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
if (req.url !== endpoint) {
console.log(`🚫 Invalid request to ${req.url}`);
log(`🚫 Invalid request to ${req.url}`);
res.writeHead(404);
res.end();
return;
}
console.log("🔌 Client connected");
log("🔌 Client connected");
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
Connection: "keep-alive",
});
/**
@ -71,7 +77,7 @@ export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) {
dispatcher.addEventListener("esbuild:end", listener);
req.on("close", () => {
console.log("🔌 Client disconnected");
log("🔌 Client disconnected");
clearInterval(keepAliveInterval);

View File

@ -0,0 +1,10 @@
{
"extends": "@goauthentik/tsconfig",
"compilerOptions": {
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"resolveJsonModule": true,
"baseUrl": ".",
"checkJs": true,
"emitDeclarationOnly": true
}
}

View File

@ -23,8 +23,9 @@ export const DefaultIgnorePatterns = [
"**/out",
"**/dist",
"**/.wireit",
"**/.venv",
"website/build/**",
"website/.docusaurus/**",
"**/.docusaurus/**",
"**/node_modules",
"**/coverage",
"**/storybook-static",

View File

@ -1,6 +1,8 @@
// @ts-check
import tseslint from "typescript-eslint";
import NodeLintPlugin from "../plugins/node-lint.js";
const MAX_DEPTH = 4;
const MAX_NESTED_CALLBACKS = 4;
const MAX_PARAMS = 5;
@ -9,22 +11,29 @@ const MAX_PARAMS = 5;
* ESLint configuration for JavaScript authentik projects.
*/
export const javaScriptConfig = tseslint.config({
plugins: {
"node-lint": NodeLintPlugin,
},
files: ["**/*.{js,jsx,mjs,cjs}"],
rules: {
"node-lint/no-unprefixed-imports": "warn",
// TODO: Clean up before enabling.
"accessor-pairs": "off",
"array-callback-return": "error",
"block-scoped-var": "error",
"consistent-return": ["error", { treatUndefinedAsUnspecified: false }],
"consistent-return": "off",
// "consistent-return": ["error", { treatUndefinedAsUnspecified: false }],
"consistent-this": ["error", "that"],
"curly": "off",
"dot-notation": [
"error",
{
allowKeywords: true,
},
],
"eqeqeq": "error",
"func-names": ["error", "as-needed"],
curly: "off",
// "dot-notation": [
// "error",
// {
// allowKeywords: true,
// },
// ],
// "eqeqeq": "error",
eqeqeq: "off",
// "func-names": ["error", "as-needed"],
"guard-for-in": "error",
"max-depth": ["error", MAX_DEPTH],
"max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS],
@ -33,13 +42,13 @@ export const javaScriptConfig = tseslint.config({
// "new-cap": "error",
"no-alert": "error",
"no-array-constructor": "error",
"no-bitwise": [
"error",
{
allow: ["~"],
int32Hint: true,
},
],
// "no-bitwise": [
// "error",
// {
// allow: ["~"],
// int32Hint: true,
// },
// ],
"no-caller": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
@ -53,10 +62,10 @@ export const javaScriptConfig = tseslint.config({
"no-dupe-args": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-else-return": "error",
// "no-else-return": "error",
"no-empty": "error",
"no-empty-character-class": "error",
"no-empty-function": ["error", { allow: ["constructors"] }],
// "no-empty-function": ["error", { allow: ["constructors"] }],
"no-labels": "error",
"no-eq-null": "error",
"no-eval": "error",
@ -68,7 +77,7 @@ export const javaScriptConfig = tseslint.config({
"no-fallthrough": "error",
"no-func-assign": "error",
"no-implied-eval": "error",
"no-implicit-coercion": "error",
"no-implicit-coercion": ["error", { boolean: true, allow: ["!!"] }],
"no-implicit-globals": "error",
"no-inner-declarations": ["error", "functions"],
"no-invalid-regexp": "error",
@ -76,18 +85,18 @@ export const javaScriptConfig = tseslint.config({
"no-iterator": "error",
"no-label-var": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
// "no-lonely-if": "error",
"no-loop-func": "error",
"no-multi-str": "error",
// TODO: Clean up before enabling.
"no-negated-condition": "off",
"no-new": "error",
"no-new": "off",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-octal-escape": "error",
"no-param-reassign": ["error", { props: false }],
// "no-param-reassign": ["error", { props: false }],
"no-proto": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
@ -99,39 +108,58 @@ export const javaScriptConfig = tseslint.config({
// TODO: Clean up before enabling.
// "no-shadow": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
// TODO: Clean up before enabling.
// "no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-throw-literal": "error",
// "no-throw-literal": "error",
"no-trailing-spaces": "off", // Handled by Prettier.
"no-undef": "off",
"no-undef-init": "off",
"no-unexpected-multiline": "error",
"no-useless-constructor": "error",
// "no-useless-constructor": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
"no-unreachable": "error",
"no-unused-expressions": "error",
"no-unused-labels": "error",
"no-use-before-define": "error",
// "no-use-before-define": "error",
"no-useless-call": "error",
"no-dupe-class-members": "error",
"no-var": "error",
"no-void": "error",
"no-with": "error",
"prefer-arrow-callback": "error",
"prefer-const": "error",
// "prefer-arrow-callback": "error",
"prefer-const": "warn",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"radix": "error",
"prefer-template": "off",
radix: "error",
"require-yield": "error",
"strict": ["error", "global"],
strict: ["error", "global"],
"use-isnan": "error",
"valid-typeof": "error",
"vars-on-top": "error",
"yoda": ["error", "never"],
yoda: ["error", "never"],
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
"no-console": [
"warn",
{
allow: [
//---
"debug",
"warn",
"error",
"group",
"groupCollapsed",
"groupEnd",
"table",
"trace",
"time",
"timeEnd",
"timeStamp",
],
},
],
// SonarJS is not yet compatible with ESLint 9. Commenting these out
// until it is.
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],

View File

@ -13,7 +13,7 @@ export const reactConfig = tseslint.config({
},
plugins: {
"react": reactPlugin,
react: reactPlugin,
"react-hooks": hooksPlugin,
},

View File

@ -1,25 +1,42 @@
// @ts-check
import tseslint from "typescript-eslint";
import NodeLintPlugin from "../plugins/node-lint.js";
/**
* ESLint configuration for TypeScript authentik projects.
*/
export const typescriptConfig = tseslint.config({
plugins: {
"node-lint": NodeLintPlugin,
},
rules: {
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-expect-error": "allow-with-description",
"ts-ignore": true,
"ts-nocheck": "allow-with-description",
"ts-check": false,
"minimumDescriptionLength": 5,
},
],
"@typescript-eslint/ban-ts-comment": "off",
// "@typescript-eslint/ban-ts-comment": [
// "error",
// {
// "ts-expect-error": "allow-with-description",
// "ts-ignore": true,
// "ts-nocheck": "allow-with-description",
// "ts-check": false,
// "minimumDescriptionLength": 5,
// },
// ],
"@typescript-eslint/no-explicit-any": "warn",
"no-unused-private-class-members": "warn",
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": "error",
// "@typescript-eslint/no-use-before-define": "error",
"no-invalid-this": "off",
"no-unused-vars": "off",
"@typescript-eslint/triple-slash-reference": [
"warn",
{
path: "never",
types: "always",
lib: "always",
},
],
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-unused-vars": [
"warn",

View File

@ -6,26 +6,9 @@
"scripts": {
"build": "tsc -p ."
},
"main": "./index.js",
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"import": "./index.js",
"types": "./out/index.d.ts"
},
"./react-config": {
"import": "./lib/react-config.js",
"types": "./out/lib/react-config.d.ts"
},
"./javascript-config": {
"import": "./lib/javascript-config.js",
"types": "./out/lib/javascript-config.d.ts"
},
"./typescript-config": {
"import": "./lib/typescript-config.js",
"types": "./out/lib/typescript-config.d.ts"
}
},
"exports": "./index.js",
"dependencies": {
"eslint": "^9.23.0",
"eslint-plugin-import": "^2.31.0",
@ -35,8 +18,8 @@
"eslint-plugin-wc": "^2.1.1"
},
"devDependencies": {
"@goauthentik/prettier-config": "^1.0.1",
"@goauthentik/tsconfig": "^1.0.1",
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/tsconfig": "^1.0.4",
"@types/eslint": "^9.6.1",
"typescript": "^5.8.2",
"typescript-eslint": "^8.29.0"

View File

@ -0,0 +1,85 @@
/**
* @file ESLint rule to enforce the use of the `node:` prefix for Node.js built-in modules.
*
* @import { Rule, ESLint } from "eslint";
* @import { RuleTextEditor } from "@eslint/core";
*/
import { builtinModules } from "node:module";
const NODE_PROTOCOL_PREFIX = "node:";
/**
* @type {Map<string, string>}
*/
const NodeModulesIndex = new Map();
for (const moduleName of builtinModules) {
if (moduleName.startsWith("_")) continue;
if (moduleName.startsWith(NODE_PROTOCOL_PREFIX)) continue;
NodeModulesIndex.set(moduleName, `${NODE_PROTOCOL_PREFIX}${moduleName}`);
}
/**
* @type {Rule.RuleModule}
*/
const rule = {
meta: {
type: "problem",
fixable: "code",
hasSuggestions: true,
docs: {
description: "Enforce `node:` prefix for Node.js built-in modules.",
recommended: true,
},
},
create: (context) => {
/**
* @type {Rule.RuleListener}
*/
const ruleListener = {
ImportDeclaration({ source }) {
if (source.type !== "Literal" || typeof source.value !== "string") {
return;
}
const moduleName = source.value;
const prefixedModuleName = NodeModulesIndex.get(moduleName);
if (!prefixedModuleName) return;
/**
* @param {RuleTextEditor} editor
*/
const fix = (editor) =>
editor.replaceText(source, JSON.stringify(prefixedModuleName));
context.report({
node: source,
message: `Module "${moduleName}" must use the "node:" prefix.`,
fix,
suggest: [
{
fix,
desc: `Use "${prefixedModuleName}" instead.`,
},
],
});
},
};
return ruleListener;
},
};
/**
* @type {ESLint.Plugin}
*/
const NodeLintPlugin = {
rules: {
"no-unprefixed-imports": rule,
},
};
export default NodeLintPlugin;

View File

@ -0,0 +1,25 @@
/**
* @file Utility functions for building and copying files.
*/
/**
* Given an object of environment variables, returns a new object with the same keys and values, but
* with the values serialized as strings.
* @template {string} K
*
* @param {Record<K, string | number | boolean | object>} input
* @returns {Record<`process.env.${K}`, string>}
*/
export function serializeEnvironmentVars(input) {
/**
* @type {Record<string, string>}
*/
const env = {};
for (const [key, value] of Object.entries(input)) {
const namespaceKey = `process.env.${key}`;
env[namespaceKey] = JSON.stringify(value || "");
}
return /** @type {Record<string, string>} */ (env);
}

View File

@ -1,4 +1,5 @@
export * from "./paths.js";
export * from "./constants.js";
export * from "./build.js";
export * from "./version.js";
export * from "./scripting.js";

View File

@ -1,19 +1,14 @@
{
"name": "@goauthentik/monorepo",
"version": "1.0.0",
"version": "1.0.4",
"description": "Utilities for the authentik monorepo.",
"private": true,
"license": "MIT",
"private": true,
"main": "index.js",
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"import": "./index.js",
"types": "./out/index.d.ts"
}
},
"types": "./out/index.d.ts",
"exports": "./index.js",
"engines": {
"node": ">=20.11"
}
},
"types": "./out/index.d.ts"
}

View File

@ -2,7 +2,7 @@ import { createRequire } from "node:module";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const relativeDirname = dirname(fileURLToPath(import.meta.url));
/**
* @typedef {'~authentik'} MonoRepoRoot
@ -11,7 +11,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* The root of the authentik monorepo.
*/
export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(__dirname, "..", ".."));
export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(relativeDirname, "..", ".."));
const require = createRequire(import.meta.url);

View File

@ -23,7 +23,7 @@ export const AuthentikPrettierConfig = {
jsxSingleQuote: false,
printWidth: 100,
proseWrap: "preserve",
quoteProps: "consistent",
quoteProps: "as-needed",
requirePragma: false,
semi: true,
singleQuote: false,
@ -31,8 +31,23 @@ export const AuthentikPrettierConfig = {
trailingComma: "all",
useTabs: false,
vueIndentScriptAndStyle: false,
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
plugins: [
// ---
"prettier-plugin-packagejson",
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-django-alpine",
],
importOrder: [
// ---
// Lit Imports
"^(@?)lit(.*)$",
// CSS Imports
"\\.css$",
// API Imports
"^@goauthentik/api$",
// Relative Imports
"^[./]",
],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
@ -49,6 +64,13 @@ export const AuthentikPrettierConfig = {
trailingComma: "none",
},
},
{
files: "authentik/**/*.html",
options: {
tabWidth: 2,
parser: "html",
},
},
{
files: "package.json",
options: {

View File

@ -6,6 +6,7 @@
"scripts": {
"build": "tsc -p ."
},
"main": "./index.js",
"type": "module",
"exports": {
"./package.json": "./package.json",
@ -15,17 +16,17 @@
}
},
"devDependencies": {
"@goauthentik/tsconfig": "^1.0.1",
"@goauthentik/tsconfig": "^1.0.4",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-django-alpine": "^1.3.0",
"prettier-plugin-packagejson": "^2.5.10",
"typescript": "^5.8.2"
},
"peerDependencies": {
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-django-alpine": "^1.3.0",
"prettier-plugin-packagejson": "^2.5.10"
},
"engines": {

View File

@ -2,19 +2,16 @@
"name": "@goauthentik/tsconfig",
"version": "1.0.4",
"description": "authentik's base TypeScript configuration.",
"license": "MIT",
"main": "tsconfig.json",
"type": "module",
"engines": {
"node": ">=20.11"
},
"keywords": [
"tsconfig",
"typescript"
],
"license": "MIT",
"scripts": {
"build": ""
},
"type": "module",
"main": "tsconfig.json",
"engines": {
"node": ">=20.11"
},
"publishConfig": {
"access": "public"
}

View File

@ -6,15 +6,14 @@ FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS web-builder
ENV NODE_ENV=production
WORKDIR /static
COPY package.json /
RUN --mount=type=bind,target=/static/package.json,src=./web/package.json \
--mount=type=bind,target=/static/package-lock.json,src=./web/package-lock.json \
--mount=type=bind,target=/static/scripts,src=./web/scripts \
--mount=type=cache,target=/root/.npm \
npm ci --include=dev
COPY ./package.json ./package.json
COPY ./package-lock.json ./package-lock.json
COPY ./packages ./packages
COPY ./web ./web
RUN --mount=type=cache,target=/root/.npm npm ci --include=dev
RUN npm run build-proxy -w @goauthentik/web
COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.24-bookworm AS builder
@ -65,10 +64,10 @@ RUN apt-get update && \
rm -rf /tmp/* /var/lib/apt/lists/*
COPY --from=builder /go/proxy /
COPY --from=web-builder /static/robots.txt /web/robots.txt
COPY --from=web-builder /static/security.txt /web/security.txt
COPY --from=web-builder /static/dist/ /web/dist/
COPY --from=web-builder /static/authentik/ /web/authentik/
COPY --from=web-builder /static/web/robots.txt /web/robots.txt
COPY --from=web-builder /static/web/security.txt /web/security.txt
COPY --from=web-builder /static/web/dist/ /web/dist/
COPY --from=web-builder /static/web/authentik/ /web/authentik/
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/proxy", "healthcheck" ]

27
tsconfig.json Normal file
View File

@ -0,0 +1,27 @@
// TypeScript Project Configuration
{
"watchOptions": {
"excludeDirectories": [
"**/.git", // Git
"**/.yarn", // Yarn
"**/.vscode", // VS Code
"**/.vscode-test-web", // VS Code Web Test
"**/dist", // Distributed build files
"**/out", // Output build files
"**/.drafts", // Drafts
"**/.github", // GitHub
"**/node_modules" // Node modules
]
},
// The root project has no sources of its own. By setting `files` to an empty
// list, TS won't automatically include all sources below root (the default).
"files": [],
"references": [
// Note that references are in the order we want them to be built.
{ "path": "./packages/prettier-config" },
{ "path": "./packages/eslint-config" },
{ "path": "./packages/esbuild-plugin-live-reload" },
{ "path": "./web" }
]
}

View File

@ -1,16 +0,0 @@
# don't ever lint node_modules
node_modules
# don't lint build output (make sure it's set to your correct build folder name)
dist
# don't lint nyc coverage output
coverage
# Import order matters
poly.ts
src/locale-codes.ts
src/locales/
storybook-static/
# Prettier breaks the tsconfig file
tsconfig.json
.storybook/css-import-maps*
package.json
packages/**/package.json

View File

@ -1,23 +0,0 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 100,
"proseWrap": "preserve",
"quoteProps": "consistent",
"requirePragma": false,
"semi": true,
"singleQuote": false,
"tabWidth": 4,
"trailingComma": "all",
"useTabs": false,
"vueIndentScriptAndStyle": false,
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"importOrderParserPlugins": ["typescript", "jsx", "classProperties", "decorators-legacy"]
}

View File

@ -1,14 +1,15 @@
import replace from "@rollup/plugin-replace";
import { NodeEnvironment, resolvePackage, serializeEnvironmentVars } from "@goauthentik/monorepo";
import { PackageRoot } from "@goauthentik/web/paths";
import type { StorybookConfig } from "@storybook/web-components-vite";
import { cwd } from "process";
import { deepmerge } from "deepmerge-ts";
import * as path from "node:path";
import modify from "rollup-plugin-modify";
import postcssLit from "rollup-plugin-postcss-lit";
import tsconfigPaths from "vite-tsconfig-paths";
export const isProdBuild = process.env.NODE_ENV === "production";
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
const AK_API_BASE_PATH = process.env.AK_API_BASE_PATH || "";
const importInlinePatterns = [
const inlineImportPatterns = [
'import AKGlobal from "(\\.\\./)*common/styles/authentik\\.css',
'import AKGlobal from "@goauthentik/common/styles/authentik\\.css',
'import PF.+ from "@patternfly/patternfly/\\S+\\.css',
@ -17,7 +18,9 @@ const importInlinePatterns = [
'import styles from "\\./LibraryPageImpl\\.css',
];
const importInlineRegexp = new RegExp(importInlinePatterns.map((a) => `(${a})`).join("|"));
const inlineImportPattern = new RegExp(inlineImportPatterns.map((a) => `(${a})`).join("|"));
const patternflyPath = resolvePackage("@patternfly/patternfly");
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
@ -29,19 +32,19 @@ const config: StorybookConfig = {
],
staticDirs: [
{
from: "../node_modules/@patternfly/patternfly/patternfly-base.css",
from: path.resolve(patternflyPath, "patternfly-base.css"),
to: "@patternfly/patternfly/patternfly-base.css",
},
{
from: "../src/common/styles/authentik.css",
from: path.resolve(PackageRoot, "src", "common", "styles", "authentik.css"),
to: "@goauthentik/common/styles/authentik.css",
},
{
from: "../src/common/styles/theme-dark.css",
from: path.resolve(PackageRoot, "src", "common", "styles", "theme-dark.css"),
to: "@goauthentik/common/styles/theme-dark.css",
},
{
from: "../src/common/styles/one-dark.css",
from: path.resolve(PackageRoot, "src", "common", "styles", "one-dark.css"),
to: "@goauthentik/common/styles/one-dark.css",
},
],
@ -53,28 +56,25 @@ const config: StorybookConfig = {
autodocs: "tag",
},
async viteFinal(config) {
return {
...config,
return deepmerge(config, {
define: serializeEnvironmentVars({
NODE_ENV: NodeEnvironment,
CWD: process.cwd(),
AK_API_BASE_PATH: AK_API_BASE_PATH,
WATCHER_URL: "",
}),
plugins: [
modify({
find: importInlineRegexp,
find: inlineImportPattern,
replace: (match: RegExpMatchArray) => {
return `${match}?inline`;
},
}),
replace({
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development",
),
"process.env.CWD": JSON.stringify(cwd()),
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true,
}),
...config.plugins,
postcssLit(),
tsconfigPaths(),
],
};
});
},
};

View File

@ -1,5 +1,6 @@
<link rel="stylesheet" href="@patternfly/patternfly/patternfly-base.css" />
<link rel="stylesheet" href="@goauthentik/common/styles/authentik.css" />
<style>
body {
overflow-y: scroll;

View File

@ -1,90 +0,0 @@
import eslint from "@eslint/js";
import tsparser from "@typescript-eslint/parser";
import litconf from "eslint-plugin-lit";
import wcconf from "eslint-plugin-wc";
import globals from "globals";
import tseslint from "typescript-eslint";
export default [
// You would not believe how much this change has frustrated users: ["if an ignores key is used
// without any other keys in the configuration object, then the patterns act as global
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
{
ignores: [
"dist/",
// don't lint the cache
".wireit/",
// let packages have their own configurations
"packages/",
// don't ever lint node_modules
"node_modules/",
".storybook/*",
// don't lint build output (make sure it's set to your correct build folder name)
// don't lint nyc coverage output
"coverage/",
"src/locale-codes.ts",
"storybook-static/",
"src/locales/",
],
},
eslint.configs.recommended,
wcconf.configs["flat/recommended"],
litconf.configs["flat/recommended"],
...tseslint.configs.recommended,
{
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
},
files: ["src/**"],
rules: {
"lit/attribute-names": "off",
// "lit/attribute-names": "error",
"lit/no-private-properties": "error",
// "lit/prefer-nothing": "warn",
"lit/no-template-bind": "error",
"no-unused-vars": "off",
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
},
},
{
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
globals: {
...globals.nodeBuiltin,
...globals.node,
},
},
files: ["scripts/**/*.mjs", "*.ts", "*.mjs"],
rules: {
"no-unused-vars": "off",
// We WANT our scripts to output to the console!
"no-console": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
},
},
];

26423
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,39 @@
{
"name": "@goauthentik/web",
"version": "0.0.0",
"overrides": {
"rapidoc": {
"@apitools/openapi-parser@": "0.0.37"
},
"chromedriver": {
"axios": "^1.8.4"
}
"license": "MIT",
"private": true,
"scripts": {
"build": "wireit",
"build-locales": "wireit",
"build-locales:build": "wireit",
"build-proxy": "wireit",
"build:sfe": "wireit",
"esbuild:watch": "node scripts/build-web.mjs --watch",
"extract-locales": "wireit",
"format": "wireit",
"lint": "wireit",
"lint:imports": "wireit",
"lint:lockfile": "wireit",
"lit-analyse": "wireit",
"postinstall": "bash scripts/patch-spotlight.sh",
"precommit": "wireit",
"prettier": "wireit",
"prettier-check": "wireit",
"pseudolocalize": "wireit",
"storybook": "storybook dev -p 6006",
"storybook:build": "wireit",
"test": "wireit",
"test:e2e": "wireit",
"test:e2e:watch": "wireit",
"test:watch": "wireit",
"watch": "run-s build-locales esbuild:watch"
},
"type": "module",
"exports": {
"./package.json": "./package.json",
"./paths": "./paths.js",
"./scripts/*": "./scripts/*.mjs"
},
"dependencies": {
"@codemirror/lang-css": "^6.3.1",
@ -44,6 +70,7 @@
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.2.4",
"esbuild-plugin-copy": "^2.1.1",
"fuse.js": "^7.0.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
@ -62,6 +89,7 @@
"remark-gfm": "^4.0.1",
"remark-mdx-frontmatter": "^5.0.0",
"style-mod": "^4.1.2",
"trusted-types": "^2.0.0",
"ts-pattern": "^5.4.0",
"unist-util-visit": "^5.0.0",
"webcomponent-qr-code": "^1.2.0",
@ -69,12 +97,15 @@
},
"devDependencies": {
"@eslint/js": "^9.11.1",
"@goauthentik/esbuild-plugin-live-reload": "^1.0.4",
"@goauthentik/monorepo": "^1.0.4",
"@goauthentik/prettier-config": "^1.0.4",
"@goauthentik/tsconfig": "^1.0.4",
"@hcaptcha/types": "^1.0.4",
"@lit/localize-tools": "^0.8.0",
"@rollup/plugin-replace": "^6.0.1",
"@storybook/addon-essentials": "^8.3.4",
"@storybook/addon-links": "^8.3.4",
"@storybook/api": "^7.6.17",
"@storybook/blocks": "^8.3.4",
"@storybook/builder-vite": "^8.3.4",
"@storybook/manager-api": "^8.3.4",
@ -84,13 +115,13 @@
"@types/chart.js": "^2.9.41",
"@types/codemirror": "^5.60.15",
"@types/dompurify": "^3.0.5",
"@types/eslint__js": "^8.42.3",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.2",
"@types/mocha": "^10.0.8",
"@types/node": "^22.7.4",
"@types/node": "^22.14.1",
"@types/react": "^18.3.13",
"@types/react-dom": "^18.3.0",
"@types/trusted-types": "^2.0.7",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"@wdio/browser-runner": "9.4",
@ -105,18 +136,15 @@
"eslint-plugin-wc": "^2.1.1",
"find-free-ports": "^3.1.1",
"github-slugger": "^2.0.0",
"glob": "^11.0.0",
"globals": "^15.10.0",
"knip": "^5.30.6",
"lit-analyzer": "^2.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.3",
"prettier": "^3.5.3",
"pseudolocale": "^2.1.0",
"rollup-plugin-modify": "^3.0.0",
"rollup-plugin-postcss-lit": "^2.1.0",
"storybook": "^8.3.4",
"storybook-addon-mock": "^5.0.0",
"syncpack": "^13.0.0",
"turnstile-types": "^1.2.3",
"typescript": "^5.6.2",
"typescript-eslint": "^8.8.0",
@ -124,10 +152,6 @@
"vite-tsconfig-paths": "^5.0.1",
"wireit": "^0.14.9"
},
"engines": {
"node": ">=20"
},
"license": "MIT",
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.24.0",
"@esbuild/linux-amd64": "^0.18.11",
@ -136,39 +160,6 @@
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
"@rollup/rollup-linux-x64-gnu": "4.23.0"
},
"private": true,
"scripts": {
"build": "wireit",
"build-locales": "wireit",
"build-locales:build": "wireit",
"build-proxy": "wireit",
"build:sfe": "wireit",
"esbuild:watch": "node scripts/build-web.mjs --watch",
"extract-locales": "wireit",
"format": "wireit",
"lint": "wireit",
"lint:imports": "wireit",
"lint:lockfile": "wireit",
"lint:nightmare": "wireit",
"lint:package": "wireit",
"lint:precommit": "wireit",
"lint:types": "wireit",
"lit-analyse": "wireit",
"postinstall": "bash scripts/patch-spotlight.sh",
"precommit": "wireit",
"prettier": "wireit",
"prettier-check": "wireit",
"pseudolocalize": "wireit",
"storybook": "storybook dev -p 6006",
"storybook:build": "wireit",
"test": "wireit",
"test:e2e": "wireit",
"test:e2e:watch": "wireit",
"test:watch": "wireit",
"tsc": "wireit",
"watch": "run-s build-locales esbuild:watch"
},
"type": "module",
"wireit": {
"build": {
"#comment": [
@ -245,10 +236,7 @@
"command": "lit-localize extract"
},
"format": {
"command": "prettier --write .",
"dependencies": [
"lint:package"
]
"command": "prettier --write ."
},
"format:packages": {
"dependencies": [
@ -267,16 +255,6 @@
"lint:imports": {
"command": "knip --config scripts/knip.config.ts"
},
"lint:types:tests": {
"command": "tsc --noEmit -p ./tests"
},
"lint:types": {
"command": "tsc --noEmit -p .",
"dependencies": [
"build-locales",
"lint:types:tests"
]
},
"lint:lockfile": {
"__comment": "The lockfile-lint package does not have an option to ensure resolved hashes are set everywhere",
"shell": true,
@ -287,27 +265,6 @@
"./packages/sfe:lint:lockfile"
]
},
"lint:package": {
"command": "syncpack format -i ' '"
},
"lint:nightmare": {
"command": "${NODE_RUNNER} ./scripts/eslint.mjs --nightmare",
"env": {
"NODE_RUNNER": {
"external": true,
"default": "node"
}
}
},
"lint:precommit": {
"command": "${NODE_RUNNER} ./scripts/eslint.mjs --precommit",
"env": {
"NODE_RUNNER": {
"external": true,
"default": "node"
}
}
},
"lint:spelling": {
"command": "node scripts/check-spelling.mjs"
},
@ -317,13 +274,10 @@
"precommit": {
"command": "prettier --write .",
"dependencies": [
"lint:types",
"lint:components",
"lint:spelling",
"lint:package",
"lint:lockfile",
"lint:lockfiles",
"lint:precommit",
"format:packages"
]
},
@ -332,9 +286,6 @@
"format"
]
},
"prettier-check": {
"command": "prettier --check ."
},
"pseudolocalize": {
"command": "node scripts/pseudolocalize.mjs"
},
@ -378,15 +329,18 @@
"env": {
"TS_NODE_PROJECT": "tsconfig.test.json"
}
},
"tsc": {
"dependencies": [
"lint:types"
]
}
},
"workspaces": [
".",
"./packages/*"
]
"engines": {
"node": ">=22.14.0"
},
"prettier": "@goauthentik/prettier-config",
"overrides": {
"rapidoc": {
"@apitools/openapi-parser@": "0.0.37"
},
"chromedriver": {
"axios": "^1.8.4"
}
}
}

View File

@ -1,23 +0,0 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 100,
"proseWrap": "preserve",
"quoteProps": "consistent",
"requirePragma": false,
"semi": true,
"singleQuote": false,
"tabWidth": 4,
"trailingComma": "all",
"useTabs": false,
"vueIndentScriptAndStyle": false,
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"importOrderParserPlugins": ["typescript", "classProperties", "decorators-legacy"]
}

View File

@ -1,6 +1,14 @@
{
"name": "@goauthentik/web-sfe",
"version": "0.0.0",
"license": "MIT",
"private": true,
"scripts": {
"build": "wireit",
"lint:lockfile": "wireit",
"prettier": "prettier --write ./src ./tsconfig.json ./rollup.config.js ./package.json",
"watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
},
"dependencies": {
"@goauthentik/api": "^2024.6.0-1719577139",
"base64-js": "^1.5.1",
@ -10,6 +18,7 @@
"weakmap-polyfill": "^2.0.4"
},
"devDependencies": {
"@goauthentik/prettier-config": "^1.0.4",
"@rollup/plugin-commonjs": "^28.0.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-swc": "^0.4.0",
@ -18,12 +27,11 @@
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/jquery": "^3.5.31",
"lockfile-lint": "^4.14.0",
"prettier": "^3.3.2",
"prettier": "^3.5.3",
"rollup": "^4.23.0",
"rollup-plugin-copy": "^3.5.0",
"wireit": "^0.14.9"
},
"license": "MIT",
"optionalDependencies": {
"@swc/core": "^1.7.28",
"@swc/core-darwin-arm64": "^1.6.13",
@ -37,13 +45,6 @@
"@swc/core-win32-ia32-msvc": "^1.6.13",
"@swc/core-win32-x64-msvc": "^1.6.13"
},
"private": true,
"scripts": {
"build": "wireit",
"lint:lockfile": "wireit",
"prettier": "prettier --write ./src ./tsconfig.json ./rollup.config.js ./package.json",
"watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
},
"wireit": {
"build:sfe": {
"command": "rollup -c rollup.config.js --bundleConfigAsCjs",

View File

@ -1,8 +1,11 @@
import { fromByteArray } from "base64-js";
// sort-imports-ignore
import "formdata-polyfill";
import $ from "jquery";
import "weakmap-polyfill";
import { fromByteArray } from "base64-js";
import $ from "jquery";
import {
type AuthenticatorValidationChallenge,
type AutosubmitChallenge,
@ -273,7 +276,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
deviceChallenge?: DeviceChallenge;
b64enc(buf: Uint8Array): string {
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]/g, "");
}
b64RawEnc(buf: Uint8Array): string {

78
web/paths.js Normal file
View File

@ -0,0 +1,78 @@
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const relativeDirname = dirname(fileURLToPath(import.meta.url));
//#region Base paths
/**
* @typedef {'@goauthentik/web'} WebPackageIdentifier
*/
/**
* The root of the web package.
*/
export const PackageRoot = /** @type {WebPackageIdentifier} */ (resolve(relativeDirname));
/**
* The name of the distribution directory.
*/
export const DistDirectoryName = "dist";
/**
* Path to the web package's distribution directory.
*
* This is where the built files are located after running the build process.
*/
export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectoryName}`} */ (
resolve(relativeDirname, DistDirectoryName)
);
//#endregion
//#region Entry points
/**
* @typedef {{ in: string, out: string }} EntryPointTarget
*
* ESBuild entrypoint target.
* Matches the type defined in the ESBuild context.
*/
/**
* Entry points available for building.
*
* @satisfies {Record<string, EntryPointTarget>}
*/
export const EntryPoint = /** @type {const} */ ({
Admin: {
in: resolve(PackageRoot, "src", "admin", "AdminInterface", "index.entrypoint.ts"),
out: resolve(DistDirectory, "admin", "AdminInterface"),
},
User: {
in: resolve(PackageRoot, "src", "user", "index.entrypoint.ts"),
out: resolve(DistDirectory, "user", "UserInterface"),
},
Flow: {
in: resolve(PackageRoot, "src", "flow", "index.entrypoint.ts"),
out: resolve(DistDirectory, "flow", "FlowInterface"),
},
Standalone: {
in: resolve(PackageRoot, "src", "standalone", "api-browser/index.entrypoint.ts"),
out: resolve(DistDirectory, "standalone", "api-browser", "index"),
},
StandaloneLoading: {
in: resolve(PackageRoot, "src", "standalone", "loading/index.entrypoint.ts"),
out: resolve(DistDirectory, "standalone", "loading", "index"),
},
RAC: {
in: resolve(PackageRoot, "src", "rac", "index.entrypoint.ts"),
out: resolve(DistDirectory, "rac", "index"),
},
Polyfill: {
in: resolve(PackageRoot, "src", "polyfill", "index.entrypoint.ts"),
out: resolve(DistDirectory, "poly"),
},
});
//#endregion

View File

@ -1,7 +1,7 @@
import { spawnSync } from "child_process";
import fs from "fs";
import path from "path";
import process from "process";
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
/**
* Determines if all the Xliff translation source files are present and if the Typescript source
@ -12,6 +12,9 @@ import process from "process";
const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8"));
/**
* @param {string} loc
*/
function generatedFileIsUpToDateWithXliffSource(loc) {
const xliff = path.join("./xliff", `${loc}.xlf`);
const gened = path.join("./src/locales", `${loc}.ts`);
@ -21,16 +24,26 @@ function generatedFileIsUpToDateWithXliffSource(loc) {
// than the generated file. The missing XLF file is important enough it
// generates a unique error message and halts the build.
/**
* @type {fs.Stats}
*/
let xlfStat;
try {
var xlfStat = fs.statSync(xliff);
xlfStat = fs.statSync(xliff);
} catch (_error) {
console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`);
process.exit(1);
}
/**
* @type {fs.Stats}
*/
let genedStat;
// If the generated file doesn't exist, of course it's not up to date.
try {
var genedStat = fs.statSync(gened);
genedStat = fs.statSync(gened);
} catch (_error) {
return false;
}

View File

@ -1,137 +1,92 @@
import { execFileSync } from "child_process";
/**
* @file ESBuild script for building the authentik web UI.
*
* @import { BuildOptions } from "esbuild";
*/
import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload/plugin";
import {
MonoRepoRoot,
NodeEnvironment,
readBuildIdentifier,
resolvePackage,
serializeEnvironmentVars,
} from "@goauthentik/monorepo";
import { DistDirectory, DistDirectoryName, EntryPoint, PackageRoot } from "@goauthentik/web/paths";
import { deepmerge } from "deepmerge-ts";
import esbuild from "esbuild";
import copy from "esbuild-plugin-copy";
import { polyfillNode } from "esbuild-plugin-polyfill-node";
import findFreePorts from "find-free-ports";
import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs";
import { globSync } from "glob";
import * as path from "path";
import { cwd } from "process";
import process from "process";
import { fileURLToPath } from "url";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import process from "node:process";
import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs";
import { buildObserverPlugin } from "./esbuild/build-observer-plugin.mjs";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
let authentikProjectRoot = path.join(__dirname, "..", "..");
const logPrefix = "[Build]";
try {
// Use the package.json file in the root folder, as it has the current version information.
authentikProjectRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
encoding: "utf8",
}).replace("\n", "");
} catch (_error) {
// We probably don't have a .git folder, which could happen in container builds.
}
const definitions = serializeEnvironmentVars({
NODE_ENV: NodeEnvironment,
CWD: process.cwd(),
AK_API_BASE_PATH: process.env.AK_API_BASE_PATH,
WATCHER_URL: process.env.WATCHER_URL,
});
const packageJSONPath = path.join(authentikProjectRoot, "./package.json");
const rootPackage = JSON.parse(readFileSync(packageJSONPath, "utf8"));
const NODE_ENV = process.env.NODE_ENV || "development";
const AK_API_BASE_PATH = process.env.AK_API_BASE_PATH || "";
const environmentVars = new Map([
["NODE_ENV", NODE_ENV],
["CWD", cwd()],
["AK_API_BASE_PATH", AK_API_BASE_PATH],
]);
const definitions = Object.fromEntries(
Array.from(environmentVars).map(([key, value]) => {
return [`process.env.${key}`, JSON.stringify(value)];
}),
);
const patternflyPath = resolvePackage("@patternfly/patternfly");
/**
* All is magic is just to make sure the assets are copied into the right places. This is a very
* stripped down version of what the rollup-copy-plugin does, without any of the features we don't
* use, and using globSync instead of globby since we already had globSync lying around thanks to
* Typescript. If there's a third argument in an array entry, it's used to replace the internal path
* before concatenating it all together as the destination target.
* @type {Array<[string, string, string?]>}
*/
const assetsFileMappings = [
["node_modules/@patternfly/patternfly/patternfly.min.css", "."],
["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"],
["src/common/styles/**", "."],
["src/assets/images/**", "./assets/images"],
["./icons/*", "./assets/icons"],
];
/**
* @param {string} filePath
*/
const isFile = (filePath) => statSync(filePath).isFile();
/**
* @param {string} src Source file
* @param {string} dest Destination folder
* @param {string} [strip] Path to strip from the source file
*/
function nameCopyTarget(src, dest, strip) {
const target = path.join(dest, strip ? src.replace(strip, "") : path.parse(src).base);
return [src, target];
}
for (const [source, rawdest, strip] of assetsFileMappings) {
const matchedPaths = globSync(source);
const dest = path.join("dist", rawdest);
const copyTargets = matchedPaths.map((path) => nameCopyTarget(path, dest, strip));
for (const [src, dest] of copyTargets) {
if (isFile(src)) {
mkdirSync(path.dirname(dest), { recursive: true });
copyFileSync(src, dest);
}
}
}
/**
* @typedef {[source: string, destination: string]} EntryPoint
*/
/**
* This starts the definitions used for esbuild: Our targets, our arguments, the function for
* running a build, and three options for building: watching, building, and building the proxy.
* Ordered by largest to smallest interface to build even faster
*
* @type {EntryPoint[]}
*/
const entryPoints = [
["admin/AdminInterface/AdminInterface.ts", "admin"],
["user/UserInterface.ts", "user"],
["flow/FlowInterface.ts", "flow"],
["standalone/api-browser/index.ts", "standalone/api-browser"],
["rac/index.ts", "rac"],
["standalone/loading/index.ts", "standalone/loading"],
["polyfill/poly.ts", "."],
];
/**
* @type {import("esbuild").BuildOptions}
* @type {Readonly<BuildOptions>}
*/
const BASE_ESBUILD_OPTIONS = {
entryNames: `[dir]/[name]-${readBuildIdentifier()}`,
chunkNames: "[dir]/chunks/[name]-[hash]",
assetNames: "assets/[dir]/[name]-[hash]",
publicPath: path.join("/static", DistDirectoryName),
outdir: DistDirectory,
bundle: true,
write: true,
sourcemap: true,
minify: NODE_ENV === "production",
minify: NodeEnvironment === "production",
legalComments: "external",
splitting: true,
treeShaking: true,
external: ["*.woff", "*.woff2"],
tsconfig: "./tsconfig.json",
tsconfig: path.resolve(PackageRoot, "tsconfig.build.json"),
loader: {
".css": "text",
},
plugins: [
copy({
assets: [
{
from: path.join(patternflyPath, "patternfly.min.css"),
to: ".",
},
{
from: path.join(patternflyPath, "assets", "**"),
to: "./assets",
},
{
from: path.resolve(PackageRoot, "src", "common", "styles", "**"),
to: ".",
},
{
from: path.resolve(PackageRoot, "src", "assets", "images", "**"),
to: "./assets/images",
},
{
from: path.resolve(PackageRoot, "icons", "*"),
to: "./assets/icons",
},
],
}),
polyfillNode({
polyfills: {
path: true,
},
}),
mdxPlugin({
root: authentikProjectRoot,
root: MonoRepoRoot,
}),
],
define: definitions,
@ -147,69 +102,42 @@ const BASE_ESBUILD_OPTIONS = {
},
};
/**
* Creates a version ID for the build.
* @returns {string}
*/
function composeVersionID() {
const { version } = rootPackage;
const buildHash = process.env.GIT_BUILD_HASH;
async function cleanDistDirectory() {
const timerLabel = `${logPrefix} ♻️ Cleaning previous builds...`;
if (buildHash) {
return `${version}+${buildHash}`;
}
console.time(timerLabel);
return version;
await fs.rm(DistDirectory, {
recursive: true,
force: true,
});
await fs.mkdir(DistDirectory, {
recursive: true,
});
console.timeEnd(timerLabel);
}
/**
* Build a single entry point.
* Creates an ESBuild options, extending the base options with the given overrides.
*
* @param {EntryPoint} buildTarget
* @param {Partial<esbuild.BuildOptions>} [overrides]
* @throws {Error} on build failure
* @param {BuildOptions} overrides
* @returns {BuildOptions}
*/
function createEntryPointOptions([source, dest], overrides = {}) {
const outdir = path.join(__dirname, "..", "dist", dest);
export function createESBuildOptions(overrides) {
/**
* @type {esbuild.BuildOptions}
* @type {BuildOptions}
*/
const mergedOptions = deepmerge(BASE_ESBUILD_OPTIONS, overrides);
const entryPointConfig = {
entryPoints: [`./src/${source}`],
entryNames: `[dir]/[name]-${composeVersionID()}`,
publicPath: path.join("/static", "dist", dest),
outdir,
};
/**
* @type {esbuild.BuildOptions}
*/
const mergedConfig = deepmerge(BASE_ESBUILD_OPTIONS, entryPointConfig, overrides);
return mergedConfig;
}
/**
* Build all entry points in parallel.
*
* @param {EntryPoint[]} entryPoints
* @returns {Promise<esbuild.BuildResult[]>}
*/
async function buildParallel(entryPoints) {
return Promise.all(
entryPoints.map((entryPoint) => {
return esbuild.build(createEntryPointOptions(entryPoint));
}),
);
return mergedOptions;
}
function doHelp() {
console.log(`Build the authentik UI
options:
-w, --watch: Build all ${entryPoints.length} interfaces
-w, --watch: Build all interfaces
-p, --proxy: Build only the polyfills and the loading application
-h, --help: This help message
`);
@ -218,36 +146,37 @@ function doHelp() {
}
async function doWatch() {
console.log("Watching all entry points...");
console.group(`${logPrefix} 🤖 Watching entry points`);
const wathcherPorts = await findFreePorts(entryPoints.length);
const entryPoints = Object.entries(EntryPoint).map(([entrypointID, target]) => {
console.log(entrypointID);
const buildContexts = await Promise.all(
entryPoints.map((entryPoint, i) => {
const port = wathcherPorts[i];
const serverURL = new URL(`http://localhost:${port}/events`);
return target;
});
return esbuild.context(
createEntryPointOptions(entryPoint, {
plugins: [
buildObserverPlugin({
serverURL,
logPrefix: entryPoint[1],
relativeRoot: path.join(__dirname, ".."),
}),
],
define: {
...definitions,
"process.env.WATCHER_URL": JSON.stringify(serverURL.toString()),
},
}),
);
console.groupEnd();
const wathcherPorts = await findFreePorts(1);
const port = wathcherPorts[0];
const serverURL = new URL(`http://localhost:${port}/events`);
const buildOptions = createESBuildOptions({
entryPoints,
plugins: [
liveReloadPlugin({
serverURL,
relativeRoot: PackageRoot,
}),
],
define: serializeEnvironmentVars({
WATCHER_URL: serverURL.toString(),
}),
);
});
await Promise.all(buildContexts.map((context) => context.rebuild()));
const buildContext = await esbuild.context(buildOptions);
await Promise.allSettled(buildContexts.map((context) => context.watch()));
await buildContext.rebuild();
await buildContext.watch();
return /** @type {Promise<void>} */ (
new Promise((resolve) => {
@ -259,15 +188,34 @@ async function doWatch() {
}
async function doBuild() {
console.log("Building all entry points");
console.group(`${logPrefix} 🚀 Building entry points:`);
return buildParallel(entryPoints);
const entryPoints = Object.entries(EntryPoint).map(([entrypointID, target]) => {
console.log(entrypointID);
return target;
});
console.groupEnd();
const buildOptions = createESBuildOptions({
entryPoints,
});
await esbuild.build(buildOptions);
console.log("Build complete");
}
async function doProxy() {
return buildParallel(
entryPoints.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)),
);
const entryPoints = [EntryPoint.StandaloneLoading];
const buildOptions = createESBuildOptions({
entryPoints,
});
await esbuild.build(buildOptions);
console.log("Proxy build complete");
}
async function delegateCommand() {
@ -289,12 +237,16 @@ async function delegateCommand() {
}
}
await delegateCommand()
.then(() => {
console.log("Build complete");
process.exit(0);
})
.catch((error) => {
console.error(error);
process.exit(1);
});
await cleanDistDirectory()
// ---
.then(() =>
delegateCommand()
.then(() => {
console.log("Build complete");
process.exit(0);
})
.catch((error) => {
console.error(error);
process.exit(1);
}),
);

View File

@ -1,5 +1,5 @@
import { execSync } from "child_process";
import path from "path";
import { execSync } from "node:child_process";
import path from "node:path";
const projectRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).replace(
"\n",

View File

@ -1,56 +0,0 @@
import { execFileSync } from "child_process";
import { ESLint } from "eslint";
import fs from "fs";
import path from "path";
import process from "process";
import { fileURLToPath } from "url";
function changedFiles() {
const gitStatus = execFileSync("git", ["diff", "--name-only", "HEAD"], { encoding: "utf8" });
const gitUntracked = execFileSync("git", ["ls-files", "--others", "--exclude-standard"], {
encoding: "utf8",
});
const changed = gitStatus
.split("\n")
.filter((line) => line.trim().substring(0, 4) === "web/")
.filter((line) => /\.(m|c)?(t|j)s$/.test(line))
.map((line) => line.substring(4))
.filter((line) => fs.existsSync(line));
const untracked = gitUntracked
.split("\n")
.filter((line) => /\.(m|c)?(t|j)s$/.test(line))
.filter((line) => fs.existsSync(line));
const sourceFiles = [...changed, ...untracked].filter((line) => /^src\//.test(line));
const scriptFiles = [...changed, ...untracked].filter(
(line) => /^scripts\//.test(line) || !/^src\//.test(line),
);
return [...sourceFiles, ...scriptFiles];
}
const __dirname = fileURLToPath(new URL(".", import.meta.url));
const projectRoot = path.join(__dirname, "..");
process.chdir(projectRoot);
const hasFlag = (flags) => process.argv.length > 1 && flags.includes(process.argv[2]);
const [configFile, files] = hasFlag(["-n", "--nightmare"])
? [path.join(__dirname, "eslint.nightmare.mjs"), changedFiles()]
: hasFlag(["-p", "--precommit"])
? [path.join(__dirname, "eslint.precommit.mjs"), changedFiles()]
: [path.join(projectRoot, "eslint.config.mjs"), ["."]];
const eslint = new ESLint({
overrideConfigFile: configFile,
warnIgnored: false,
});
const results = await eslint.lintFiles(files);
const formatter = await eslint.loadFormatter("stylish");
const resultText = formatter.format(results);
const errors = results.reduce((acc, result) => acc + result.errorCount, 0);
console.log(resultText);
process.exit(errors > 1 ? 1 : 0);

View File

@ -1,217 +0,0 @@
import eslint from "@eslint/js";
import tsparser from "@typescript-eslint/parser";
import litconf from "eslint-plugin-lit";
import wcconf from "eslint-plugin-wc";
import globals from "globals";
import tseslint from "typescript-eslint";
const MAX_DEPTH = 4;
const MAX_NESTED_CALLBACKS = 4;
const MAX_PARAMS = 5;
// Waiting for SonarJS to be compatible
// const MAX_COGNITIVE_COMPLEXITY = 9;
const rules = {
"accessor-pairs": "error",
"array-callback-return": "error",
"block-scoped-var": "error",
"consistent-return": "error",
"consistent-this": ["error", "that"],
"curly": ["error", "all"],
"dot-notation": [
"error",
{
allowKeywords: true,
},
],
"eqeqeq": "error",
"func-names": "error",
"guard-for-in": "error",
"max-depth": ["error", MAX_DEPTH],
"max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS],
"max-params": ["error", MAX_PARAMS],
"new-cap": "error",
"no-alert": "error",
"no-array-constructor": "error",
"no-bitwise": "error",
"no-caller": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-div-regex": "error",
"no-dupe-args": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-else-return": "error",
"no-empty": "error",
"no-empty-character-class": "error",
"no-empty-function": "error",
"no-labels": "error",
"no-eq-null": "error",
"no-eval": "error",
"no-ex-assign": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-boolean-cast": "error",
"no-extra-label": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-implied-eval": "error",
"no-implicit-coercion": "error",
"no-implicit-globals": "error",
"no-inner-declarations": ["error", "functions"],
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-iterator": "error",
"no-invalid-this": "error",
"no-label-var": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loop-func": "error",
"no-magic-numbers": ["error", { ignore: [0, 1, -1] }],
"no-multi-str": "error",
"no-negated-condition": "error",
"no-nested-ternary": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-wrappers": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-octal-escape": "error",
"no-param-reassign": "error",
"no-proto": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-restricted-syntax": ["error", "WithStatement"],
"no-script-url": "error",
"no-self-assign": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef": "error",
"no-undef-init": "error",
"no-unexpected-multiline": "error",
"no-useless-constructor": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
"no-unreachable": "error",
"no-unused-expressions": "error",
"no-unused-labels": "error",
"no-use-before-define": "error",
"no-useless-call": "error",
"no-dupe-class-members": "error",
"no-var": "error",
"no-void": "error",
"no-with": "error",
"prefer-arrow-callback": "error",
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"radix": "error",
"require-yield": "error",
"strict": ["error", "global"],
"use-isnan": "error",
"valid-typeof": "error",
"vars-on-top": "error",
"yoda": ["error", "never"],
"no-unused-vars": "off",
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
// SonarJS is not yet compatible with ESLint 9. Commenting these out
// until it is.
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
// "sonarjs/no-duplicate-string": "off",
// "sonarjs/no-nested-template-literals": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
};
export default [
// You would not believe how much this change has frustrated users: ["if an ignores key is used
// without any other keys in the configuration object, then the patterns act as global
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
{
ignores: [
"dist/",
".wireit/",
"packages/",
// don't ever lint node_modules
"node_modules/",
".storybook/*",
// don't lint build output (make sure it's set to your correct build folder name)
// don't lint nyc coverage output
"coverage/",
"src/locale-codes.ts",
"storybook-static/",
"src/locales/",
"src/**/*.test.ts",
],
},
eslint.configs.recommended,
wcconf.configs["flat/recommended"],
litconf.configs["flat/recommended"],
...tseslint.configs.recommended,
// sonar.configs.recommended,
{
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
globals: {
...globals.browser,
},
},
files: ["src/**"],
rules,
},
{
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
globals: {
...globals.nodeBuiltin,
},
},
files: ["scripts/*.mjs", "*.ts", "*.mjs"],
rules,
},
{
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
globals: {
...globals.nodeBuiltin,
...globals.jest,
},
},
files: ["src/**/*.test.ts"],
rules,
},
];

View File

@ -1,87 +0,0 @@
import eslint from "@eslint/js";
import tsparser from "@typescript-eslint/parser";
import litconf from "eslint-plugin-lit";
import wcconf from "eslint-plugin-wc";
import globals from "globals";
import tseslint from "typescript-eslint";
export default [
// You would not believe how much this change has frustrated users: ["if an ignores key is used
// without any other keys in the configuration object, then the patterns act as global
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
{
ignores: [
"dist/",
".wireit/",
"packages/",
// don't ever lint node_modules
"node_modules/",
".storybook/*",
// don't lint build output (make sure it's set to your correct build folder name)
// don't lint nyc coverage output
"coverage/",
"src/locale-codes.ts",
"storybook-static/",
"src/locales/",
],
},
eslint.configs.recommended,
wcconf.configs["flat/recommended"],
litconf.configs["flat/recommended"],
...tseslint.configs.recommended,
// sonar.configs.recommended,
{
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
},
files: ["src/**"],
rules: {
"no-unused-vars": "off",
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
// SonarJS is not yet compatible with ESLint 9. Commenting these out
// until it is.
// "sonarjs/cognitive-complexity": ["off", 9],
// "sonarjs/no-duplicate-string": "off",
// "sonarjs/no-nested-template-literals": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
},
},
{
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 12,
sourceType: "module",
},
globals: {
...globals.nodeBuiltin,
},
},
files: ["scripts/*.mjs", "*.ts", "*.mjs"],
rules: {
"no-unused-vars": "off",
"no-console": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
},
],
},
},
];

View File

@ -1,7 +1,7 @@
import { type KnipConfig } from "knip";
const config: KnipConfig = {
"entry": [
entry: [
"./src/admin/AdminInterface/AdminInterface.ts",
"./src/user/UserInterface.ts",
"./src/flow/FlowInterface.ts",
@ -10,18 +10,18 @@ const config: KnipConfig = {
"./src/standalone/loading/index.ts",
"./src/polyfill/poly.ts",
],
"project": ["src/**/*.ts", "src/**/*.js", "./scripts/*.mjs", ".storybook/*.ts"],
project: ["src/**/*.ts", "src/**/*.js", "./scripts/*.mjs", ".storybook/*.ts"],
// "ignore": ["src/**/*.test.ts", "src/**/*.stories.ts"],
// Prevent Knip from complaining about web components, which export their classes but also
// export their registration, and we don't always use both.
"ignoreExportsUsedInFile": true,
"typescript": {
ignoreExportsUsedInFile: true,
typescript: {
config: ["tsconfig.json"],
},
"wireit": {
wireit: {
config: ["package.json"],
},
"storybook": {
storybook: {
config: [".storybook/{main,test-runner}.{js,ts}"],
entry: [
".storybook/{manager,preview}.{js,jsx,ts,tsx}",
@ -29,7 +29,7 @@ const config: KnipConfig = {
],
project: [".storybook/**/*.{js,jsx,ts,tsx}"],
},
"eslint": {
eslint: {
entry: [
"eslint.config.mjs",
"scripts/eslint.precommit.mjs",

View File

@ -1,16 +1,18 @@
import { readFileSync } from "fs";
import path from "path";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import pseudolocale from "pseudolocale";
import { fileURLToPath } from "url";
import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js";
import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js";
import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
const relativeDirname = fileURLToPath(new URL(".", import.meta.url));
const pseudoLocale = "pseudo-LOCALE";
const targetLocales = [pseudoLocale];
const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8"));
const baseConfig = JSON.parse(
readFileSync(path.join(relativeDirname, "../lit-localize.json"), "utf-8"),
);
// Need to make some internal specifications to satisfy the transformer. It doesn't actually matter
// which Localizer we use (transformer or runtime), because all of the functionality we care about
@ -19,7 +21,7 @@ const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize
const config = {
...baseConfig,
baseDir: path.join(__dirname, ".."),
baseDir: path.join(relativeDirname, ".."),
targetLocales,
output: {
...baseConfig.output,
@ -41,4 +43,8 @@ const translations = messages.map(pseudoMessagify);
const sorted = sortProgramMessages([...messages]);
const formatter = makeFormatter(config);
formatter.writeOutput(sorted, new Map([[pseudoLocale, translations]]));
formatter.writeOutput(
sorted,
// @ts-expect-error Needs typing.
new Map([[pseudoLocale, translations]]),
);

View File

@ -2,9 +2,9 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
import "@goauthentik/elements/EmptyState";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
import { ModalButton } from "@goauthentik/elements/buttons/ModalButton";
import { WithBrandConfig } from "@goauthentik/elements/mixins/brand";
import { WithLicenseSummary } from "@goauthentik/elements/mixins/license";
import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
import { msg } from "@lit/localize";
@ -59,7 +59,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
renderModal() {
let product = globalAK().brand.brandingTitle || DefaultBrand.brandingTitle;
if (this.licenseSummary.status != LicenseSummaryStatusEnum.Unlicensed) {
if (this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed) {
product += ` ${msg("Enterprise")}`;
}
return html`<div

View File

@ -4,8 +4,8 @@ import { AKElement } from "@goauthentik/elements/Base";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
} from "@goauthentik/elements/mixins/capabilities";
import { WithVersion } from "@goauthentik/elements/mixins/version";
import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle";
import { spread } from "@open-wc/lit-helpers";
@ -13,6 +13,7 @@ import { spread } from "@open-wc/lit-helpers";
import { msg } from "@lit/localize";
import { TemplateResult, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { map } from "lit/directives/map.js";
import { UiThemeEnum } from "@goauthentik/api";
@ -71,10 +72,12 @@ export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement
render() {
return html`
<ak-sidebar
class="pf-c-page__sidebar ${this.open ? "pf-m-expanded" : "pf-m-collapsed"} ${this
.activeTheme === UiThemeEnum.Light
? "pf-m-light"
: ""}"
class=${classMap({
"pf-c-page__sidebar": true,
"pf-m-expanded": this.open,
"pf-m-collapsed": !this.open,
"pf-m-light": this.colorScheme === UiThemeEnum.Light,
})}
>
${this.renderSidebarItems()}
</ak-sidebar>

View File

@ -8,7 +8,7 @@ import {
import { configureSentry } from "@goauthentik/common/sentry";
import { me } from "@goauthentik/common/users";
import { WebsocketClient } from "@goauthentik/common/ws";
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
import { AKAuthenticatedInterfaceElement } from "@goauthentik/elements/Interface";
import "@goauthentik/elements/ak-locale-context";
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
import "@goauthentik/elements/banner/VersionBanner";
@ -30,12 +30,12 @@ import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { SessionUser, UiThemeEnum } from "@goauthentik/api";
import { SessionUser } from "@goauthentik/api";
import "./AdminSidebar";
@customElement("ak-interface-admin")
export class AdminInterface extends AuthenticatedInterface {
export class AdminInterface extends AKAuthenticatedInterfaceElement {
@property({ type: Boolean })
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
@ -123,15 +123,17 @@ export class AdminInterface extends AuthenticatedInterface {
super.connectedCallback();
if (process.env.NODE_ENV === "development" && process.env.WATCHER_URL) {
const { ESBuildObserver } = await import("@goauthentik/common/client");
const { ESBuildObserver } = await import(
"@goauthentik/esbuild-plugin-live-reload/client"
);
new ESBuildObserver(process.env.WATCHER_URL);
ESBuildObserver.initialize(process.env.WATCHER_URL);
}
}
render(): TemplateResult {
const sidebarClasses = {
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
"pf-m-light": this.colorScheme === "light",
};
const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen;

View File

@ -1,5 +1,5 @@
import { AdminInterface } from "./AdminInterface";
import "./AdminInterface";
import { AdminInterface } from "./index.entrypoint";
import "./index.entrypoint";
export { AdminInterface };
export default AdminInterface;

View File

@ -11,11 +11,11 @@ import "@goauthentik/admin/admin-overview/charts/SyncStatusChart";
import { VERSION } from "@goauthentik/common/constants";
import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
import "@goauthentik/elements/PageHeader";
import "@goauthentik/elements/cards/AggregatePromiseCard";
import "@goauthentik/elements/cards/QuickActionsCard.js";
import type { QuickAction } from "@goauthentik/elements/cards/QuickActionsCard.js";
import { WithLicenseSummary, isEnterpriseLicense } from "@goauthentik/elements/mixins/license.js";
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
import { msg, str } from "@lit/localize";
@ -164,15 +164,16 @@ export class AdminOverviewPage extends AdminOverviewBase {
}
renderCards() {
const isEnterprise = this.hasEnterpriseLicense;
const enterprise = isEnterpriseLicense(this.licenseSummary);
const classes = {
"card-container": true,
"pf-l-grid__item": true,
"pf-m-6-col": true,
"pf-m-4-col-on-md": !isEnterprise,
"pf-m-4-col-on-xl": !isEnterprise,
"pf-m-3-col-on-md": isEnterprise,
"pf-m-3-col-on-xl": isEnterprise,
"pf-m-4-col-on-md": !enterprise,
"pf-m-4-col-on-xl": !enterprise,
"pf-m-3-col-on-md": enterprise,
"pf-m-3-col-on-xl": enterprise,
};
return html`<div class=${classMap(classes)}>
@ -184,7 +185,7 @@ export class AdminOverviewPage extends AdminOverviewBase {
<div class=${classMap(classes)}>
<ak-admin-status-card-workers> </ak-admin-status-card-workers>
</div>
${isEnterprise
${enterprise
? html` <div class=${classMap(classes)}>
<ak-admin-fips-status-system> </ak-admin-fips-status-system>
</div>`

View File

@ -207,8 +207,8 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
.row=${(f?: FooterLink) =>
akFooterLinkInput({
".footerLink": f,
"style": "width: 100%",
"name": "footer-link",
style: "width: 100%",
name: "footer-link",
} as unknown as IFooterLinkInput)}
>
</ak-array-input>

View File

@ -8,10 +8,6 @@ import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
import "@goauthentik/elements/Alert.js";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/Interface/capabilitiesProvider";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/ModalForm";
@ -19,6 +15,10 @@ import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/ProxyForm";
import "@goauthentik/elements/forms/Radio";
import "@goauthentik/elements/forms/SearchSelect";
import {
CapabilitiesEnum,
WithCapabilitiesConfig,
} from "@goauthentik/elements/mixins/capabilities.js";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";

Some files were not shown because too many files have changed in this diff Show More