From 5cc3e47c1a7303f8eb93c131718fbeeba0f4b511 Mon Sep 17 00:00:00 2001 From: suraimu-team Date: Tue, 21 Oct 2025 02:01:58 +0800 Subject: [PATCH 01/15] fix(auth): Fix admin user authentication for /users/db-health endpoint by adding cookie JWT support (#422) Fixed authentication issue for admin users accessing the /users/db-health endpoint: - Added JWT token extraction from cookies (req.cookies?.jwt) - Added support for Bearer token from Authorization header - Improved error handling for missing and invalid tokens - Ensured consistent authentication flow for admin users Changes made: - Check for JWT token in req.cookies?.jwt - Support Bearer token from Authorization header - Return 401 error when token is missing - Return 401 error when token validation fails Fixes: https://github.com/Termix-SSH/Support/issues/12 --- src/backend/utils/auth-manager.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/backend/utils/auth-manager.ts b/src/backend/utils/auth-manager.ts index e560d845..5a1b3263 100644 --- a/src/backend/utils/auth-manager.ts +++ b/src/backend/utils/auth-manager.ts @@ -239,12 +239,19 @@ class AuthManager { createAdminMiddleware() { return async (req: Request, res: Response, next: NextFunction) => { - const authHeader = req.headers["authorization"]; - if (!authHeader?.startsWith("Bearer ")) { - return res.status(401).json({ error: "Missing Authorization header" }); + let token = req.cookies?.jwt; + + if (!token) { + const authHeader = req.headers["authorization"]; + if (authHeader?.startsWith("Bearer ")) { + token = authHeader.split(" ")[1]; + } + } + + if (!token) { + return res.status(401).json({ error: "Missing authentication token" }); } - const token = authHeader.split(" ")[1]; const payload = await this.verifyJWTToken(token); if (!payload) { From e8120386d0e8980661b1945bb2d052b767194c3c Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:36:58 -0500 Subject: [PATCH 02/15] Update Docker login credentials and image names --- .github/workflows/docker-image.yml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index bab6cd4f..aa42b2e6 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -58,8 +58,8 @@ jobs: uses: docker/login-action@v3 with: registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + username: lukegus + password: ${{ secrets.GHCR_TOKEN }} - name: Login to Docker Hub if: github.event.inputs.registry == 'dockerhub' @@ -84,13 +84,12 @@ jobs: fi echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV - # Determine registry and image name if [ "${{ github.event.inputs.registry }}" == "dockerhub" ]; then echo "REGISTRY=docker.io" >> $GITHUB_ENV echo "IMAGE_NAME=bugattiguy527/termix" >> $GITHUB_ENV else echo "REGISTRY=ghcr.io" >> $GITHUB_ENV - echo "IMAGE_NAME=$REPO_OWNER/termix" >> $GITHUB_ENV + echo "IMAGE_NAME=lukegus/termix" >> $GITHUB_ENV fi - name: Build and Push Multi-Arch Docker Image @@ -109,20 +108,6 @@ jobs: BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 outputs: type=registry,compression=zstd,compression-level=19 - - name: Move cache - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache - - - name: Delete all untagged image versions - if: success() && github.event.inputs.registry != 'dockerhub' - uses: quartx-analytics/ghcr-cleaner@v1 - with: - owner-type: user - token: ${{ secrets.GHCR_TOKEN }} - repository-owner: ${{ github.repository_owner }} - delete-untagged: true - - name: Cleanup Docker Images Locally if: always() run: | From ba6a0bb5472833309906ae14a3b6badaa2c67756 Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:41:50 -0500 Subject: [PATCH 03/15] Update docker-image.yml --- .github/workflows/docker-image.yml | 52 ++++++++++++++---------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index aa42b2e6..66278bea 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,20 +1,22 @@ -name: Build and Push Docker Image +name: Build and Push Multi-Registry Docker Image on: workflow_dispatch: inputs: tag_name: - description: "Custom tag name for the Docker image" + description: "Custom tag name for the Docker image (comma-separated for multiple tags, e.g. latest,release-1.8.0)" required: false default: "" - registry: - description: "Docker registry to push to" + push_to_ghcr: + description: "Push to GitHub Container Registry" + type: boolean required: true - default: "ghcr" - type: choice - options: - - "ghcr" - - "dockerhub" + default: true + push_to_dockerhub: + description: "Push to Docker Hub" + type: boolean + required: true + default: true jobs: build: @@ -28,7 +30,7 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: - platforms: arm64 + platforms: arm64,arm/v7,arm/v6 - name: Setup Blacksmith Builder uses: useblacksmith/setup-docker-builder@v1 @@ -54,7 +56,7 @@ jobs: ${{ runner.os }}-buildx- - name: Login to GitHub Container Registry - if: github.event.inputs.registry != 'dockerhub' + if: ${{ inputs.push_to_ghcr }} uses: docker/login-action@v3 with: registry: ghcr.io @@ -62,35 +64,29 @@ jobs: password: ${{ secrets.GHCR_TOKEN }} - name: Login to Docker Hub - if: github.event.inputs.registry == 'dockerhub' + if: ${{ inputs.push_to_dockerhub }} uses: docker/login-action@v3 with: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine Docker image tag + - name: Determine Docker image tags + id: vars run: | REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') echo "REPO_OWNER=$REPO_OWNER" >> $GITHUB_ENV if [ "${{ github.event.inputs.tag_name }}" != "" ]; then - IMAGE_TAG="${{ github.event.inputs.tag_name }}" + IFS=',' read -ra TAGS <<< "${{ github.event.inputs.tag_name }}" elif [ "${{ github.ref }}" == "refs/heads/main" ]; then - IMAGE_TAG="latest" + TAGS=("latest") elif [ "${{ github.ref }}" == "refs/heads/development" ]; then - IMAGE_TAG="development-latest" + TAGS=("development-latest") else - IMAGE_TAG="${{ github.ref_name }}" + TAGS=("${{ github.ref_name }}") fi - echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV - if [ "${{ github.event.inputs.registry }}" == "dockerhub" ]; then - echo "REGISTRY=docker.io" >> $GITHUB_ENV - echo "IMAGE_NAME=bugattiguy527/termix" >> $GITHUB_ENV - else - echo "REGISTRY=ghcr.io" >> $GITHUB_ENV - echo "IMAGE_NAME=lukegus/termix" >> $GITHUB_ENV - fi + echo "TAGS=${TAGS[*]}" >> $GITHUB_ENV - name: Build and Push Multi-Arch Docker Image uses: useblacksmith/build-push-action@v2 @@ -98,8 +94,10 @@ jobs: context: . file: ./docker/Dockerfile push: true - platforms: linux/amd64,linux/arm64 - tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 + tags: | + ${{ inputs.push_to_ghcr && format('ghcr.io/lukegus/termix:{0}', join(fromJson('["' + replace(env.TAGS, " ", '","') + '"]'), '\nghcr.io/lukegus/termix:')) }} + ${{ inputs.push_to_dockerhub && format('docker.io/bugattiguy527/termix:{0}', join(fromJson('["' + replace(env.TAGS, " ", '","') + '"]'), '\ndocker.io/bugattiguy527/termix:')) }} labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} From d49c68896ceaa05450f38aa00b037f00f735ffdb Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:44:51 -0500 Subject: [PATCH 04/15] Refactor Docker image workflow for registry options Updated workflow to allow selection of Docker registry and simplified tag handling. --- .github/workflows/docker-image.yml | 52 +++++++++++++++++++----------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 66278bea..3372bfa6 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,4 +1,4 @@ -name: Build and Push Multi-Registry Docker Image +name: Build and Push Docker Image on: workflow_dispatch: @@ -7,16 +7,15 @@ on: description: "Custom tag name for the Docker image (comma-separated for multiple tags, e.g. latest,release-1.8.0)" required: false default: "" - push_to_ghcr: - description: "Push to GitHub Container Registry" - type: boolean + registry: + description: "Docker registry to push to" required: true - default: true - push_to_dockerhub: - description: "Push to Docker Hub" - type: boolean - required: true - default: true + default: "ghcr" + type: choice + options: + - "ghcr" + - "dockerhub" + - "both" jobs: build: @@ -56,7 +55,7 @@ jobs: ${{ runner.os }}-buildx- - name: Login to GitHub Container Registry - if: ${{ inputs.push_to_ghcr }} + if: ${{ github.event.inputs.registry != 'dockerhub' }} uses: docker/login-action@v3 with: registry: ghcr.io @@ -64,20 +63,26 @@ jobs: password: ${{ secrets.GHCR_TOKEN }} - name: Login to Docker Hub - if: ${{ inputs.push_to_dockerhub }} + if: ${{ github.event.inputs.registry != 'ghcr' }} uses: docker/login-action@v3 with: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine Docker image tags + - name: Determine Docker image tag(s) id: vars run: | REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') echo "REPO_OWNER=$REPO_OWNER" >> $GITHUB_ENV - if [ "${{ github.event.inputs.tag_name }}" != "" ]; then - IFS=',' read -ra TAGS <<< "${{ github.event.inputs.tag_name }}" + IFS=',' read -ra RAW_TAGS <<< "${{ github.event.inputs.tag_name }}" + TAGS=() + for t in "${RAW_TAGS[@]}"; do + tt="$(echo "$t" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + if [ -n "$tt" ]; then + TAGS+=("$tt") + fi + done elif [ "${{ github.ref }}" == "refs/heads/main" ]; then TAGS=("latest") elif [ "${{ github.ref }}" == "refs/heads/development" ]; then @@ -85,8 +90,19 @@ jobs: else TAGS=("${{ github.ref_name }}") fi - echo "TAGS=${TAGS[*]}" >> $GITHUB_ENV + echo "ALL_TAGS<> $GITHUB_ENV + for tag in "${TAGS[@]}"; do + if [ "${{ github.event.inputs.registry }}" == "ghcr" ]; then + echo "ghcr.io/lukegus/termix:${tag}" + elif [ "${{ github.event.inputs.registry }}" == "dockerhub" ]; then + echo "docker.io/bugattiguy527/termix:${tag}" + else + echo "ghcr.io/lukegus/termix:${tag}" + echo "docker.io/bugattiguy527/termix:${tag}" + fi + done + echo "EOF" >> $GITHUB_ENV - name: Build and Push Multi-Arch Docker Image uses: useblacksmith/build-push-action@v2 @@ -95,9 +111,7 @@ jobs: file: ./docker/Dockerfile push: true platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - tags: | - ${{ inputs.push_to_ghcr && format('ghcr.io/lukegus/termix:{0}', join(fromJson('["' + replace(env.TAGS, " ", '","') + '"]'), '\nghcr.io/lukegus/termix:')) }} - ${{ inputs.push_to_dockerhub && format('docker.io/bugattiguy527/termix:{0}', join(fromJson('["' + replace(env.TAGS, " ", '","') + '"]'), '\ndocker.io/bugattiguy527/termix:')) }} + tags: ${{ env.ALL_TAGS }} labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} From 19aaeff3ffae84bd615614ddc303981bc990a1a8 Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:48:13 -0500 Subject: [PATCH 05/15] Update Docker login conditions and tag handling --- .github/workflows/docker-image.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 3372bfa6..35a17f6a 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -55,7 +55,7 @@ jobs: ${{ runner.os }}-buildx- - name: Login to GitHub Container Registry - if: ${{ github.event.inputs.registry != 'dockerhub' }} + if: ${{ github.event.inputs.registry == 'ghcr' || github.event.inputs.registry == 'both' }} uses: docker/login-action@v3 with: registry: ghcr.io @@ -63,17 +63,15 @@ jobs: password: ${{ secrets.GHCR_TOKEN }} - name: Login to Docker Hub - if: ${{ github.event.inputs.registry != 'ghcr' }} + if: ${{ github.event.inputs.registry == 'dockerhub' || github.event.inputs.registry == 'both' }} uses: docker/login-action@v3 with: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine Docker image tag(s) + - name: Determine Docker image tags id: vars run: | - REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') - echo "REPO_OWNER=$REPO_OWNER" >> $GITHUB_ENV if [ "${{ github.event.inputs.tag_name }}" != "" ]; then IFS=',' read -ra RAW_TAGS <<< "${{ github.event.inputs.tag_name }}" TAGS=() @@ -90,19 +88,19 @@ jobs: else TAGS=("${{ github.ref_name }}") fi - echo "TAGS=${TAGS[*]}" >> $GITHUB_ENV - echo "ALL_TAGS<> $GITHUB_ENV + + TAG_STR="" for tag in "${TAGS[@]}"; do if [ "${{ github.event.inputs.registry }}" == "ghcr" ]; then - echo "ghcr.io/lukegus/termix:${tag}" + TAG_STR+="ghcr.io/lukegus/termix:${tag} " elif [ "${{ github.event.inputs.registry }}" == "dockerhub" ]; then - echo "docker.io/bugattiguy527/termix:${tag}" + TAG_STR+="docker.io/bugattiguy527/termix:${tag} " else - echo "ghcr.io/lukegus/termix:${tag}" - echo "docker.io/bugattiguy527/termix:${tag}" + TAG_STR+="ghcr.io/lukegus/termix:${tag} docker.io/bugattiguy527/termix:${tag} " fi done - echo "EOF" >> $GITHUB_ENV + echo "ALL_TAGS=$TAG_STR" >> $GITHUB_ENV + echo "TAGS: $TAG_STR" - name: Build and Push Multi-Arch Docker Image uses: useblacksmith/build-push-action@v2 From 26ba8945da78b2d070bff7d246e1ecb874816bfa Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:55:05 -0500 Subject: [PATCH 06/15] Enhance Docker image workflow with better tagging Updated Docker image workflow to improve tag handling and descriptions. --- .github/workflows/docker-image.yml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 35a17f6a..db943e3d 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: tag_name: - description: "Custom tag name for the Docker image (comma-separated for multiple tags, e.g. latest,release-1.8.0)" + description: "Custom tag names (comma-separated, e.g. latest,release-1.8.0,release-1.8.1)" required: false default: "" registry: @@ -69,9 +69,10 @@ jobs: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine Docker image tags + - name: Determine Docker image tag(s) id: vars run: | + # Determine tags array from workflow input or branch if [ "${{ github.event.inputs.tag_name }}" != "" ]; then IFS=',' read -ra RAW_TAGS <<< "${{ github.event.inputs.tag_name }}" TAGS=() @@ -89,18 +90,23 @@ jobs: TAGS=("${{ github.ref_name }}") fi - TAG_STR="" + # Build the final tag list for Docker Buildx (multi-line) + ALL_TAGS="" for tag in "${TAGS[@]}"; do if [ "${{ github.event.inputs.registry }}" == "ghcr" ]; then - TAG_STR+="ghcr.io/lukegus/termix:${tag} " + ALL_TAGS+=$'\n'"ghcr.io/lukegus/termix:${tag}" elif [ "${{ github.event.inputs.registry }}" == "dockerhub" ]; then - TAG_STR+="docker.io/bugattiguy527/termix:${tag} " + ALL_TAGS+=$'\n'"docker.io/bugattiguy527/termix:${tag}" else - TAG_STR+="ghcr.io/lukegus/termix:${tag} docker.io/bugattiguy527/termix:${tag} " + ALL_TAGS+=$'\n'"ghcr.io/lukegus/termix:${tag}" + ALL_TAGS+=$'\n'"docker.io/bugattiguy527/termix:${tag}" fi done - echo "ALL_TAGS=$TAG_STR" >> $GITHUB_ENV - echo "TAGS: $TAG_STR" + # Trim leading newline + ALL_TAGS="${ALL_TAGS#$'\n'}" + echo "ALL_TAGS=$ALL_TAGS" >> $GITHUB_ENV + echo "ALL_TAGS:" + echo "$ALL_TAGS" - name: Build and Push Multi-Arch Docker Image uses: useblacksmith/build-push-action@v2 From 8845521ef7d7127e48afb0726625efdc63b47200 Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 15:59:27 -0500 Subject: [PATCH 07/15] Update Docker workflow for tag handling and cleanup --- .github/workflows/docker-image.yml | 60 +++++++++++------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index db943e3d..dd5f8d84 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -4,13 +4,13 @@ on: workflow_dispatch: inputs: tag_name: - description: "Custom tag names (comma-separated, e.g. latest,release-1.8.0,release-1.8.1)" + description: "Comma-separated list of tags, e.g. latest,release-1.8.0,release-1.8.1" required: false default: "" registry: description: "Docker registry to push to" required: true - default: "ghcr" + default: "both" type: choice options: - "ghcr" @@ -21,21 +21,17 @@ jobs: build: runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - - name: Checkout repository - uses: actions/checkout@v5 + - uses: actions/checkout@v5 with: fetch-depth: 1 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-qemu-action@v3 with: platforms: arm64,arm/v7,arm/v6 - - name: Setup Blacksmith Builder - uses: useblacksmith/setup-docker-builder@v1 + - uses: useblacksmith/setup-docker-builder@v1 - - name: Cache npm dependencies - uses: actions/cache@v4 + - uses: actions/cache@v4 with: path: | ~/.npm @@ -45,8 +41,7 @@ jobs: restore-keys: | ${{ runner.os }}-node- - - name: Cache Docker layers - uses: actions/cache@v4 + - uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.ref_name }}-${{ hashFiles('docker/Dockerfile') }} @@ -54,25 +49,22 @@ jobs: ${{ runner.os }}-buildx-${{ github.ref_name }}- ${{ runner.os }}-buildx- - - name: Login to GitHub Container Registry + - uses: docker/login-action@v3 if: ${{ github.event.inputs.registry == 'ghcr' || github.event.inputs.registry == 'both' }} - uses: docker/login-action@v3 with: registry: ghcr.io username: lukegus password: ${{ secrets.GHCR_TOKEN }} - - name: Login to Docker Hub + - uses: docker/login-action@v3 if: ${{ github.event.inputs.registry == 'dockerhub' || github.event.inputs.registry == 'both' }} - uses: docker/login-action@v3 with: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine Docker image tag(s) + - name: Determine Docker image tags id: vars run: | - # Determine tags array from workflow input or branch if [ "${{ github.event.inputs.tag_name }}" != "" ]; then IFS=',' read -ra RAW_TAGS <<< "${{ github.event.inputs.tag_name }}" TAGS=() @@ -89,33 +81,26 @@ jobs: else TAGS=("${{ github.ref_name }}") fi - - # Build the final tag list for Docker Buildx (multi-line) - ALL_TAGS="" + TAG_ARRAY=() for tag in "${TAGS[@]}"; do - if [ "${{ github.event.inputs.registry }}" == "ghcr" ]; then - ALL_TAGS+=$'\n'"ghcr.io/lukegus/termix:${tag}" - elif [ "${{ github.event.inputs.registry }}" == "dockerhub" ]; then - ALL_TAGS+=$'\n'"docker.io/bugattiguy527/termix:${tag}" + if [ "${{ github.event.inputs.registry }}" = "ghcr" ]; then + TAG_ARRAY+=("ghcr.io/lukegus/termix:${tag}") + elif [ "${{ github.event.inputs.registry }}" = "dockerhub" ]; then + TAG_ARRAY+=("docker.io/bugattiguy527/termix:${tag}") else - ALL_TAGS+=$'\n'"ghcr.io/lukegus/termix:${tag}" - ALL_TAGS+=$'\n'"docker.io/bugattiguy527/termix:${tag}" + TAG_ARRAY+=("ghcr.io/lukegus/termix:${tag}") + TAG_ARRAY+=("docker.io/bugattiguy527/termix:${tag}") fi done - # Trim leading newline - ALL_TAGS="${ALL_TAGS#$'\n'}" - echo "ALL_TAGS=$ALL_TAGS" >> $GITHUB_ENV - echo "ALL_TAGS:" - echo "$ALL_TAGS" + echo "tags=$(printf '%s\n' "${TAG_ARRAY[@]}" | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT - - name: Build and Push Multi-Arch Docker Image - uses: useblacksmith/build-push-action@v2 + - uses: useblacksmith/build-push-action@v2 with: context: . file: ./docker/Dockerfile push: true platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - tags: ${{ env.ALL_TAGS }} + tags: ${{ fromJSON(steps.vars.outputs.tags) }} labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} @@ -124,8 +109,7 @@ jobs: BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 outputs: type=registry,compression=zstd,compression-level=19 - - name: Cleanup Docker Images Locally - if: always() - run: | + - run: | docker image prune -af docker system prune -af --volumes + if: always() From 4f91984032251050a264c550fa2ce9d4daed4dad Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:03:28 -0500 Subject: [PATCH 08/15] Update docker-image.yml --- .github/workflows/docker-image.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index dd5f8d84..1c2715cc 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -81,6 +81,7 @@ jobs: else TAGS=("${{ github.ref_name }}") fi + TAG_ARRAY=() for tag in "${TAGS[@]}"; do if [ "${{ github.event.inputs.registry }}" = "ghcr" ]; then @@ -92,7 +93,9 @@ jobs: TAG_ARRAY+=("docker.io/bugattiguy527/termix:${tag}") fi done - echo "tags=$(printf '%s\n' "${TAG_ARRAY[@]}" | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT + + JSON_TAGS=$(printf '%s\n' "${TAG_ARRAY[@]}" | jq -R -s -c 'split("\n")[:-1]') + echo "tags=$JSON_TAGS" >> $GITHUB_OUTPUT - uses: useblacksmith/build-push-action@v2 with: From 1a37888a4e8394d9749b222f6f651053f3d5d3e3 Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:07:55 -0500 Subject: [PATCH 09/15] Update Docker workflow inputs and tag logic Refactor Docker workflow to include version and production inputs, and streamline tag determination. --- .github/workflows/docker-image.yml | 81 +++++++----------------------- 1 file changed, 17 insertions(+), 64 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1c2715cc..c9a747ac 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -3,19 +3,13 @@ name: Build and Push Docker Image on: workflow_dispatch: inputs: - tag_name: - description: "Comma-separated list of tags, e.g. latest,release-1.8.0,release-1.8.1" - required: false - default: "" - registry: - description: "Docker registry to push to" + version: + description: "Version to build, e.g. 1.8.0" required: true - default: "both" - type: choice - options: - - "ghcr" - - "dockerhub" - - "both" + production: + description: "Set true for prod build, false for dev build" + required: true + default: false jobs: build: @@ -31,71 +25,30 @@ jobs: - uses: useblacksmith/setup-docker-builder@v1 - - uses: actions/cache@v4 - with: - path: | - ~/.npm - node_modules - */*/node_modules - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.ref_name }}-${{ hashFiles('docker/Dockerfile') }} - restore-keys: | - ${{ runner.os }}-buildx-${{ github.ref_name }}- - ${{ runner.os }}-buildx- - - uses: docker/login-action@v3 - if: ${{ github.event.inputs.registry == 'ghcr' || github.event.inputs.registry == 'both' }} with: registry: ghcr.io username: lukegus password: ${{ secrets.GHCR_TOKEN }} - uses: docker/login-action@v3 - if: ${{ github.event.inputs.registry == 'dockerhub' || github.event.inputs.registry == 'both' }} + if: ${{ github.event.inputs.production == 'true' }} with: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine Docker image tags - id: vars + - name: Determine Docker tags + id: tags run: | - if [ "${{ github.event.inputs.tag_name }}" != "" ]; then - IFS=',' read -ra RAW_TAGS <<< "${{ github.event.inputs.tag_name }}" - TAGS=() - for t in "${RAW_TAGS[@]}"; do - tt="$(echo "$t" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - if [ -n "$tt" ]; then - TAGS+=("$tt") - fi - done - elif [ "${{ github.ref }}" == "refs/heads/main" ]; then - TAGS=("latest") - elif [ "${{ github.ref }}" == "refs/heads/development" ]; then - TAGS=("development-latest") + VERSION="${{ github.event.inputs.version }}" + PRODUCTION="${{ github.event.inputs.production }}" + if [ "$PRODUCTION" == "true" ]; then + TAGS="ghcr.io/lukegus/termix:release-$VERSION ghcr.io/lukegus/termix:latest docker.io/bugattiguy527/termix:release-$VERSION docker.io/bugattiguy527/termix:latest" else - TAGS=("${{ github.ref_name }}") + TAGS="ghcr.io/lukegus/termix:dev-$VERSION" fi - - TAG_ARRAY=() - for tag in "${TAGS[@]}"; do - if [ "${{ github.event.inputs.registry }}" = "ghcr" ]; then - TAG_ARRAY+=("ghcr.io/lukegus/termix:${tag}") - elif [ "${{ github.event.inputs.registry }}" = "dockerhub" ]; then - TAG_ARRAY+=("docker.io/bugattiguy527/termix:${tag}") - else - TAG_ARRAY+=("ghcr.io/lukegus/termix:${tag}") - TAG_ARRAY+=("docker.io/bugattiguy527/termix:${tag}") - fi - done - - JSON_TAGS=$(printf '%s\n' "${TAG_ARRAY[@]}" | jq -R -s -c 'split("\n")[:-1]') - echo "tags=$JSON_TAGS" >> $GITHUB_OUTPUT + echo "TAGS=$TAGS" >> $GITHUB_ENV + echo "Docker tags: $TAGS" - uses: useblacksmith/build-push-action@v2 with: @@ -103,7 +56,7 @@ jobs: file: ./docker/Dockerfile push: true platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - tags: ${{ fromJSON(steps.vars.outputs.tags) }} + tags: ${{ env.TAGS }} labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} From 9bbff4aa4a9f86f0cd4a0a99eb4f4b28a3171993 Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:13:17 -0500 Subject: [PATCH 10/15] Update Docker image workflow for multi-platform builds --- .github/workflows/docker-image.yml | 39 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index c9a747ac..f8fd51eb 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -21,7 +21,7 @@ jobs: - uses: docker/setup-qemu-action@v3 with: - platforms: arm64,arm/v7,arm/v6 + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - uses: useblacksmith/setup-docker-builder@v1 @@ -37,26 +37,33 @@ jobs: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine Docker tags - id: tags - run: | - VERSION="${{ github.event.inputs.version }}" - PRODUCTION="${{ github.event.inputs.production }}" - if [ "$PRODUCTION" == "true" ]; then - TAGS="ghcr.io/lukegus/termix:release-$VERSION ghcr.io/lukegus/termix:latest docker.io/bugattiguy527/termix:release-$VERSION docker.io/bugattiguy527/termix:latest" - else - TAGS="ghcr.io/lukegus/termix:dev-$VERSION" - fi - echo "TAGS=$TAGS" >> $GITHUB_ENV - echo "Docker tags: $TAGS" - - - uses: useblacksmith/build-push-action@v2 + - name: Build and Push to GHCR + uses: useblacksmith/build-push-action@v2 with: context: . file: ./docker/Dockerfile push: true platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - tags: ${{ env.TAGS }} + tags: ${{ github.event.inputs.production == 'true' && format('ghcr.io/lukegus/termix:release-{0} ghcr.io/lukegus/termix:latest', github.event.inputs.version) || format('ghcr.io/lukegus/termix:dev-{0}', github.event.inputs.version) }} + labels: | + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + build-args: | + BUILDKIT_INLINE_CACHE=1 + BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 + outputs: type=registry,compression=zstd,compression-level=19 + + - name: Build and Push to Docker Hub + if: ${{ github.event.inputs.production == 'true' }} + uses: useblacksmith/build-push-action@v2 + with: + context: . + file: ./docker/Dockerfile + push: true + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 + tags: | + docker.io/bugattiguy527/termix:release-${{ github.event.inputs.version }} + docker.io/bugattiguy527/termix:latest labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} From f6f42cf937872c091164f64759ddc1a29c17bb4c Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:17:57 -0500 Subject: [PATCH 11/15] Refactor Docker image tags for clarity Updated Docker image tags to use multi-line syntax for better readability and added latest tag conditionally. --- .github/workflows/docker-image.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index f8fd51eb..a616abc4 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -44,7 +44,9 @@ jobs: file: ./docker/Dockerfile push: true platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - tags: ${{ github.event.inputs.production == 'true' && format('ghcr.io/lukegus/termix:release-{0} ghcr.io/lukegus/termix:latest', github.event.inputs.version) || format('ghcr.io/lukegus/termix:dev-{0}', github.event.inputs.version) }} + tags: | + ${{ github.event.inputs.production == 'true' && format('ghcr.io/lukegus/termix:release-{0}', github.event.inputs.version) || format('ghcr.io/lukegus/termix:dev-{0}', github.event.inputs.version) }} + ${{ github.event.inputs.production == 'true' && 'ghcr.io/lukegus/termix:latest' || '' }} labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} From e969672350f912402ffee72f060e0eea58baab0e Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:21:35 -0500 Subject: [PATCH 12/15] Fix typo in exposed ports in Dockerfile --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 65fdce1b..555114e6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,7 +82,7 @@ COPY --chown=node:node package.json ./ VOLUME ["/app/data"] -EXPOSE ${PORT} 30001 30002 30003 30004 30005 300006 +EXPOSE ${PORT} 30001 30002 30003 30004 30005 30006 COPY docker/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh From f2102e6fe20c76b9ca392fd2ecd1d8e2850cbb25 Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:09:58 -0500 Subject: [PATCH 13/15] Update docker-image.yml --- .github/workflows/docker-image.yml | 101 ++++++++++++++++++----------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index a616abc4..c01dd0c0 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -4,77 +4,104 @@ on: workflow_dispatch: inputs: version: - description: "Version to build, e.g. 1.8.0" + description: "Version to build (e.g., 1.8.0)" required: true production: - description: "Set true for prod build, false for dev build" + description: "Is this a production build?" required: true default: false + type: boolean + registry: + description: "Docker registry to push to" + required: true + default: "both" + type: choice + options: + - "ghcr" + - "dockerhub" + - "both" jobs: build: - runs-on: blacksmith-4vcpu-ubuntu-2404 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - name: Checkout repository + uses: actions/checkout@v5 with: fetch-depth: 1 - - uses: docker/setup-qemu-action@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 with: - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 + platforms: linux/amd64,linux/arm64,linux/arm/v7 - - uses: useblacksmith/setup-docker-builder@v1 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 - - uses: docker/login-action@v3 + - name: Determine tags + id: tags + run: | + VERSION=${{ github.event.inputs.version }} + PROD=${{ github.event.inputs.production }} + REGISTRY=${{ github.event.inputs.registry }} + + TAGS=() + if [ "$PROD" = "true" ]; then + TAGS+=("release-$VERSION") + TAGS+=("latest") + else + TAGS+=("dev-$VERSION") + fi + + ALL_TAGS=() + for tag in "${TAGS[@]}"; do + if [ "$REGISTRY" = "ghcr" ] || [ "$REGISTRY" = "both" ]; then + ALL_TAGS+=("ghcr.io/lukegus/termix:$tag") + fi + if [ "$REGISTRY" = "dockerhub" ] || [ "$REGISTRY" = "both" ]; then + if [ "$PROD" = "true" ]; then + ALL_TAGS+=("docker.io/bugattiguy527/termix:$tag") + fi + fi + done + + echo "ALL_TAGS=${ALL_TAGS[*]}" >> $GITHUB_ENV + echo "All tags to build:" + printf '%s\n' "${ALL_TAGS[@]}" + + - name: Login to GHCR + if: ${{ github.event.inputs.registry == 'ghcr' || github.event.inputs.registry == 'both' }} + uses: docker/login-action@v3 with: registry: ghcr.io username: lukegus password: ${{ secrets.GHCR_TOKEN }} - - uses: docker/login-action@v3 - if: ${{ github.event.inputs.production == 'true' }} + - name: Login to Docker Hub + if: ${{ github.event.inputs.registry == 'dockerhub' || github.event.inputs.registry == 'both' }} + uses: docker/login-action@v3 with: username: bugattiguy527 password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and Push to GHCR - uses: useblacksmith/build-push-action@v2 + - name: Build and push multi-arch image + uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile push: true - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - tags: | - ${{ github.event.inputs.production == 'true' && format('ghcr.io/lukegus/termix:release-{0}', github.event.inputs.version) || format('ghcr.io/lukegus/termix:dev-{0}', github.event.inputs.version) }} - ${{ github.event.inputs.production == 'true' && 'ghcr.io/lukegus/termix:latest' || '' }} - labels: | - org.opencontainers.image.source=https://github.com/${{ github.repository }} - org.opencontainers.image.revision=${{ github.sha }} + platforms: linux/amd64,linux/arm64,linux/arm/v7 + tags: ${{ env.ALL_TAGS }} build-args: | BUILDKIT_INLINE_CACHE=1 BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 - outputs: type=registry,compression=zstd,compression-level=19 - - - name: Build and Push to Docker Hub - if: ${{ github.event.inputs.production == 'true' }} - uses: useblacksmith/build-push-action@v2 - with: - context: . - file: ./docker/Dockerfile - push: true - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 - tags: | - docker.io/bugattiguy527/termix:release-${{ github.event.inputs.version }} - docker.io/bugattiguy527/termix:latest labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} - build-args: | - BUILDKIT_INLINE_CACHE=1 - BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 outputs: type=registry,compression=zstd,compression-level=19 - - run: | + - name: Cleanup Docker + if: always() + run: | docker image prune -af docker system prune -af --volumes - if: always() From 19ab2011d96bd9d547a0f2c1409f0a51bb8389cf Mon Sep 17 00:00:00 2001 From: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:13:20 -0500 Subject: [PATCH 14/15] Refactor Docker image workflow for registry handling Removed registry input and adjusted Docker Hub login condition. --- .github/workflows/docker-image.yml | 45 +++++++++++------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index c01dd0c0..80ab5da6 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -11,15 +11,6 @@ on: required: true default: false type: boolean - registry: - description: "Docker registry to push to" - required: true - default: "both" - type: choice - options: - - "ghcr" - - "dockerhub" - - "both" jobs: build: @@ -43,42 +34,38 @@ jobs: run: | VERSION=${{ github.event.inputs.version }} PROD=${{ github.event.inputs.production }} - REGISTRY=${{ github.event.inputs.registry }} TAGS=() - if [ "$PROD" = "true" ]; then - TAGS+=("release-$VERSION") - TAGS+=("latest") - else - TAGS+=("dev-$VERSION") - fi - ALL_TAGS=() - for tag in "${TAGS[@]}"; do - if [ "$REGISTRY" = "ghcr" ] || [ "$REGISTRY" = "both" ]; then + + if [ "$PROD" = "true" ]; then + # Production build → push release + latest to both GHCR and Docker Hub + TAGS+=("release-$VERSION" "latest") + for tag in "${TAGS[@]}"; do ALL_TAGS+=("ghcr.io/lukegus/termix:$tag") - fi - if [ "$REGISTRY" = "dockerhub" ] || [ "$REGISTRY" = "both" ]; then - if [ "$PROD" = "true" ]; then - ALL_TAGS+=("docker.io/bugattiguy527/termix:$tag") - fi - fi - done + ALL_TAGS+=("docker.io/bugattiguy527/termix:$tag") + done + else + # Dev build → push only dev-x.x.x to GHCR + TAGS+=("dev-$VERSION") + for tag in "${TAGS[@]}"; do + ALL_TAGS+=("ghcr.io/lukegus/termix:$tag") + done + fi echo "ALL_TAGS=${ALL_TAGS[*]}" >> $GITHUB_ENV echo "All tags to build:" printf '%s\n' "${ALL_TAGS[@]}" - name: Login to GHCR - if: ${{ github.event.inputs.registry == 'ghcr' || github.event.inputs.registry == 'both' }} uses: docker/login-action@v3 with: registry: ghcr.io username: lukegus password: ${{ secrets.GHCR_TOKEN }} - - name: Login to Docker Hub - if: ${{ github.event.inputs.registry == 'dockerhub' || github.event.inputs.registry == 'both' }} + - name: Login to Docker Hub (prod only) + if: ${{ github.event.inputs.production == 'true' }} uses: docker/login-action@v3 with: username: bugattiguy527 From 232d6fe6ea074aea6b0517319346bdff9c6ec617 Mon Sep 17 00:00:00 2001 From: Nikola Novoselec <12149536+nikolanovoselec@users.noreply.github.com> Date: Tue, 21 Oct 2025 23:24:57 +0200 Subject: [PATCH 15/15] Handle OIDC users during database import (#424) * Update Docker image name for GitHub registry * Fix image name casing in Docker workflow * Remove untagged image cleanup step from workflow Removed the step to delete untagged image versions from the workflow. * Change Docker login to use GHCR credentials Updated Docker login credentials for GitHub Container Registry. * Remove cache moving step from Docker workflow Removed the step to move the build cache in the Docker workflow. * Refactor Docker image workflow for versioning and builds * Update docker-image.yml * Allow OIDC users to import database without password * Skip import password prompt for OIDC users * docs: clarify OIDC import unlocking flow * docs: explain admin import password logic --------- Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> Co-authored-by: Nikola Novoselec --- src/backend/database/database.ts | 49 ++++++++++++++++++++------ src/ui/Desktop/Admin/AdminSettings.tsx | 46 +++++++++++++++++++++--- 2 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/backend/database/database.ts b/src/backend/database/database.ts index 4ed59561..bdfe0f03 100644 --- a/src/backend/database/database.ts +++ b/src/backend/database/database.ts @@ -901,17 +901,40 @@ app.post( const userId = (req as AuthenticatedRequest).userId; const { password } = req.body; + const mainDb = getDb(); - if (!password) { - return res.status(400).json({ - error: "Password required for import", - code: "PASSWORD_REQUIRED", - }); + const userRecords = await mainDb + .select() + .from(users) + .where(eq(users.id, userId)); + + if (!userRecords || userRecords.length === 0) { + return res.status(404).json({ error: "User not found" }); } - const unlocked = await authManager.authenticateUser(userId, password); - if (!unlocked) { - return res.status(401).json({ error: "Invalid password" }); + const isOidcUser = !!userRecords[0].is_oidc; + + if (!isOidcUser) { + // Local accounts still prove knowledge of the password so their DEK can be derived again. + if (!password) { + return res.status(400).json({ + error: "Password required for import", + code: "PASSWORD_REQUIRED", + }); + } + + const unlocked = await authManager.authenticateUser(userId, password); + if (!unlocked) { + return res.status(401).json({ error: "Invalid password" }); + } + } else if (!DataCrypto.getUserDataKey(userId)) { + // OIDC users skip the password prompt; make sure their DEK is unlocked via the OIDC session. + const oidcUnlocked = await authManager.authenticateOIDCUser(userId); + if (!oidcUnlocked) { + return res.status(403).json({ + error: "Failed to unlock user data with SSO credentials", + }); + } } apiLogger.info("Importing SQLite data", { @@ -922,7 +945,14 @@ app.post( mimetype: req.file.mimetype, }); - const userDataKey = DataCrypto.getUserDataKey(userId); + let userDataKey = DataCrypto.getUserDataKey(userId); + if (!userDataKey && isOidcUser) { + // authenticateOIDCUser lazily provisions the session key; retry the fetch when it succeeds. + const oidcUnlocked = await authManager.authenticateOIDCUser(userId); + if (oidcUnlocked) { + userDataKey = DataCrypto.getUserDataKey(userId); + } + } if (!userDataKey) { throw new Error("User data not unlocked"); } @@ -976,7 +1006,6 @@ app.post( }; try { - const mainDb = getDb(); try { const importedHosts = importDb diff --git a/src/ui/Desktop/Admin/AdminSettings.tsx b/src/ui/Desktop/Admin/AdminSettings.tsx index 0d829895..6d0ff0f8 100644 --- a/src/ui/Desktop/Admin/AdminSettings.tsx +++ b/src/ui/Desktop/Admin/AdminSettings.tsx @@ -45,6 +45,8 @@ import { makeUserAdmin, removeAdminStatus, deleteUser, + getUserInfo, + getCookie, isElectron, } from "@/ui/main-axios.ts"; @@ -94,6 +96,14 @@ export function AdminSettings({ null, ); + const [securityInitialized, setSecurityInitialized] = React.useState(true); + const [currentUser, setCurrentUser] = React.useState<{ + id: string; + username: string; + is_admin: boolean; + is_oidc: boolean; + } | null>(null); + const [exportLoading, setExportLoading] = React.useState(false); const [importLoading, setImportLoading] = React.useState(false); const [importFile, setImportFile] = React.useState(null); @@ -101,6 +111,11 @@ export function AdminSettings({ const [showPasswordInput, setShowPasswordInput] = React.useState(false); const [importPassword, setImportPassword] = React.useState(""); + const requiresImportPassword = React.useMemo( + () => !currentUser?.is_oidc, + [currentUser?.is_oidc], + ); + React.useEffect(() => { if (isElectron()) { const serverUrl = (window as { configuredServerUrl?: string }) @@ -119,6 +134,23 @@ export function AdminSettings({ toast.error(t("admin.failedToFetchOidcConfig")); } }); + // Capture the current session so we know whether to ask for a password later. + getUserInfo() + .then((info) => { + if (info) { + setCurrentUser({ + id: info.userId, + username: info.username, + is_admin: info.is_admin, + is_oidc: info.is_oidc, + }); + } + }) + .catch((err) => { + if (!err?.message?.includes("No server configured")) { + console.warn("Failed to fetch current user info", err); + } + }); fetchUsers(); }, []); @@ -372,7 +404,7 @@ export function AdminSettings({ return; } - if (!importPassword.trim()) { + if (requiresImportPassword && !importPassword.trim()) { toast.error(t("admin.passwordRequired")); return; } @@ -395,7 +427,10 @@ export function AdminSettings({ const formData = new FormData(); formData.append("file", importFile); - formData.append("password", importPassword); + if (requiresImportPassword) { + // Preserve the existing password flow for non-OIDC accounts. + formData.append("password", importPassword); + } const response = await fetch(apiUrl, { method: "POST", @@ -1016,7 +1051,8 @@ export function AdminSettings({ - {importFile && ( + {/* Only render the password field when a local account is performing the import. */} + {importFile && requiresImportPassword && (