diff --git a/VERSION b/VERSION index 4e379d2..bcab45a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.2 +0.0.3 diff --git a/config/README.md b/config/README.md index 2ea85f1..b7f5df2 100644 --- a/config/README.md +++ b/config/README.md @@ -1,5 +1,7 @@ # Configuration locale (`config/`) -- **`services.local.env.example`** : variables d’environnement agrégées pour les micro-services (ports sur `127.0.0.1`, jetons, URL de l’API globale et du SSO). Copier vers **`services.local.env`** (gitignoré à la racine du monorepo). +- **`services.local.env.example`** : variables d’environnement agrégées pour les micro-services (ports sur `127.0.0.1`, jetons, URL de l’API globale et du SSO). Copier vers **`services.local.env`** (gitignoré à la racine du monorepo). Référencé par les unités systemd utilisateur décrites dans [docs/repo/systemd-units.md](../docs/repo/systemd-units.md). + +- **Paquet partagé des amonts** : `packages/smart-ide-upstreams/` — une seule définition des clés `orchestrator`, `repos_devtools`, etc. Voir aussi [docs/README.md](../docs/README.md). diff --git a/docs/API/global-api.md b/docs/API/global-api.md index c8733ea..6165f9e 100644 --- a/docs/API/global-api.md +++ b/docs/API/global-api.md @@ -47,6 +47,10 @@ Fichier **`.logs/global-api/access.log`** : lignes JSON (`ts`, `method`, `path`, Jetons et hôtes des micro-services : mêmes noms que dans `config/services.local.env.example`. +## Source des amonts + +Les clés et la résolution `host:port` / jetons sont définies dans le paquet partagé **`packages/smart-ide-upstreams/`** (`@4nk/smart-ide-upstreams`). Toute nouvelle clé d’amont doit y être ajoutée une seule fois, puis `npm run build` dans ce paquet et commit du répertoire `dist/`. + ## Voir aussi - [sso-gateway-api.md](./sso-gateway-api.md) diff --git a/docs/API/sso-gateway-api.md b/docs/API/sso-gateway-api.md index dcf6383..3eb2ec9 100644 --- a/docs/API/sso-gateway-api.md +++ b/docs/API/sso-gateway-api.md @@ -55,7 +55,7 @@ Aucun stockage d’**utilisateurs** ou de **comptes par projet** dans ce service | `GLOBAL_API_INTERNAL_TOKEN` | Obligatoire — même valeur que sur smart-ide-global-api | | `SMART_IDE_MONOREPO_ROOT` | Optionnel — racine pour écrire sous `.logs/sso-gateway/` | -Les jetons et hôtes des micro-services sont lus par **smart-ide-global-api** ; voir `config/services.local.env.example` et [global-api.md](./global-api.md). +Les jetons et hôtes des micro-services sont lus par **smart-ide-global-api** ; la liste des clés `GET /v1/upstreams` provient du module **`@4nk/smart-ide-upstreams`** (`packages/smart-ide-upstreams/`). Voir `config/services.local.env.example` et [global-api.md](./global-api.md). ## Voir aussi diff --git a/docs/README.md b/docs/README.md index 4578073..9216ea6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,6 +8,7 @@ Index principal. Les **fonctionnalités** détaillées sont dans [`features/`](. |---------|------| | [config/services.local.env.example](../config/services.local.env.example) | Ports, jetons, `SMART_IDE_MONOREPO_ROOT` — copier vers `config/services.local.env` | | [config/README.md](../config/README.md) | Rôle du répertoire `config/` | +| [packages/smart-ide-upstreams/README.md](../packages/smart-ide-upstreams/README.md) | Clés d’amont HTTP partagées (`@4nk/smart-ide-upstreams`) pour l’API globale et le SSO | ## Documentation du dépôt (`repo/`) diff --git a/docs/features/sso-docv-enso.md b/docs/features/sso-docv-enso.md index f341da1..ed927c5 100644 --- a/docs/features/sso-docv-enso.md +++ b/docs/features/sso-docv-enso.md @@ -50,7 +50,7 @@ Un **client OAuth par env** (test / pprod / prod) ou un seul client avec **claim ## Références internes -- [sso-gateway-service.md](./sso-gateway-service.md) — passerelle OIDC → API internes +- [sso-gateway-service.md](./sso-gateway-service.md) — passerelle OIDC → **smart-ide-global-api** → micro-services - [platform-target.md](../platform-target.md) — matrice test / pprod / prod - [deployment-target.md](../deployment-target.md) — TLS, pas de HTTP de contournement diff --git a/docs/features/sso-gateway-service.md b/docs/features/sso-gateway-service.md index 542fd14..baf6b6f 100644 --- a/docs/features/sso-gateway-service.md +++ b/docs/features/sso-gateway-service.md @@ -30,7 +30,7 @@ Les services amont peuvent s’en servir pour du **journal** ou des **règles fi ## Comptes, projets et bases métier -La passerelle **ne conserve pas** de comptes utilisateurs, de profils projet, ni de quotas métier : elle ne fait que **vérifier** le JWT OIDC et **relayer** vers les micro-services. +La passerelle **ne conserve pas** de comptes utilisateurs, de profils projet, ni de quotas métier : elle ne fait que **vérifier** le JWT OIDC et **relayer** vers **smart-ide-global-api**, qui applique les jetons techniques et proxifie vers chaque micro-service. Les **comptes**, droits par **projet**, abonnements, historiques et toute persistance **métier** liée à l’identité restent dans les **bases des applications** (ex. docv, Enso, autres produits) et dans leurs schémas de données — pas dans `smart-ide-sso-gateway`. Le découpage par projet côté IDE et chemins de déploiement est décrit sous `projects//conf.json` — [repo/projects-directory.md](../repo/projects-directory.md). @@ -40,5 +40,6 @@ Les services amont qui reçoivent `X-OIDC-Sub` / `X-OIDC-Email` sont responsable - [API/sso-gateway-api.md](../API/sso-gateway-api.md) - [API/global-api.md](../API/global-api.md) +- [packages/smart-ide-upstreams/README.md](../../packages/smart-ide-upstreams/README.md) — clés d’amont partagées (`@4nk/smart-ide-upstreams`) - [services/smart-ide-sso-gateway/README.md](../../services/smart-ide-sso-gateway/README.md) - [services/smart-ide-global-api/README.md](../../services/smart-ide-global-api/README.md) diff --git a/docs/repo/README.md b/docs/repo/README.md index 10e5967..5610575 100644 --- a/docs/repo/README.md +++ b/docs/repo/README.md @@ -43,6 +43,7 @@ Toute la documentation **opérationnelle** qui vivait auparavant sous des `READM | [service-smart-ide-tools-bridge.md](./service-smart-ide-tools-bridge.md) | Pont HTTP IDE + outils sous-modules | | [service-smart-ide-global-api.md](./service-smart-ide-global-api.md) | API HTTP interne : proxy vers micro-services (Bearer partagé avec SSO) | | [service-smart-ide-sso-gateway.md](./service-smart-ide-sso-gateway.md) | Passerelle OIDC utilisateur → API globale → micro-services | +| [../packages/smart-ide-upstreams/README.md](../packages/smart-ide-upstreams/README.md) | Paquet `@4nk/smart-ide-upstreams` : liste des clés et résolution des URL / jetons | | [extension-anythingllm-workspaces.md](./extension-anythingllm-workspaces.md) | Extension AnythingLLM IDE (supprimée ; anythingllm-devtools) | Les **spécifications** détaillées (contrats HTTP, sécurité, orchestration) restent dans [../API/README.md](../API/README.md) et [../features/](../features/). diff --git a/docs/repo/service-smart-ide-global-api.md b/docs/repo/service-smart-ide-global-api.md index ced7ab9..7dbb641 100644 --- a/docs/repo/service-smart-ide-global-api.md +++ b/docs/repo/service-smart-ide-global-api.md @@ -1,6 +1,6 @@ # Service smart-ide-global-api (`services/smart-ide-global-api/`) -Couche HTTP **interne** : proxy vers les micro-services avec jetons techniques, **sans OIDC**. Consommée exclusivement par **`smart-ide-sso-gateway`** (Bearer `GLOBAL_API_INTERNAL_TOKEN`). +Couche HTTP **interne** : proxy vers les micro-services avec jetons techniques, **sans OIDC**. **`smart-ide-sso-gateway`** l’appelle avec `GLOBAL_API_INTERNAL_TOKEN` ; des **scripts M2M** peuvent aussi utiliser le même Bearer pour un point d’entrée unique (voir [sso-gateway-service.md](../features/sso-gateway-service.md)). ## Configuration locale @@ -11,6 +11,10 @@ Couche HTTP **interne** : proxy vers les micro-services avec jetons techniques, Démarrer **avant** la passerelle SSO. Voir **[`services/smart-ide-global-api/README.md`](../../services/smart-ide-global-api/README.md)** et **[`docs/API/global-api.md`](../API/global-api.md)**. +**Liste des amonts** : source unique dans **`packages/smart-ide-upstreams/`** (import `@4nk/smart-ide-upstreams` côté global-api et SSO). + +**systemd (utilisateur)** : gabarits `systemd/user/smart-ide-global-api.service.in` et `smart-ide-sso-gateway.service.in` ; installation — [systemd-units.md](./systemd-units.md), script `scripts/install-smart-ide-gateway-systemd-user.sh`. + ## Voir aussi - [service-smart-ide-sso-gateway.md](./service-smart-ide-sso-gateway.md) diff --git a/docs/repo/service-smart-ide-sso-gateway.md b/docs/repo/service-smart-ide-sso-gateway.md index 8b8146e..01feaa1 100644 --- a/docs/repo/service-smart-ide-sso-gateway.md +++ b/docs/repo/service-smart-ide-sso-gateway.md @@ -11,6 +11,8 @@ Passerelle HTTP : validation **JWT utilisateur** (issuer docv / Enso), puis **pr Voir **[`services/smart-ide-sso-gateway/README.md`](../../services/smart-ide-sso-gateway/README.md)**, **[`docs/features/sso-gateway-service.md`](../features/sso-gateway-service.md)** et **[`docs/API/sso-gateway-api.md`](../API/sso-gateway-api.md)**. +**systemd (utilisateur)** : l’unité SSO déclare **`After=`** et **`Requires=smart-ide-global-api.service`** — [systemd-units.md](./systemd-units.md), `scripts/install-smart-ide-gateway-systemd-user.sh`. + ## Voir aussi - [service-smart-ide-global-api.md](./service-smart-ide-global-api.md) diff --git a/docs/repo/systemd-units.md b/docs/repo/systemd-units.md index cb578d8..3b5485f 100644 --- a/docs/repo/systemd-units.md +++ b/docs/repo/systemd-units.md @@ -46,6 +46,22 @@ Tirage **périodique** des dépôts listés dans `projects/*/conf.json` (voir [c - Installation : `scripts/install-git-pull-systemd-user.sh` (écrit dans `~/.config/systemd/user/`, **sans** `sudo`) - Configuration : `cron/config.env` +## smart-ide-global-api et smart-ide-sso-gateway (utilisateur) + +Unités **user** pour l’agrégateur HTTP interne puis la passerelle OIDC. L’unité SSO déclare **`Requires=smart-ide-global-api.service`** et **`After=smart-ide-global-api.service`** pour garantir l’ordre de démarrage. + +- Gabarits : `systemd/user/smart-ide-global-api.service.in`, `systemd/user/smart-ide-sso-gateway.service.in` +- Variables : `config/services.local.env` (copie de `config/services.local.env.example`, gitignoré) — `GLOBAL_API_INTERNAL_TOKEN`, `OIDC_ISSUER`, jetons micro-services, etc. +- Prérequis : `npm ci && npm run build` dans `packages/smart-ide-upstreams`, `services/smart-ide-global-api`, `services/smart-ide-sso-gateway` + +Installation : + +```bash +./scripts/install-smart-ide-gateway-systemd-user.sh +systemctl --user start smart-ide-global-api smart-ide-sso-gateway +systemctl --user status smart-ide-global-api smart-ide-sso-gateway +``` + ## Linger (session fermée) Les timers **user** ne tournent en général que lorsqu’une session utilisateur systemd est active. Pour exécuter les timers après déconnexion graphique complète, activer le **linger** pour l’utilisateur : `loginctl enable-linger ` (décision d’administration machine). diff --git a/docs/system-architecture.md b/docs/system-architecture.md index 05ad9c9..dfdcc67 100644 --- a/docs/system-architecture.md +++ b/docs/system-architecture.md @@ -45,6 +45,9 @@ Conséquences : | `services/ia-dev-gateway/` | Gateway HTTP (stub runner) : registre agents `.md`, runs, SSE — [features/ia-dev-service.md](./features/ia-dev-service.md) | | `services/smart-ide-orchestrator/` | Routage intentions (stub forward) — [features/orchestrator-api.md](./features/orchestrator-api.md) | | `services/smart-ide-tools-bridge/` | API IDE : registre des services + Carbonyl / PageIndex / Chandra — [repo/service-smart-ide-tools-bridge.md](./repo/service-smart-ide-tools-bridge.md) | +| `services/smart-ide-global-api/` | Agrégateur HTTP interne (Bearer partagé avec le SSO) → micro-services avec jetons techniques — [repo/service-smart-ide-global-api.md](./repo/service-smart-ide-global-api.md) | +| `services/smart-ide-sso-gateway/` | Validation JWT utilisateur (docv / Enso) puis proxy via l’API globale — [repo/service-smart-ide-sso-gateway.md](./repo/service-smart-ide-sso-gateway.md) | +| `packages/smart-ide-upstreams/` | Module partagé : clés d’amont et `resolveUpstream()` pour l’API globale et la liste `GET /v1/upstreams` du SSO — [packages/smart-ide-upstreams/README.md](../packages/smart-ide-upstreams/README.md) | | `config/` | Configuration locale agrégée (`services.local.env.example`) pour l’IDE et les services | ## Environnements test, pprod, prod diff --git a/packages/smart-ide-upstreams/.gitignore b/packages/smart-ide-upstreams/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/packages/smart-ide-upstreams/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/packages/smart-ide-upstreams/README.md b/packages/smart-ide-upstreams/README.md new file mode 100644 index 0000000..5948545 --- /dev/null +++ b/packages/smart-ide-upstreams/README.md @@ -0,0 +1,7 @@ +# @4nk/smart-ide-upstreams + +Module partagé : **`listUpstreamKeys()`**, **`resolveUpstream(key)`** et types associés. Consommé par **`services/smart-ide-global-api`** (proxy HTTP) et **`services/smart-ide-sso-gateway`** (liste exposée à `GET /v1/upstreams`). + +Une seule source pour les clés et les variables d’environnement des amonts. + +Le répertoire **`dist/`** est versionné pour que `npm ci` dans les services suffise sans étape préalable. Après modification de `src/`, exécuter dans ce paquet : `npm ci && npm run build`, puis committer `dist/` avec le TypeScript. diff --git a/packages/smart-ide-upstreams/dist/index.d.ts b/packages/smart-ide-upstreams/dist/index.d.ts new file mode 100644 index 0000000..dc07060 --- /dev/null +++ b/packages/smart-ide-upstreams/dist/index.d.ts @@ -0,0 +1,2 @@ +export { resolveUpstream, listUpstreamKeys, type UpstreamAuth, type UpstreamTarget, } from "./upstreams.js"; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/smart-ide-upstreams/dist/index.d.ts.map b/packages/smart-ide-upstreams/dist/index.d.ts.map new file mode 100644 index 0000000..0965341 --- /dev/null +++ b/packages/smart-ide-upstreams/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC"} \ No newline at end of file diff --git a/packages/smart-ide-upstreams/dist/index.js b/packages/smart-ide-upstreams/dist/index.js new file mode 100644 index 0000000..05d98f4 --- /dev/null +++ b/packages/smart-ide-upstreams/dist/index.js @@ -0,0 +1 @@ +export { resolveUpstream, listUpstreamKeys, } from "./upstreams.js"; diff --git a/packages/smart-ide-upstreams/dist/upstreams.d.ts b/packages/smart-ide-upstreams/dist/upstreams.d.ts new file mode 100644 index 0000000..d04d3d1 --- /dev/null +++ b/packages/smart-ide-upstreams/dist/upstreams.d.ts @@ -0,0 +1,15 @@ +export type UpstreamAuth = { + kind: "bearer"; + token: string; +} | { + kind: "header"; + name: string; + value: string; +}; +export type UpstreamTarget = { + baseUrl: string; + auth: UpstreamAuth; +}; +export declare const resolveUpstream: (key: string) => UpstreamTarget | null; +export declare const listUpstreamKeys: () => string[]; +//# sourceMappingURL=upstreams.d.ts.map \ No newline at end of file diff --git a/packages/smart-ide-upstreams/dist/upstreams.d.ts.map b/packages/smart-ide-upstreams/dist/upstreams.d.ts.map new file mode 100644 index 0000000..2db5af5 --- /dev/null +++ b/packages/smart-ide-upstreams/dist/upstreams.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"upstreams.d.ts","sourceRoot":"","sources":["../src/upstreams.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,YAAY,CAAC;CACpB,CAAC;AAMF,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,cAAc,GAAG,IAsE9D,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,MAAM,EAUzC,CAAC"} \ No newline at end of file diff --git a/packages/smart-ide-upstreams/dist/upstreams.js b/packages/smart-ide-upstreams/dist/upstreams.js new file mode 100644 index 0000000..5350995 --- /dev/null +++ b/packages/smart-ide-upstreams/dist/upstreams.js @@ -0,0 +1,68 @@ +const trimSlash = (s) => s.replace(/\/+$/, ""); +const env = (k, d) => process.env[k]?.trim() ?? d; +export const resolveUpstream = (key) => { + switch (key) { + case "orchestrator": + return { + baseUrl: trimSlash(`http://${env("ORCHESTRATOR_HOST", "127.0.0.1")}:${env("ORCHESTRATOR_PORT", "37145")}`), + auth: { kind: "bearer", token: env("ORCHESTRATOR_TOKEN", "") }, + }; + case "repos_devtools": + return { + baseUrl: trimSlash(`http://${env("REPOS_DEVTOOLS_HOST", "127.0.0.1")}:${env("REPOS_DEVTOOLS_PORT", "37140")}`), + auth: { kind: "bearer", token: env("REPOS_DEVTOOLS_TOKEN", "") }, + }; + case "ia_dev_gateway": + return { + baseUrl: trimSlash(`http://${env("IA_DEV_GATEWAY_HOST", "127.0.0.1")}:${env("IA_DEV_GATEWAY_PORT", "37144")}`), + auth: { kind: "bearer", token: env("IA_DEV_GATEWAY_TOKEN", "") }, + }; + case "anythingllm_devtools": + return { + baseUrl: trimSlash(`http://${env("ANYTHINGLLM_DEVTOOLS_HOST", "127.0.0.1")}:${env("ANYTHINGLLM_DEVTOOLS_PORT", "37146")}`), + auth: { kind: "bearer", token: env("ANYTHINGLLM_DEVTOOLS_TOKEN", "") }, + }; + case "tools_bridge": + return { + baseUrl: trimSlash(`http://${env("TOOLS_BRIDGE_HOST", "127.0.0.1")}:${env("TOOLS_BRIDGE_PORT", "37147")}`), + auth: { kind: "bearer", token: env("TOOLS_BRIDGE_TOKEN", "") }, + }; + case "langextract": + return { + baseUrl: trimSlash(`http://${env("LANGEXTRACT_API_HOST", "127.0.0.1")}:${env("LANGEXTRACT_API_PORT", "37141")}`), + auth: { kind: "bearer", token: env("LANGEXTRACT_SERVICE_TOKEN", "") }, + }; + case "regex_search": + return { + baseUrl: trimSlash(`http://${env("REGEX_SEARCH_HOST", "127.0.0.1")}:${env("REGEX_SEARCH_PORT", "37143")}`), + auth: { kind: "bearer", token: env("REGEX_SEARCH_TOKEN", "") }, + }; + case "claw_proxy": + return { + baseUrl: trimSlash(`http://${env("CLAW_PROXY_HOST", "127.0.0.1")}:${env("CLAW_PROXY_PORT", "37142")}`), + auth: { kind: "bearer", token: env("CLAW_PROXY_TOKEN", "") }, + }; + case "local_office": + return { + baseUrl: trimSlash(env("LOCAL_OFFICE_URL", "http://127.0.0.1:8000")), + auth: { + kind: "header", + name: "X-API-Key", + value: env("LOCAL_OFFICE_API_KEY", ""), + }, + }; + default: + return null; + } +}; +export const listUpstreamKeys = () => [ + "orchestrator", + "repos_devtools", + "ia_dev_gateway", + "anythingllm_devtools", + "tools_bridge", + "langextract", + "regex_search", + "claw_proxy", + "local_office", +]; diff --git a/packages/smart-ide-upstreams/package-lock.json b/packages/smart-ide-upstreams/package-lock.json new file mode 100644 index 0000000..fa2cfed --- /dev/null +++ b/packages/smart-ide-upstreams/package-lock.json @@ -0,0 +1,51 @@ +{ + "name": "@4nk/smart-ide-upstreams", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@4nk/smart-ide-upstreams", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^20.11.0", + "typescript": "^5.3.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@types/node": { + "version": "20.19.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", + "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/packages/smart-ide-upstreams/package.json b/packages/smart-ide-upstreams/package.json new file mode 100644 index 0000000..58242fe --- /dev/null +++ b/packages/smart-ide-upstreams/package.json @@ -0,0 +1,26 @@ +{ + "name": "@4nk/smart-ide-upstreams", + "version": "0.1.0", + "private": true, + "description": "Shared upstream keys and resolveUpstream() for smart-ide-global-api and smart-ide-sso-gateway.", + "license": "MIT", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc -p ." + }, + "engines": { + "node": ">=20" + }, + "devDependencies": { + "@types/node": "^20.11.0", + "typescript": "^5.3.3" + } +} diff --git a/packages/smart-ide-upstreams/src/index.ts b/packages/smart-ide-upstreams/src/index.ts new file mode 100644 index 0000000..47f9794 --- /dev/null +++ b/packages/smart-ide-upstreams/src/index.ts @@ -0,0 +1,6 @@ +export { + resolveUpstream, + listUpstreamKeys, + type UpstreamAuth, + type UpstreamTarget, +} from "./upstreams.js"; diff --git a/services/smart-ide-global-api/src/upstreams.ts b/packages/smart-ide-upstreams/src/upstreams.ts similarity index 100% rename from services/smart-ide-global-api/src/upstreams.ts rename to packages/smart-ide-upstreams/src/upstreams.ts diff --git a/packages/smart-ide-upstreams/tsconfig.json b/packages/smart-ide-upstreams/tsconfig.json new file mode 100644 index 0000000..d3473cc --- /dev/null +++ b/packages/smart-ide-upstreams/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "declaration": true, + "declarationMap": true + }, + "include": ["src/**/*.ts"] +} diff --git a/scripts/install-smart-ide-gateway-systemd-user.sh b/scripts/install-smart-ide-gateway-systemd-user.sh new file mode 100755 index 0000000..dcc5107 --- /dev/null +++ b/scripts/install-smart-ide-gateway-systemd-user.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Installe les unités systemd *utilisateur* pour smart-ide-global-api puis smart-ide-sso-gateway. +# Prérequis : config/services.local.env (copie de config/services.local.env.example), builds npm +# (packages/smart-ide-upstreams, services/smart-ide-global-api, services/smart-ide-sso-gateway). +# Usage : ./scripts/install-smart-ide-gateway-systemd-user.sh +# Puis : systemctl --user start smart-ide-global-api smart-ide-sso-gateway +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +UNIT_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user" +GLOBAL_IN="${ROOT}/systemd/user/smart-ide-global-api.service.in" +SSO_IN="${ROOT}/systemd/user/smart-ide-sso-gateway.service.in" +GLOBAL_OUT="${UNIT_DIR}/smart-ide-global-api.service" +SSO_OUT="${UNIT_DIR}/smart-ide-sso-gateway.service" + +mkdir -p "$UNIT_DIR" +sed "s|@SMART_IDE_ROOT@|$ROOT|g" "$GLOBAL_IN" >"$GLOBAL_OUT" +sed "s|@SMART_IDE_ROOT@|$ROOT|g" "$SSO_IN" >"$SSO_OUT" +chmod 0644 "$GLOBAL_OUT" "$SSO_OUT" + +systemctl --user daemon-reload +systemctl --user enable smart-ide-global-api.service +systemctl --user enable smart-ide-sso-gateway.service + +echo "Unités : $GLOBAL_OUT" +echo " $SSO_OUT" +echo "Démarrage : systemctl --user start smart-ide-global-api smart-ide-sso-gateway" +echo "État : systemctl --user status smart-ide-global-api smart-ide-sso-gateway" diff --git a/services/smart-ide-global-api/README.md b/services/smart-ide-global-api/README.md index 2b328db..2a49f8a 100644 --- a/services/smart-ide-global-api/README.md +++ b/services/smart-ide-global-api/README.md @@ -20,6 +20,8 @@ npm start Écoute par défaut : `http://127.0.0.1:37149`. +**Amonts** : module **`@4nk/smart-ide-upstreams`** (`../../packages/smart-ide-upstreams/`). Après modification des clés ou des variables d’environnement mappées, reconstruire ce paquet et committer son `dist/`. + ## Journaux Une ligne JSON par requête (hors `GET /health`) dans **`.logs/global-api/access.log`** (répertoire `.logs/` à la racine du monorepo, créé au besoin). diff --git a/services/smart-ide-global-api/package-lock.json b/services/smart-ide-global-api/package-lock.json index bfac5e4..838e63a 100644 --- a/services/smart-ide-global-api/package-lock.json +++ b/services/smart-ide-global-api/package-lock.json @@ -8,6 +8,9 @@ "name": "@4nk/smart-ide-global-api", "version": "0.1.0", "license": "MIT", + "dependencies": { + "@4nk/smart-ide-upstreams": "file:../../packages/smart-ide-upstreams" + }, "devDependencies": { "@types/node": "^20.11.0", "typescript": "^5.3.3" @@ -16,6 +19,22 @@ "node": ">=20" } }, + "../../packages/smart-ide-upstreams": { + "name": "@4nk/smart-ide-upstreams", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^20.11.0", + "typescript": "^5.3.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@4nk/smart-ide-upstreams": { + "resolved": "../../packages/smart-ide-upstreams", + "link": true + }, "node_modules/@types/node": { "version": "20.19.39", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", diff --git a/services/smart-ide-global-api/package.json b/services/smart-ide-global-api/package.json index 8b3f64d..562b58d 100644 --- a/services/smart-ide-global-api/package.json +++ b/services/smart-ide-global-api/package.json @@ -13,6 +13,9 @@ "engines": { "node": ">=20" }, + "dependencies": { + "@4nk/smart-ide-upstreams": "file:../../packages/smart-ide-upstreams" + }, "devDependencies": { "@types/node": "^20.11.0", "typescript": "^5.3.3" diff --git a/services/smart-ide-global-api/src/server.ts b/services/smart-ide-global-api/src/server.ts index e248093..57e5469 100644 --- a/services/smart-ide-global-api/src/server.ts +++ b/services/smart-ide-global-api/src/server.ts @@ -1,6 +1,10 @@ import * as http from "node:http"; +import { + listUpstreamKeys, + resolveUpstream, + type UpstreamAuth, +} from "@4nk/smart-ide-upstreams"; import { appendGlobalApiAccessLog } from "./accessLog.js"; -import { listUpstreamKeys, resolveUpstream, type UpstreamAuth } from "./upstreams.js"; const HOST = process.env.GLOBAL_API_HOST ?? "127.0.0.1"; const PORT = Number(process.env.GLOBAL_API_PORT ?? "37149"); diff --git a/services/smart-ide-sso-gateway/README.md b/services/smart-ide-sso-gateway/README.md index 41e5373..db1f160 100644 --- a/services/smart-ide-sso-gateway/README.md +++ b/services/smart-ide-sso-gateway/README.md @@ -6,7 +6,7 @@ HTTP gateway that validates **user** access tokens from the docv / Enso OIDC iss - Verify `Authorization: Bearer ` with JWKS (`OIDC_ISSUER`, optional `OIDC_AUDIENCE`, optional `OIDC_JWKS_URI`). - Expose `GET /health` without auth. -- Expose `GET /v1/token/verify` and `GET /v1/upstreams` with user Bearer. +- Expose `GET /v1/token/verify` and `GET /v1/upstreams` with user Bearer (upstream keys from **`@4nk/smart-ide-upstreams`**). - Proxy `ANY /proxy//` to **smart-ide-global-api** (`GLOBAL_API_URL`, `GLOBAL_API_INTERNAL_TOKEN`), which relays to the target service and adds upstream auth plus `X-OIDC-Sub` / `X-OIDC-Email` when present in the JWT. Structured request logs (except `GET /health` and `OPTIONS`) are appended to **`.logs/sso-gateway/access.log`** under the monorepo root. diff --git a/services/smart-ide-sso-gateway/package-lock.json b/services/smart-ide-sso-gateway/package-lock.json index 9536bfe..9576759 100644 --- a/services/smart-ide-sso-gateway/package-lock.json +++ b/services/smart-ide-sso-gateway/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { + "@4nk/smart-ide-upstreams": "file:../../packages/smart-ide-upstreams", "jose": "^5.9.6" }, "devDependencies": { @@ -19,6 +20,22 @@ "node": ">=20" } }, + "../../packages/smart-ide-upstreams": { + "name": "@4nk/smart-ide-upstreams", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^20.11.0", + "typescript": "^5.3.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@4nk/smart-ide-upstreams": { + "resolved": "../../packages/smart-ide-upstreams", + "link": true + }, "node_modules/@types/node": { "version": "20.19.39", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", diff --git a/services/smart-ide-sso-gateway/package.json b/services/smart-ide-sso-gateway/package.json index 16370cc..25a36a1 100644 --- a/services/smart-ide-sso-gateway/package.json +++ b/services/smart-ide-sso-gateway/package.json @@ -2,7 +2,7 @@ "name": "@4nk/smart-ide-sso-gateway", "version": "0.1.0", "private": true, - "description": "OIDC JWT validation (docv/Enso IdP) and authenticated proxy to smart_ide micro-services.", + "description": "OIDC JWT validation (docv/Enso IdP); proxies via smart-ide-global-api to smart_ide micro-services.", "license": "MIT", "type": "module", "main": "dist/server.js", @@ -14,6 +14,7 @@ "node": ">=20" }, "dependencies": { + "@4nk/smart-ide-upstreams": "file:../../packages/smart-ide-upstreams", "jose": "^5.9.6" }, "devDependencies": { diff --git a/services/smart-ide-sso-gateway/src/server.ts b/services/smart-ide-sso-gateway/src/server.ts index 3076fb9..5949ff1 100644 --- a/services/smart-ide-sso-gateway/src/server.ts +++ b/services/smart-ide-sso-gateway/src/server.ts @@ -2,7 +2,7 @@ import * as http from "node:http"; import type { JWTPayload } from "jose"; import { appendSsoAccessLog } from "./accessLog.js"; import { discoverJwksUri, createVerify, type VerifyFn } from "./oidc.js"; -import { listUpstreamKeys } from "./upstreams.js"; +import { listUpstreamKeys } from "@4nk/smart-ide-upstreams"; const HOST = process.env.SSO_GATEWAY_HOST ?? "127.0.0.1"; const PORT = Number(process.env.SSO_GATEWAY_PORT ?? "37148"); diff --git a/services/smart-ide-sso-gateway/src/upstreams.ts b/services/smart-ide-sso-gateway/src/upstreams.ts deleted file mode 100644 index 46c069b..0000000 --- a/services/smart-ide-sso-gateway/src/upstreams.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** Upstream keys exposed to OIDC-authenticated clients; traffic is relayed via smart-ide-global-api. */ -export const listUpstreamKeys = (): string[] => [ - "orchestrator", - "repos_devtools", - "ia_dev_gateway", - "anythingllm_devtools", - "tools_bridge", - "langextract", - "regex_search", - "claw_proxy", - "local_office", -]; diff --git a/systemd/user/smart-ide-global-api.service.in b/systemd/user/smart-ide-global-api.service.in new file mode 100644 index 0000000..cf1b852 --- /dev/null +++ b/systemd/user/smart-ide-global-api.service.in @@ -0,0 +1,16 @@ +[Unit] +Description=smart_ide — API globale interne (proxy vers micro-services, jetons techniques) +Documentation=file://@SMART_IDE_ROOT@/docs/repo/service-smart-ide-global-api.md +After=network-online.target + +[Service] +Type=simple +WorkingDirectory=@SMART_IDE_ROOT@/services/smart-ide-global-api +# Fichier agrégé (gitignoré) : copier config/services.local.env.example vers config/services.local.env +EnvironmentFile=-@SMART_IDE_ROOT@/config/services.local.env +ExecStart=/usr/bin/node @SMART_IDE_ROOT@/services/smart-ide-global-api/dist/server.js +Restart=on-failure +RestartSec=3 + +[Install] +WantedBy=default.target diff --git a/systemd/user/smart-ide-sso-gateway.service.in b/systemd/user/smart-ide-sso-gateway.service.in new file mode 100644 index 0000000..1fb60da --- /dev/null +++ b/systemd/user/smart-ide-sso-gateway.service.in @@ -0,0 +1,17 @@ +[Unit] +Description=smart_ide — passerelle SSO OIDC (délègue le proxy à smart-ide-global-api) +Documentation=file://@SMART_IDE_ROOT@/docs/repo/service-smart-ide-sso-gateway.md +After=network-online.target +After=smart-ide-global-api.service +Requires=smart-ide-global-api.service + +[Service] +Type=simple +WorkingDirectory=@SMART_IDE_ROOT@/services/smart-ide-sso-gateway +EnvironmentFile=-@SMART_IDE_ROOT@/config/services.local.env +ExecStart=/usr/bin/node @SMART_IDE_ROOT@/services/smart-ide-sso-gateway/dist/server.js +Restart=on-failure +RestartSec=3 + +[Install] +WantedBy=default.target