The service account is silently filtered out of Object Type 109 (Store
Address / Hierarchy) despite having schema-level read on schema 68, so
every AQL against the store type returns total=0. Until that permission
is granted, resolve store numbers from a local cache populated by a
personal PAT (different auth path, different account, has the role).
- new: src/services/jira/assetsSyncClient.js — Basic-auth axios against
api.atlassian.com/jsm/assets/workspace/{ws}/v1, credentials sourced
from ASSETS_SYNC_EMAIL / ASSETS_SYNC_TOKEN (loaded from Keychain by
bin/load-assets-sync-secret.sh so the PAT never touches .env)
- new: src/services/jira/storesCache.js — in-memory Map + on-disk JSON
at data/stores.json (gitignored), atomic write, paginated full sync
via AQL (objectTypeId=N), boot-time load + background refresh if
stale, periodic setInterval every STORES_CACHE_REFRESH_HOURS
- new: bin/load-assets-sync-secret.sh — Keychain -> env var wrapper
(security find-generic-password -s jira-assets-sync -a <email>)
- change: resolveStoreAssetReference now tries cache -> live PAT -> the
existing service-account AQL, in that order; the fallback path is
preserved so this cleanly deactivates once the permission on #1 is
fixed. Error message names all three routes and points at the refresh
endpoint.
- new admin routes: GET /api/wxccai/admin/storesCache/status,
POST /api/wxccai/admin/storesCache/refresh
- app.js kicks off storesCache.init() after listen()
- config: STORES_CACHE_ENABLED / _PATH / _REFRESH_HOURS /
_STALE_AFTER_HOURS / _PAGE_SIZE / _MAX_PAGES, plus intFromEnv /
boolFromEnv helpers
- .gitignore adds data/; .env.example documents the new vars; README
adds an "Admin" endpoints section and a "Stores cache" setup guide
Co-authored-by: Cursor <cursoragent@cursor.com>
72 lines
2.9 KiB
Text
72 lines
2.9 KiB
Text
# =============================================
|
|
# .env.example
|
|
# Copy to .env and fill in real values. DO NOT commit .env.
|
|
# =============================================
|
|
|
|
# --- Server ---
|
|
PORT=1866
|
|
NODE_ENV=development
|
|
|
|
# --- Jira ---
|
|
# Preferred: use the JIRA_CLOUD_ID gateway form. When set, requests go to
|
|
# https://api.atlassian.com/ex/jira/{cloudId}. If unset, JIRA_BASE_URL is used
|
|
# directly (e.g. https://your-site.atlassian.net).
|
|
JIRA_CLOUD_ID=
|
|
JIRA_BASE_URL=https://your-site.atlassian.net
|
|
|
|
# Auth. 'basic' = email + API token (Atlassian API tokens).
|
|
# 'bearer' = OAuth bearer token in Authorization header.
|
|
JIRA_AUTH_TYPE=basic
|
|
JIRA_EMAIL=service-account@example.com
|
|
JIRA_API_TOKEN=REPLACE_ME
|
|
|
|
# JSM Service Desk (Store Support). Numeric service desk id.
|
|
JIRA_SERVICE_DESK_ID=170
|
|
|
|
# Role name used to restrict visibility on comments posted by this service.
|
|
# Common values: "Administrators", "Service Desk Team".
|
|
JIRA_COMMENT_VISIBILITY_ROLE=Service Desk Team
|
|
|
|
# --- Jira Assets (Store Number -> Assets object resolution) ---
|
|
# Workspace id for Jira Assets. If unset, the app tries to auto-discover via
|
|
# /rest/servicedeskapi/assets/workspace, but setting it explicitly is safer
|
|
# on tenants with more than one Assets workspace.
|
|
JIRA_ASSETS_WORKSPACE_ID=
|
|
|
|
# Numeric object schema and object type id for the Stores schema in Assets.
|
|
JIRA_ASSETS_STORE_SCHEMA_ID=68
|
|
JIRA_ASSETS_STORE_OBJECT_TYPE_ID=109
|
|
|
|
# The attribute name (as shown in the Assets UI) holding the store number.
|
|
JIRA_ASSETS_STORE_NUMBER_ATTRIBUTE=Store Number
|
|
# Optional. If set, the app will also try attribute[<id>]=... form in AQL.
|
|
JIRA_ASSETS_STORE_NUMBER_ATTRIBUTE_ID=
|
|
|
|
# The custom field on the JSM request that holds the Store Assets reference.
|
|
JIRA_STORE_CUSTOM_FIELD_ID=customfield_10261
|
|
|
|
# --- Assets Stores cache (personal-PAT sync workaround) ---
|
|
# The service account is silently filtered out of Object Type 109 (see
|
|
# Forgejo issue #1). Until that's fixed, the app populates a local store-number
|
|
# -> objectId cache using a *personal* Atlassian PAT that has the right role.
|
|
# This PAT is used ONLY for reading the Stores schema; nothing that mutates
|
|
# Jira state uses it.
|
|
#
|
|
# ASSETS_SYNC_TOKEN should be exported at runtime from macOS Keychain via
|
|
# bin/load-assets-sync-secret.sh, NOT hardcoded here. It's listed only for
|
|
# completeness / documentation.
|
|
ASSETS_SYNC_EMAIL=you@ae.com
|
|
# ASSETS_SYNC_TOKEN= # loaded from Keychain by bin/load-assets-sync-secret.sh
|
|
|
|
# Where the local cache lives on disk (JSON). Gitignored. Regenerable via
|
|
# POST /api/wxccai/admin/storesCache/refresh.
|
|
STORES_CACHE_PATH=./data/stores.json
|
|
# How often to run a full resync (hours). 0 disables the scheduler.
|
|
STORES_CACHE_REFRESH_HOURS=24
|
|
# Cache is considered "stale" after this many hours; boot-time refresh fires
|
|
# if the on-disk snapshot is older than this.
|
|
STORES_CACHE_STALE_AFTER_HOURS=48
|
|
|
|
# --- xAI (Grok) ---
|
|
XAI_API_KEY=REPLACE_ME
|
|
XAI_BASE_URL=https://api.x.ai/v1
|