diff --git a/api/package.json b/api/package.json index ed12ad42..0f6c01d0 100644 --- a/api/package.json +++ b/api/package.json @@ -13,7 +13,7 @@ "test": "vitest --watch=false", "dev": "yarn build && yarn start", "build": "tsc", - "start": "./.env.local.sh forever dist/main.js", + "start": "dotenv -e ../.env -- forever dist/main.js", "_format": "prettier \"**/*.{ts,tsx,json,md}\"", "format": "yarn run _format --write", "format:check": "yarn run _format --list-different", @@ -51,6 +51,7 @@ "cors": "^2.8.5", "csv-parse": "^5.0.4", "deepmerge": "^4.2.2", + "dotenv-cli": "^7.4.1", "evt": "^2.5.7", "express": "^4.17.2", "forever": "^4.0.3", diff --git a/api/src/env.ts b/api/src/env.ts index a5a1a82b..060c0633 100644 --- a/api/src/env.ts +++ b/api/src/env.ts @@ -1,11 +1,8 @@ -import { symToStr } from "tsafe/symToStr"; -import { assert } from "tsafe/assert"; import * as JSONC from "comment-json"; import { z } from "zod"; import { zLocalizedString } from "./core/ports/GetSoftwareExternalData"; -import { is } from "tsafe/is"; -const zParsedCONFIGURATION = z.object({ +const zConfiguration = z.object({ "keycloakParams": z .object({ "url": z.string().nonempty(), //Example: https://auth.code.gouv.fr/auth (with the /auth at the end) @@ -33,42 +30,59 @@ const zParsedCONFIGURATION = z.object({ // we use the GitHub API for pre filling the version when adding a software "githubPersonalAccessTokenForApiRateLimit": z.string().nonempty(), //Port we listen to, default 8080 - "port": z.number().optional(), + "port": z.coerce.number().optional(), "isDevEnvironnement": z.boolean().optional(), // Completely disable this instance and redirect to another url "redirectUrl": z.string().optional(), "externalSoftwareDataOrigin": z.enum(["wikidata", "HAL"]).optional() }); -const { parsedCONFIGURATION } = (() => { +const getJsonConfiguration = () => { const { CONFIGURATION } = process.env; - - if (CONFIGURATION === undefined) { - throw new Error( - `We need a ${symToStr({ - CONFIGURATION - })} environnement variable` - ); - } - - let parsedCONFIGURATION: unknown; - - try { - parsedCONFIGURATION = JSONC.parse(CONFIGURATION) as any; - } catch (error) { - throw new Error( - `The CONFIGURATION environnement variable is not a valid JSONC string (JSONC = JSON + Comment support)\n${CONFIGURATION}: ${String( - error - )}` - ); + if (CONFIGURATION) { + try { + return JSONC.parse(CONFIGURATION) as any; + } catch (error) { + throw new Error( + `The CONFIGURATION environnement variable is not a valid JSONC string (JSONC = JSON + Comment support)\n${CONFIGURATION}: ${String( + error + )}` + ); + } } - zParsedCONFIGURATION.parse(parsedCONFIGURATION); + return { + "keycloakParams": { + "url": process.env.SILL_KEYCLOAK_URL, + "realm": process.env.SILL_KEYCLOAK_REALM, + "clientId": process.env.SILL_KEYCLOAK_CLIENT_ID, + "adminPassword": process.env.SILL_KEYCLOAK_ADMIN_PASSWORD, + "organizationUserProfileAttributeName": process.env.SILL_KEYCLOAK_ORGANIZATION_USER_PROFILE_ATTRIBUTE_NAME + }, + "readmeUrl": process.env.SILL_README_URL, + "termsOfServiceUrl": process.env.SILL_TERMS_OF_SERVICE_URL, + "jwtClaimByUserKey": { + "id": process.env.SILL_JWT_ID, + "email": process.env.SILL_JWT_EMAIL, + "organization": process.env.SILL_JWT_ORGANIZATION + }, + "dataRepoSshUrl": process.env.SILL_DATA_REPO_SSH_URL, + "sshPrivateKeyForGitName": process.env.SILL_SSH_NAME, + "sshPrivateKeyForGit": process.env.SILL_SSH_PRIVATE_KEY, + "githubPersonalAccessTokenForApiRateLimit": process.env.SILL_GITHUB_TOKEN, + "githubWebhookSecret": process.env.SILL_WEBHOOK_SECRET, + "port": parseInt(process.env.SILL_API_PORT ?? ""), + "isDevEnvironnement": process.env.SILL_IS_DEV_ENVIRONNEMENT?.toLowerCase() === "true", + "externalSoftwareDataOrigin": process.env.SILL_EXTERNAL_SOFTWARE_DATA_ORIGIN + }; +}; - assert(is>(parsedCONFIGURATION)); +const getValidConfiguration = (): z.infer => { + const configuration = getJsonConfiguration(); + return zConfiguration.parse(configuration); +}; - return { parsedCONFIGURATION }; -})(); +const parsedCONFIGURATION = getValidConfiguration(); export const env = { ...parsedCONFIGURATION, diff --git a/yarn.lock b/yarn.lock index c97d2b2e..4f3cb25f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7642,6 +7642,21 @@ dot-prop@^4.1.0: dependencies: is-obj "^1.0.0" +dotenv-cli@^7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-7.4.1.tgz#be3895878775c257e9864e5e57ff801d7492dcf8" + integrity sha512-fE1aywjRrWGxV3miaiUr3d2zC/VAiuzEGghi+QzgIA9fEf/M5hLMaRSXb4IxbUAwGmaLi0IozdZddnVU96acag== + dependencies: + cross-spawn "^7.0.3" + dotenv "^16.3.0" + dotenv-expand "^10.0.0" + minimist "^1.2.6" + +dotenv-expand@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" + integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== + dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" @@ -7652,6 +7667,11 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +dotenv@^16.3.0: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + dotenv@^8.0.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"