From dfd5f8dd2d28b65ff9c2db437a10a0a0d1547ecf Mon Sep 17 00:00:00 2001 From: rock Date: Thu, 24 Sep 2026 21:24:30 +0800 Subject: [PATCH] tmp --- k8s/apps/paperless/paperless-ai-new.yaml | 127 +++++++++++++++++++++++ scripts/iam/provision-rbac.py | 20 ++++ 2 files changed, 147 insertions(+) create mode 100644 k8s/apps/paperless/paperless-ai-new.yaml diff --git a/k8s/apps/paperless/paperless-ai-new.yaml b/k8s/apps/paperless/paperless-ai-new.yaml new file mode 100644 index 0000000..041d2f2 --- /dev/null +++ b/k8s/apps/paperless/paperless-ai-new.yaml @@ -0,0 +1,127 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: paperless-ai-data + namespace: paperless +spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 2Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: paperless-ai + namespace: paperless + labels: + app.kubernetes.io/name: paperless-ai +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: paperless-ai + template: + metadata: + labels: + app.kubernetes.io/name: paperless-ai + spec: + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + + volumes: + - name: paperless-ai-data + persistentVolumeClaim: + claimName: paperless-ai-data + + initContainers: + - name: fetch-llm-token + image: curlimages/curl:8.12.0 + command: + - sh + - -c + - | + set -e + echo "[init] Fetching JWT token from Authentik for paperless-ai-agent..." + + # Get JWT token via client_credentials grant + TOKEN_RESPONSE=$(curl -s -X POST https://authentik.riotpiao.com/application/o/token/ \ + -d "grant_type=client_credentials" \ + -d "client_id=paperless-ai-agent" \ + -d "client_secret=29skAhE0tBzWn0qrPvmPTQMva0joFjcCr7msDQr_VXQ" \ + -d "scope=openid llm:inference" 2>/dev/null) + + # Extract token + TOKEN=$(echo "$TOKEN_RESPONSE" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4) + + if [ -z "$TOKEN" ]; then + echo "[error] Failed to get token. Response: $TOKEN_RESPONSE" + exit 1 + fi + + # Store token in file for main container to read + mkdir -p /data + echo "$TOKEN" > /data/llm_token.txt + echo "[init] Token fetched and stored successfully" + volumeMounts: + - name: paperless-ai-data + mountPath: /data + + containers: + - name: paperless-ai + image: clusterzx/paperless-ai:latest + imagePullPolicy: Always + env: + # Paperless-ngx connection + - name: PAPERLESS_API_URL + value: "http://paperless.paperless.svc.cluster.local:8000" + - name: PAPERLESS_API_TOKEN + valueFrom: + secretKeyRef: + name: paperless-ai-config + key: PAPERLESS_API_TOKEN + - name: PAPERLESS_USERNAME + value: "admin" + + # LLM API configuration + - name: AI_PROVIDER + value: "custom" + - name: CUSTOM_BASE_URL + value: "https://api.riotpiao.com/v1" + # Token file created by init container with JWT from Authentik + - name: CUSTOM_API_KEY_FILE + value: "/app/data/llm_token.txt" + - name: CUSTOM_MODEL + value: "qwen2.5:3b-instruct" + + # Scanning behavior + - name: SCAN_INTERVAL + value: "60" + - name: PROCESS_PREDEFINED_DOCUMENTS + value: "no" + + # Tagging configuration + - name: ADD_AI_TAG + value: "yes" + - name: AI_TAG_NAME + value: "ai-processed" + - name: ADD_AI_DESCRIPTIONS + value: "yes" + - name: USE_PROMPT_TAGS + value: "yes" + + volumeMounts: + - name: paperless-ai-data + mountPath: /app/data + + resources: + requests: + cpu: 100m + memory: 512Mi + limits: + cpu: "1" + memory: 2Gi diff --git a/scripts/iam/provision-rbac.py b/scripts/iam/provision-rbac.py index 98fb231..3615e8d 100644 --- a/scripts/iam/provision-rbac.py +++ b/scripts/iam/provision-rbac.py @@ -145,6 +145,7 @@ SERVICE_ACCOUNTS: Dict[str, Dict[str, Any]] = { SCOPE_MAPPINGS: Dict[str, Dict[str, str]] = { "roles": {"expression": 'return user.attributes.get("roles", [])'}, "permissions": {"expression": 'return ["*"] if any(user.groups.filter(is_superuser=True)) else list(user.groups.values_list("name", flat=True))'}, + "groups": {"expression": 'return {"groups": list(user.groups.values_list("name", flat=True))}'}, "minio_buckets": {"expression": 'return user.attributes.get("minio_buckets", [])'}, "paperless_doctypes": {"expression": 'return user.attributes.get("paperless_doctypes", [])'}, "memory_projects": {"expression": 'return user.attributes.get("memory_projects", [])'}, @@ -246,6 +247,25 @@ for scope_name, scope_spec in SCOPE_MAPPINGS.items(): else: print(f" {scope_name}: FAILED {status} {res}") +# Fix built-in 'openid' scope to include preferred_username (needed by Grafana, Forgejo, etc.) +# Note: sub MUST be a string, not int, for JSON compatibility with Go struct unmarshaling +if "openid" in existing_scopes: + openid_scope = existing_scopes["openid"] + openid_pk = openid_scope["pk"] + openid_expression = 'return {"sub": str(user.pk), "preferred_username": user.username}' + if openid_expression not in openid_scope.get("expression", ""): + status, res = api("PUT", f"/api/v3/propertymappings/provider/scope/{openid_pk}/", { + "name": openid_scope["name"], + "scope_name": "openid", + "expression": openid_expression, + }) + if status in (200, 201): + print(f" openid: updated (sub as string + preferred_username)") + else: + print(f" openid: UPDATE FAILED {status} {res}") + else: + print(f" openid: already correct (sub as string)") + # =========================================================================== # Phase 4: Get flow UUIDs (needed for providers) # ===========================================================================