setup electron app for production

This commit is contained in:
talksik
2026-04-13 10:03:23 -07:00
parent 1579de8c6d
commit 106254ef83
13 changed files with 96 additions and 21 deletions
+3 -3
View File
@@ -7,9 +7,9 @@
"private": true,
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package --arch=arm64 && electron-forge package --arch=x64",
"make": "electron-forge make --arch=arm64 && electron-forge make --arch=x64",
"release": "echo '\n⚠️ Have you bumped the version in package.json? (current: '$(node -p \"require('./package.json').version\")') [y/N]' && read -r answer && [ \"$answer\" = \"y\" ] && electron-forge publish --arch=arm64 && electron-forge publish --arch=x64 && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/arm64/RELEASES.json && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/x64/RELEASES.json",
"package": "APP_ENV=prod electron-forge package --arch=arm64 && APP_ENV=prod electron-forge package --arch=x64",
"make": "APP_ENV=prod electron-forge make --arch=arm64 && APP_ENV=prod electron-forge make --arch=x64",
"release": "echo '\n⚠️ Have you bumped the version in package.json? (current: '$(node -p \"require('./package.json').version\")') [y/N]' && read -r answer && [ \"$answer\" = \"y\" ] && APP_ENV=prod electron-forge publish --arch=arm64 && APP_ENV=prod electron-forge publish --arch=x64 && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/arm64/RELEASES.json && gsutil setmeta -h 'Cache-Control:no-cache, no-store, must-revalidate' gs://flowy-releases/llink/darwin/x64/RELEASES.json",
"invalidate-gcs-cache": "gsutil -h \"Cache-Control:no-cache, no-store, max-age=0\" cp gs://flowy-releases/llink/darwin/arm64/RELEASES.json gs://flowy-releases/llink/darwin/arm64/RELEASES.json ; gsutil -h \"Cache-Control:no-cache, no-store, max-age=0\" cp gs://flowy-releases/llink/darwin/x64/RELEASES.json gs://flowy-releases/llink/darwin/x64/RELEASES.json",
"lint": "eslint --ext .ts,.tsx .",
"compile": "npx tsc --noEmit 2>&1 | grep '^src/'"
+2 -1
View File
@@ -1,3 +1,4 @@
import { appConfig } from "@/config/env";
import { useSessionStore } from "@/stores/session-store";
import type { z } from "zod";
import {
@@ -218,7 +219,7 @@ class ApiClient {
}
export const apiClient = new ApiClient({
baseUrl: "https://orion.dev.flowy.live",
baseUrl: appConfig.orionUrl,
getToken: () => useSessionStore.getState().token,
onUnauthorized: () => useSessionStore.getState().clearToken(),
});
+56
View File
@@ -0,0 +1,56 @@
// Build-time environment selection.
//
// __APP_ENV__ is replaced by Vite via `define` in each vite.*.config.ts. Selected
// via the APP_ENV env var at build/dev time (defaults to 'dev'). `yarn package`,
// `yarn make`, and `yarn release` set APP_ENV=prod.
//
// Firebase web config is public by design (security is enforced via Firestore
// rules + App Check), so both configs live in source. To refresh, run:
// cd infra/gcp/{dev,prod} && terraform output -json firebase_config
declare const __APP_ENV__: "dev" | "prod";
type FirebaseConfig = {
apiKey: string;
appId: string;
authDomain: string;
messagingSenderId: string;
projectId: string;
storageBucket: string;
};
type AppConfig = {
orionUrl: string;
pusherUrl: string;
firebase: FirebaseConfig;
};
const configs: Record<"dev" | "prod", AppConfig> = {
dev: {
orionUrl: "https://orion.dev.flowy.live",
pusherUrl: "wss://pusher.dev.flowy.live/ws",
firebase: {
apiKey: "AIzaSyDF_fbk7tDY9tNxUiOwwi--2nYZWZygMGk",
appId: "1:1006580076785:web:e2a0736d60a78e02b15950",
authDomain: "flowy-dev-440017.firebaseapp.com",
messagingSenderId: "1006580076785",
projectId: "flowy-dev-440017",
storageBucket: "flowy-dev-440017.firebasestorage.app",
},
},
prod: {
orionUrl: "https://orion.flowy.live",
pusherUrl: "wss://pusher.flowy.live/ws",
firebase: {
apiKey: "AIzaSyB8it3SKr9DHScHGlpu4uwWicvt8wzjdBg",
appId: "1:68063426854:web:5054f16f50898f5706e9e7",
authDomain: "flowy-prod-440017.firebaseapp.com",
messagingSenderId: "68063426854",
projectId: "flowy-prod-440017",
storageBucket: "flowy-prod-440017.firebasestorage.app",
},
},
};
export const appConfig: AppConfig = configs[__APP_ENV__];
export const appEnv: "dev" | "prod" = __APP_ENV__;
+2 -10
View File
@@ -1,16 +1,8 @@
import { initializeApp } from 'firebase/app';
import { getFirestore } from "firebase/firestore";
import { appConfig } from "@/config/env";
const firebaseConfig = {
apiKey: "AIzaSyDF_fbk7tDY9tNxUiOwwi--2nYZWZygMGk",
appId: "1:1006580076785:web:e2a0736d60a78e02b15950",
authDomain: "flowy-dev-440017.firebaseapp.com",
messagingSenderId: "1006580076785",
projectId: "flowy-dev-440017",
storageBucket: "flowy-dev-440017.firebasestorage.app",
};
export const firebaseApp = initializeApp(firebaseConfig);
export const firebaseApp = initializeApp(appConfig.firebase);
export const firestoreDb = getFirestore(firebaseApp);
// simplifying setup to debug production issues
+2 -4
View File
@@ -8,13 +8,11 @@ import {
} from "react";
import { PusherClient, type ConnectionState } from "./pusher-client";
import { useSessionStore } from "@/stores/session-store";
import { appConfig } from "@/config/env";
const PusherContext = createContext<PusherClient | null>(null);
const PusherStateContext = createContext<ConnectionState>("disconnected");
// TODO: make this configurable per environment
const PUSHER_URL = "wss://pusher.dev.flowy.live/ws";
export function PusherProvider({ children }: { children: ReactNode }) {
const token = useSessionStore((s) => s.token);
const clientRef = useRef<PusherClient | null>(null);
@@ -33,7 +31,7 @@ export function PusherProvider({ children }: { children: ReactNode }) {
}
const client = new PusherClient({
url: PUSHER_URL,
url: appConfig.pusherUrl,
getToken: () => useSessionStore.getState().token,
});
+2 -1
View File
@@ -4,6 +4,7 @@ import started from 'electron-squirrel-startup';
import { updateElectronApp, UpdateSourceType } from 'update-electron-app';
import type { LinkMetadata } from './lib/link-metadata';
import { appConfig } from './config/env';
if (app.isPackaged) {
updateElectronApp({
@@ -484,7 +485,7 @@ app.on('ready', () => {
// The server doesn't handle OPTIONS preflight, so we intercept at the
// Electron network layer: inject CORS headers and return 200 for preflight.
session.defaultSession.webRequest.onHeadersReceived(
{ urls: ['https://orion.dev.flowy.live/*', 'https://storage.googleapis.com/*'] },
{ urls: [`${appConfig.orionUrl}/*`, 'https://storage.googleapis.com/*'] },
(details, callback) => {
const headers = { ...details.responseHeaders };
headers['access-control-allow-origin'] = ['*'];
+2
View File
@@ -2,6 +2,7 @@ import path from "path"
import tailwindcss from "@tailwindcss/vite"
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { defineEnv } from './vite.env';
// https://vitejs.dev/config
export default defineConfig({
@@ -20,4 +21,5 @@ export default defineConfig({
allow: [path.resolve(__dirname)],
},
},
define: defineEnv,
});
+13
View File
@@ -0,0 +1,13 @@
// Shared Vite `define` block that injects the build-time __APP_ENV__ constant
// consumed by src/config/env.ts. Sourced from process.env.APP_ENV (default 'dev').
const raw = process.env.APP_ENV ?? "dev";
if (raw !== "dev" && raw !== "prod") {
throw new Error(`APP_ENV must be "dev" or "prod", got: ${raw}`);
}
export const appEnv = raw;
export const defineEnv = {
__APP_ENV__: JSON.stringify(appEnv),
};
+2
View File
@@ -2,6 +2,7 @@ import path from "path"
import tailwindcss from "@tailwindcss/vite"
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { defineEnv } from './vite.env';
// https://vitejs.dev/config
export default defineConfig({
@@ -20,4 +21,5 @@ export default defineConfig({
allow: [path.resolve(__dirname)],
},
},
define: defineEnv,
});
+4 -1
View File
@@ -1,4 +1,7 @@
import { defineConfig } from 'vite';
import { defineEnv } from './vite.env';
// https://vitejs.dev/config
export default defineConfig({});
export default defineConfig({
define: defineEnv,
});
+4 -1
View File
@@ -1,4 +1,7 @@
import { defineConfig } from 'vite';
import { defineEnv } from './vite.env';
// https://vitejs.dev/config
export default defineConfig({});
export default defineConfig({
define: defineEnv,
});
+2
View File
@@ -2,6 +2,7 @@ import path from "path"
import tailwindcss from "@tailwindcss/vite"
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { defineEnv } from './vite.env';
// https://vitejs.dev/config
export default defineConfig({
@@ -11,4 +12,5 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src"),
},
},
define: defineEnv,
});
+2
View File
@@ -2,6 +2,7 @@ import path from "path"
import tailwindcss from "@tailwindcss/vite"
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { defineEnv } from './vite.env';
// https://vitejs.dev/config
export default defineConfig({
@@ -20,4 +21,5 @@ export default defineConfig({
allow: [path.resolve(__dirname)],
},
},
define: defineEnv,
});