From 5543e134fa862c3d4645adf4412453df46fe8abf Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:13:50 +0200 Subject: [PATCH] Use env vars --- .aiignore | 2 +- .env.template | 9 +++++++ .github/workflows/update_i18n_languages.yml | 2 +- .gitignore | 2 -- gql_codegen.ts | 4 ++- package.json | 1 + src/lib/requests/client/BaseClient.ts | 6 +---- .../settings/screens/ServerSettings.tsx | 5 +++- .../components/cards/SettingsTrackerCard.tsx | 2 +- src/vite-env.d.ts | 15 +++++++++++ .../scripts/release/CommitChangelog.utils.ts | 4 +-- .../release/TranslationChangelog.utils.ts | 17 +++++------- tools/scripts/tokens.template.json | 4 --- tools/scripts/weblate/Weblate.utils.ts | 27 +++++++------------ vite.config.ts | 4 ++- yarn.lock | 5 ++++ 16 files changed, 63 insertions(+), 46 deletions(-) create mode 100644 .env.template delete mode 100644 tools/scripts/tokens.template.json diff --git a/.aiignore b/.aiignore index 0136ff4f..8d12f249 100644 --- a/.aiignore +++ b/.aiignore @@ -11,4 +11,4 @@ dist/ build/ out/ -tools/scripts/tokens.json \ No newline at end of file +.env \ No newline at end of file diff --git a/.env.template b/.env.template new file mode 100644 index 00000000..e06c84f8 --- /dev/null +++ b/.env.template @@ -0,0 +1,9 @@ +PORT = 3000 +ALLOWED_HOSTS = + +VITE_SERVER_URL_DEFAULT = http://localhost:4567 + +CODEGEN_SERVER_URL_GQL = http://localhost:4567/api/graphql + +GITHUB_TOKEN = +WEBLATE_TOKEN = diff --git a/.github/workflows/update_i18n_languages.yml b/.github/workflows/update_i18n_languages.yml index 7851db9b..c82c5875 100644 --- a/.github/workflows/update_i18n_languages.yml +++ b/.github/workflows/update_i18n_languages.yml @@ -41,5 +41,5 @@ jobs: - name: Update i18n resources run: | cd master - echo '{"weblateToken": "${{ secrets.WEBLATE_TOKEN }}"}' > tools/scripts/tokens.json + echo "WEBLATE_TOKEN=${{ secrets.MY_SECRET }}" > .env yarn i18n:gen-resources diff --git a/.gitignore b/.gitignore index ee93e851..3ad8875f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,4 @@ node_modules/ build/* -tools/scripts/tokens.json - src/lib/graphql/schema.json \ No newline at end of file diff --git a/gql_codegen.ts b/gql_codegen.ts index d3581c12..1d5cb0fc 100644 --- a/gql_codegen.ts +++ b/gql_codegen.ts @@ -7,10 +7,12 @@ */ import type { CodegenConfig } from '@graphql-codegen/cli'; +// eslint-disable-next-line import/no-extraneous-dependencies +import 'dotenv/config'; const config: CodegenConfig = { overwrite: true, - schema: 'http://localhost:4567/api/graphql', + schema: process.env.CODEGEN_SERVER_URL_GQL, documents: [ 'src/lib/graphql/queries/**', 'src/lib/graphql/mutations/**', diff --git a/package.json b/package.json index 78fb1b03..5961f43e 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "@typescript-eslint/parser": "7.16.1", "@vitejs/plugin-legacy": "7.0.0", "@vitejs/plugin-react-swc": "3.10.2", + "dotenv": "17.2.1", "eslint": "8.57.0", "eslint-config-airbnb": "19.0.4", "eslint-config-airbnb-typescript": "18.0.0", diff --git a/src/lib/requests/client/BaseClient.ts b/src/lib/requests/client/BaseClient.ts index 6b5855b1..435ee7ce 100644 --- a/src/lib/requests/client/BaseClient.ts +++ b/src/lib/requests/client/BaseClient.ts @@ -14,11 +14,7 @@ export abstract class BaseClient { public abstract readonly fetcher: Fetcher; public getBaseUrl(): string { - const { hostname, port, protocol } = window.location; - - // if port is 3000 it's probably running from webpack development server - const inferredPort = import.meta.env.DEV && port === '3000' ? '4567' : port; - return AppStorage.local.getItemParsed('serverBaseURL', `${protocol}//${hostname}:${inferredPort}`); + return AppStorage.local.getItemParsed('serverBaseURL', import.meta.env.VITE_SERVER_URL_DEFAULT); } public abstract updateConfig(config: Partial): void; diff --git a/src/modules/settings/screens/ServerSettings.tsx b/src/modules/settings/screens/ServerSettings.tsx index 94754928..b17d6f93 100644 --- a/src/modules/settings/screens/ServerSettings.tsx +++ b/src/modules/settings/screens/ServerSettings.tsx @@ -99,7 +99,10 @@ export const ServerSettings = () => { }); const [mutateSettings] = requestManager.useUpdateServerSettings(); - const [serverAddress, setServerAddress] = useLocalStorage('serverBaseURL', window.location.origin); + const [serverAddress, setServerAddress] = useLocalStorage( + 'serverBaseURL', + import.meta.env.VITE_SERVER_URL_DEFAULT, + ); const handleServerAddressChange = (address: string) => { const serverBaseUrl = address.replaceAll(/(\/)+$/g, ''); diff --git a/src/modules/tracker/components/cards/SettingsTrackerCard.tsx b/src/modules/tracker/components/cards/SettingsTrackerCard.tsx index a6139381..c8e9ba24 100644 --- a/src/modules/tracker/components/cards/SettingsTrackerCard.tsx +++ b/src/modules/tracker/components/cards/SettingsTrackerCard.tsx @@ -32,7 +32,7 @@ import { TTrackerSearch } from '@/modules/tracker/Tracker.types.ts'; export const SettingsTrackerCard = ({ tracker }: { tracker: TTrackerSearch }) => { const { t } = useTranslation(); - const [serverAddress] = useLocalStorage('serverBaseURL', window.location.origin); + const [serverAddress] = useLocalStorage('serverBaseURL', import.meta.env.VITE_SERVER_URL_DEFAULT); const [loginTrackerCredentials, { loading: isCredentialLoginInProgress }] = requestManager.useLoginToTrackerCredentials(); diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 719c0c93..b126cb5d 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -7,3 +7,18 @@ */ /// + +interface ViteTypeOptions { + // By adding this line, you can make the type of ImportMetaEnv strict + // to disallow unknown keys. + strictImportMetaEnv: unknown; +} + +interface ImportMetaEnv { + readonly VITE_SERVER_URL_DEFAULT: string; + // more env variables... +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/tools/scripts/release/CommitChangelog.utils.ts b/tools/scripts/release/CommitChangelog.utils.ts index e45a85fb..83d9bf21 100644 --- a/tools/scripts/release/CommitChangelog.utils.ts +++ b/tools/scripts/release/CommitChangelog.utils.ts @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import tokens from '../tokens.json'; +import 'dotenv/config'; type GithubAuthor = { name: string; @@ -115,7 +115,7 @@ const fetchCommits = async ( method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${tokens.githubToken}`, + Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, }, body: JSON.stringify({ query, variables }), }) diff --git a/tools/scripts/release/TranslationChangelog.utils.ts b/tools/scripts/release/TranslationChangelog.utils.ts index 8237ddd4..c4c8ab58 100644 --- a/tools/scripts/release/TranslationChangelog.utils.ts +++ b/tools/scripts/release/TranslationChangelog.utils.ts @@ -10,7 +10,6 @@ import path from 'path'; import fs from 'fs'; import readline from 'readline'; import { ControlledPromise } from '@/lib/ControlledPromise.ts'; -import tokens from '../tokens.json'; import { ActionByTranslationUrlByUserUrl, ContributionsByLanguage, @@ -74,25 +73,23 @@ const extractContributionInfoFromChanges = ( const getUsernameByUserUrlMap = async ( userUrlsByTranslationUrl: UserUrlsByTranslationUrl, - authToken: string, ): Promise> => Object.fromEntries( (await Promise.all( Object.values(userUrlsByTranslationUrl) .flat() - .map(async (userUrl) => [userUrl, await getUsername(userUrl, authToken)]), + .map(async (userUrl) => [userUrl, await getUsername(userUrl)]), )) satisfies [string, string][], ); const getLanguageNameByTranslationUrl = async ( userUrlsByTranslationUrl: UserUrlsByTranslationUrl, - authToken: string, ): Promise => Object.fromEntries( (await Promise.all( Object.keys(userUrlsByTranslationUrl) .flat() - .map(async (translationUrl) => [translationUrl, await getLanguageName(translationUrl, authToken)]), + .map(async (translationUrl) => [translationUrl, await getLanguageName(translationUrl)]), )) satisfies [string, string][], ); @@ -129,8 +126,8 @@ const doesLanguageOfChangeMeetTranslatePercentThreshold = ( return meetsTranslatedPercentThreshold(langaugeStats.translated_percent); }; -const getContributorsForRange = async (url: string, authToken: string): Promise => { - const weblateChanges = await fetchWeblateChanges(url, authToken); +const getContributorsForRange = async (url: string): Promise => { + const weblateChanges = await fetchWeblateChanges(url); const weblateLanguageStats = await fetchWeblateLanguageStats(); const validWeblateChanges = weblateChanges.filter((change) => @@ -147,8 +144,8 @@ const getContributorsForRange = async (url: string, authToken: string): Promise< const { userUrlsByTranslationUrl, actionsByTranslationUrlByUserUrl } = extractContributionInfoFromChanges(validWeblateChanges); - const usernameByUserUrl = await getUsernameByUserUrlMap(userUrlsByTranslationUrl, authToken); - const languageNameByTranslationUrl = await getLanguageNameByTranslationUrl(userUrlsByTranslationUrl, authToken); + const usernameByUserUrl = await getUsernameByUserUrlMap(userUrlsByTranslationUrl); + const languageNameByTranslationUrl = await getLanguageNameByTranslationUrl(userUrlsByTranslationUrl); return getUserContributionByLanguage( userUrlsByTranslationUrl, @@ -238,7 +235,7 @@ export const createTranslationChangelog = async ( const url = `https://hosted.weblate.org/api/components/suwayomi/suwayomi-webui/changes/?timestamp_after=${timestampAfter}×tamp_before=${timestampBefore}&${creditRelevantActions.map((action) => `action=${action}`).join('&')}`; - const contributorsForRange = await getContributorsForRange(url, tokens.weblateToken); + const contributorsForRange = await getContributorsForRange(url); const knownContributorsByLanguage = await getKnownContributorsByLanguage(); const contributionInfo: { language: string; contributors: string[] }[] = Object.entries(contributorsForRange).map( diff --git a/tools/scripts/tokens.template.json b/tools/scripts/tokens.template.json deleted file mode 100644 index 1508021a..00000000 --- a/tools/scripts/tokens.template.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "githubToken": "", - "weblateToken": "" -} \ No newline at end of file diff --git a/tools/scripts/weblate/Weblate.utils.ts b/tools/scripts/weblate/Weblate.utils.ts index e795ed02..72d4dd9e 100644 --- a/tools/scripts/weblate/Weblate.utils.ts +++ b/tools/scripts/weblate/Weblate.utils.ts @@ -13,14 +13,14 @@ import { WeblateLanguageStatistic, WeblateUserPayload, } from '@/../tools/scripts/weblate/Weblate.types.ts'; -import tokens from '../tokens.json'; +import 'dotenv/config'; import { TRANSLATED_PERCENT_THRESHOLD } from '@/../tools/scripts/weblate/Weblate.constants.ts'; -export const fetchData = async (url: string, authToken: string): Promise => { +export const fetchData = async (url: string): Promise => { const response = await fetch(url, { method: 'GET', headers: { - Authorization: `Token ${authToken}`, + ...(process.env.WEBLATE_TOKEN ? { Authorization: `Token ${process.env.WEBLATE_TOKEN}` } : {}), Accept: 'application/json', }, }); @@ -34,36 +34,29 @@ export const fetchData = async (url: string, authToken: string): Promis export const fetchWeblateChanges = async ( url: string, - authToken: string, weblateChangeResults: WeblateChangeResult[] = [], ): Promise => { - const weblateChangePage = await fetchData(url, authToken); + const weblateChangePage = await fetchData(url); if (!weblateChangePage.next) { return [...weblateChangePage.results, ...weblateChangeResults]; } - return fetchWeblateChanges(weblateChangePage.next, authToken, [ - ...weblateChangePage.results, - ...weblateChangeResults, - ]); + return fetchWeblateChanges(weblateChangePage.next, [...weblateChangePage.results, ...weblateChangeResults]); }; -export const getUsername = async (url: string, authToken: string): Promise => { - const userPayload = await fetchData(url, authToken); +export const getUsername = async (url: string): Promise => { + const userPayload = await fetchData(url); return userPayload.full_name; }; -export const getLanguageName = async (url: string, authToken: string): Promise => { - const languagePayload = await fetchData(url, authToken); +export const getLanguageName = async (url: string): Promise => { + const languagePayload = await fetchData(url); return languagePayload.language.name.replace(/\(([a-zA-Z]+) Han script\)/g, '($1)'); }; export const fetchWeblateLanguageStats = async () => - fetchData( - 'https://hosted.weblate.org/api/components/suwayomi/suwayomi-webui/statistics/?format=json-flat', - tokens.weblateToken, - ); + fetchData('https://hosted.weblate.org/api/components/suwayomi/suwayomi-webui/statistics/?format=json-flat'); const dateRegex = /[0-9]{4}-[0-9]{2}-[0-9]{2}/g; const isValidIS08601Date = (date: string): boolean => !!date.match(dateRegex); diff --git a/vite.config.ts b/vite.config.ts index 12911830..5f70a880 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -14,6 +14,7 @@ import react from '@vitejs/plugin-react-swc'; import viteTsconfigPaths from 'vite-tsconfig-paths'; import legacy from '@vitejs/plugin-legacy'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; +import 'dotenv/config'; // eslint-disable-next-line import/no-default-export export default defineConfig(() => ({ @@ -21,7 +22,8 @@ export default defineConfig(() => ({ outDir: 'build', }, server: { - port: 3000, + port: Number(process.env.PORT), + allowedHosts: process.env.ALLOWED_HOSTS.split(','), }, resolve: { alias: { diff --git a/yarn.lock b/yarn.lock index 6545ecf3..71a3e2f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4511,6 +4511,11 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +dotenv@17.2.1: + version "17.2.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.1.tgz#6f32e10faf014883515538dc922a0fb8765d9b32" + integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ== + dotenv@^16.0.0: version "16.4.5" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"