From 165f81758ea0482f5bd4bbec8fd7c46d0a5203db Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Mon, 6 Jul 2026 09:16:55 +0800 Subject: [PATCH 1/3] fix: build error --- mono/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/mono/Dockerfile b/mono/Dockerfile index bb0529f4f..9dcaea0d5 100644 --- a/mono/Dockerfile +++ b/mono/Dockerfile @@ -38,6 +38,7 @@ COPY orion/buck/Cargo.toml orion/buck/ COPY orion-scheduler/Cargo.toml orion-scheduler/ COPY orion-server/Cargo.toml orion-server/ COPY clients/orion-client/Cargo.toml clients/orion-client/ +COPY clients/orion-scheduler-client/Cargo.toml clients/orion-scheduler-client/ COPY saturn/Cargo.toml saturn/ COPY vault/Cargo.toml vault/ From 7ba2d1db2504084b7e9d91fa8d210b299af6db9c Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Tue, 7 Jul 2026 15:44:19 +0800 Subject: [PATCH 2/3] feat(oauth): make Campsite API session cookie name configurable Allow per-environment overrides of the Campsite session cookie used by the web UI and mono OAuth store, defaulting to _campsite_api_session. --- common/src/config/mod.rs | 9 +++++++++ docker/demo/.env.example | 1 + docker/demo/docker-compose.demo.yml | 1 + docker/deployment/.env.example | 1 + mono/src/api/oauth/api_store.rs | 2 +- mono/src/api/oauth/campsite_store.rs | 16 +++++++++------- mono/src/server/http_server.rs | 9 ++++++--- mono/tests/campsite_api_store_tests.rs | 5 +++-- mono/tests/login_user_extractor_tests.rs | 5 +++-- moon/apps/web/.env.runtime | 4 +++- moon/apps/web/docker-entrypoint.sh | 3 ++- moon/apps/web/utils/apiCookieHeaders.ts | 8 ++++---- moon/apps/web/utils/queryClient.ts | 15 ++++++++++----- moon/packages/config/src/index.ts | 4 ++++ 14 files changed, 57 insertions(+), 26 deletions(-) diff --git a/common/src/config/mod.rs b/common/src/config/mod.rs index 5ade091cc..68985c341 100644 --- a/common/src/config/mod.rs +++ b/common/src/config/mod.rs @@ -660,6 +660,14 @@ pub struct OauthConfig { #[serde(default)] pub api_store_backend: OauthApiStoreBackend, pub allowed_cors_origins: Vec, + #[serde(default = "default_campsite_api_session_cookie")] + pub campsite_api_session_cookie: String, +} + +pub const DEFAULT_CAMPSITE_API_SESSION_COOKIE: &str = "_campsite_api_session"; + +fn default_campsite_api_session_cookie() -> String { + DEFAULT_CAMPSITE_API_SESSION_COOKIE.to_string() } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] @@ -685,6 +693,7 @@ impl Default for OauthConfig { .into_iter() .map(|s| s.to_string()) .collect(), + campsite_api_session_cookie: default_campsite_api_session_cookie(), } } } diff --git a/docker/demo/.env.example b/docker/demo/.env.example index c128903b0..519b02079 100644 --- a/docker/demo/.env.example +++ b/docker/demo/.env.example @@ -99,6 +99,7 @@ MEGA_BUILD__ENABLE_BUILD=true MEGA_BUILD__ORION_SERVER=http://orion_server:8004 MEGA_OAUTH__CAMPSITE_API_DOMAIN=http://api.gitmono.local:18080 +MEGA_OAUTH__CAMPSITE_API_SESSION_COOKIE=_campsite_api_session MEGA_OAUTH__UI_DOMAIN=http://app.gitmono.local MEGA_OAUTH__COOKIE_DOMAIN=gitmono.local MEGA_OAUTH__ALLOWED_CORS_ORIGINS="http://app.gitmono.local" diff --git a/docker/demo/docker-compose.demo.yml b/docker/demo/docker-compose.demo.yml index a55dd1018..1a8ec4bef 100644 --- a/docker/demo/docker-compose.demo.yml +++ b/docker/demo/docker-compose.demo.yml @@ -150,6 +150,7 @@ services: # Campsite integration (OAuth / SSO etc.) MEGA_OAUTH__CAMPSITE_API_DOMAIN: ${MEGA_OAUTH__CAMPSITE_API_DOMAIN:-http://campsite_api:8080} + MEGA_OAUTH__CAMPSITE_API_SESSION_COOKIE: ${MEGA_OAUTH__CAMPSITE_API_SESSION_COOKIE:-_campsite_api_session} MEGA_OAUTH__UI_DOMAIN: ${MEGA_OAUTH__UI_DOMAIN:-http://app.gitmono.local} MEGA_OAUTH__COOKIE_DOMAIN: ${MEGA_OAUTH__COOKIE_DOMAIN:-localhost} # Note: allowed_cors_origins expects an array format in TOML diff --git a/docker/deployment/.env.example b/docker/deployment/.env.example index bc917216a..667c9b2cc 100644 --- a/docker/deployment/.env.example +++ b/docker/deployment/.env.example @@ -92,6 +92,7 @@ MEGA_BUILD__ENABLE_BUILD=true MEGA_BUILD__ORION_SERVER=http://orion_server:8004 MEGA_OAUTH__CAMPSITE_API_DOMAIN=http://api.gitmono.local:18080 +MEGA_OAUTH__CAMPSITE_API_SESSION_COOKIE=_campsite_api_session MEGA_OAUTH__UI_DOMAIN=http://app.gitmono.local MEGA_OAUTH__COOKIE_DOMAIN=gitmono.local MEGA_OAUTH__ALLOWED_CORS_ORIGINS="http://app.gitmono.local" diff --git a/mono/src/api/oauth/api_store.rs b/mono/src/api/oauth/api_store.rs index 7adeb2997..bde917484 100644 --- a/mono/src/api/oauth/api_store.rs +++ b/mono/src/api/oauth/api_store.rs @@ -16,7 +16,7 @@ pub enum OAuthApiStore { } impl OAuthApiStore { - pub fn session_cookie_name(&self) -> &'static str { + pub fn session_cookie_name(&self) -> &str { match self { OAuthApiStore::Campsite(store) => store.session_cookie_name(), OAuthApiStore::Tinyship(store) => store.session_cookie_name(), diff --git a/mono/src/api/oauth/campsite_store.rs b/mono/src/api/oauth/campsite_store.rs index e2feeb662..c303d7fd8 100644 --- a/mono/src/api/oauth/campsite_store.rs +++ b/mono/src/api/oauth/campsite_store.rs @@ -12,13 +12,11 @@ use tower_sessions::{ use crate::api::oauth::model::{CampsiteUserJson, LoginUser}; -static CAMPSITE_API_COOKIE: &str = "_campsite_api_session"; - #[derive(Debug, Clone)] pub struct CampsiteApiStore { client: Arc, - // cookie_store: Arc, api_base_url: String, + session_cookie: String, } #[async_trait] @@ -41,11 +39,11 @@ impl SessionStore for CampsiteApiStore { } impl CampsiteApiStore { - pub fn session_cookie_name(&self) -> &'static str { - CAMPSITE_API_COOKIE + pub fn session_cookie_name(&self) -> &str { + &self.session_cookie } - pub fn new(api_base_url: String) -> Self { + pub fn new(api_base_url: String, session_cookie: String) -> Self { let client = Client::builder() .no_proxy() .build() @@ -53,6 +51,7 @@ impl CampsiteApiStore { Self { client: Arc::new(client), api_base_url, + session_cookie, } } @@ -68,7 +67,10 @@ impl CampsiteApiStore { let resp = self .client .get(url) - .header(COOKIE, format!("{}={}", CAMPSITE_API_COOKIE, cookie_value)) + .header( + COOKIE, + format!("{}={}", self.session_cookie_name(), cookie_value), + ) .send() .await .context("failed to send request to campsite API")?; diff --git a/mono/src/server/http_server.rs b/mono/src/server/http_server.rs index 91d328a0a..469220072 100644 --- a/mono/src/server/http_server.rs +++ b/mono/src/server/http_server.rs @@ -396,9 +396,12 @@ pub async fn app(ctx: AppContext, host: String, port: u16) -> Router { git_object_cache, build_dispatch, Some(match oauth_config.api_store_backend { - common::config::OauthApiStoreBackend::Campsite => { - OAuthApiStore::Campsite(CampsiteApiStore::new(oauth_config.campsite_api_domain)) - } + common::config::OauthApiStoreBackend::Campsite => OAuthApiStore::Campsite( + CampsiteApiStore::new( + oauth_config.campsite_api_domain.clone(), + oauth_config.campsite_api_session_cookie.clone(), + ), + ), common::config::OauthApiStoreBackend::Tinyship => { OAuthApiStore::Tinyship(TinyshipApiStore::new(oauth_config.tinyship_api_domain)) } diff --git a/mono/tests/campsite_api_store_tests.rs b/mono/tests/campsite_api_store_tests.rs index 41b4fa406..2acfc25a9 100644 --- a/mono/tests/campsite_api_store_tests.rs +++ b/mono/tests/campsite_api_store_tests.rs @@ -41,8 +41,9 @@ use common::*; use qlean::{Distro, GuestArch, Image, ImageConfig, MachineConfig, with_machine}; use serde_json::Value; +use common::config::DEFAULT_CAMPSITE_API_SESSION_COOKIE; + const TEST_COOKIE: &str = "test_session_cookie"; -const CAMPSITE_API_COOKIE_NAME: &str = "_campsite_api_session"; // ============================================================================ // Test phases - directly calling Campsite API @@ -54,7 +55,7 @@ async fn call_campsite_api( cookie: Option<&str>, ) -> Result<(u16, Value)> { let cookie_arg = cookie - .map(|c| format!("Cookie: {}={}", CAMPSITE_API_COOKIE_NAME, c)) + .map(|c| format!("Cookie: {}={}", DEFAULT_CAMPSITE_API_SESSION_COOKIE, c)) .unwrap_or_default(); let cmd = if cookie.is_some() { diff --git a/mono/tests/login_user_extractor_tests.rs b/mono/tests/login_user_extractor_tests.rs index 1a2c0e8d6..e6131925e 100644 --- a/mono/tests/login_user_extractor_tests.rs +++ b/mono/tests/login_user_extractor_tests.rs @@ -36,6 +36,7 @@ use std::time::Duration; use anyhow::{Context, Result}; use common::*; +use common::config::DEFAULT_CAMPSITE_API_SESSION_COOKIE; use qlean::{Distro, GuestArch, Image, ImageConfig, MachineConfig, with_machine}; use serde_json::Value; @@ -47,11 +48,11 @@ const CAMPSITE_API_PORT: u16 = 8080; /// Call Campsite API /v1/users/me endpoint async fn call_users_me(vm: &mut qlean::Machine, cookie: &str) -> Result<(u16, Option)> { - // Format cookie with prefix: _campsite_api_session= + // Format cookie with prefix: {session_cookie}= let cookie_header = if cookie.is_empty() { "".to_string() } else { - format!("_campsite_api_session={}", cookie) + format!("{}={}", DEFAULT_CAMPSITE_API_SESSION_COOKIE, cookie) }; let cmd = if cookie.is_empty() { diff --git a/moon/apps/web/.env.runtime b/moon/apps/web/.env.runtime index b8da280d2..722421bfc 100644 --- a/moon/apps/web/.env.runtime +++ b/moon/apps/web/.env.runtime @@ -12,7 +12,8 @@ # Provide the real values at runtime (ECS task definition / Cloud Run env): # NEXT_PUBLIC_API_URL, NEXT_PUBLIC_INTERNAL_API_URL, NEXT_PUBLIC_MONO_API_URL, # NEXT_PUBLIC_ORION_API_URL, NEXT_PUBLIC_AUTH_URL, NEXT_PUBLIC_WEB_URL, -# NEXT_PUBLIC_SYNC_URL, NEXT_PUBLIC_CRATES_PRO_URL +# NEXT_PUBLIC_SYNC_URL, NEXT_PUBLIC_CRATES_PRO_URL, +# NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE # ============================================================================= NEXT_PUBLIC_API_URL=https://rt-api.placeholder.local @@ -23,3 +24,4 @@ NEXT_PUBLIC_AUTH_URL=https://rt-auth.placeholder.local NEXT_PUBLIC_WEB_URL=https://rt-web.placeholder.local NEXT_PUBLIC_SYNC_URL=wss://rt-sync.placeholder.local NEXT_PUBLIC_CRATES_PRO_URL=https://rt-crates-pro.placeholder.local +NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE=__rt_campsite_api_session_cookie__ diff --git a/moon/apps/web/docker-entrypoint.sh b/moon/apps/web/docker-entrypoint.sh index d165a75d5..344301d62 100755 --- a/moon/apps/web/docker-entrypoint.sh +++ b/moon/apps/web/docker-entrypoint.sh @@ -25,7 +25,8 @@ https://rt-orion-api.placeholder.local${TAB}NEXT_PUBLIC_ORION_API_URL https://rt-auth.placeholder.local${TAB}NEXT_PUBLIC_AUTH_URL https://rt-web.placeholder.local${TAB}NEXT_PUBLIC_WEB_URL wss://rt-sync.placeholder.local${TAB}NEXT_PUBLIC_SYNC_URL -https://rt-crates-pro.placeholder.local${TAB}NEXT_PUBLIC_CRATES_PRO_URL" +https://rt-crates-pro.placeholder.local${TAB}NEXT_PUBLIC_CRATES_PRO_URL +__rt_campsite_api_session_cookie__${TAB}NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE" # Escape a string for safe use on the left (regex) side of a sed s||| command. escape_regex() { printf '%s' "$1" | sed -e 's/[.[\*^$()+?{|/\\]/\\&/g'; } diff --git a/moon/apps/web/utils/apiCookieHeaders.ts b/moon/apps/web/utils/apiCookieHeaders.ts index 0c0538def..c7c46fe90 100644 --- a/moon/apps/web/utils/apiCookieHeaders.ts +++ b/moon/apps/web/utils/apiCookieHeaders.ts @@ -1,16 +1,16 @@ import { NextApiRequestCookies } from 'next/dist/server/api-utils' -const ApiCookieName = '_campsite_api_session' +import { CAMPSITE_API_SESSION_COOKIE } from '@gitmono/config' export const SsrSecretHeader: Record = { 'x-campsite-ssr-secret': process.env.SSR_SECRET || '' } export function apiCookieHeaders(cookies: NextApiRequestCookies) { let headers: Record = {} - if (cookies[ApiCookieName]) { - const apiCookie = encodeURIComponent(cookies[ApiCookieName]) + if (cookies[CAMPSITE_API_SESSION_COOKIE]) { + const apiCookie = encodeURIComponent(cookies[CAMPSITE_API_SESSION_COOKIE]) - headers['Cookie'] = `${ApiCookieName}=${apiCookie}` + headers['Cookie'] = `${CAMPSITE_API_SESSION_COOKIE}=${apiCookie}` } return { ...headers, ...SsrSecretHeader } diff --git a/moon/apps/web/utils/queryClient.ts b/moon/apps/web/utils/queryClient.ts index c916af111..7423108fb 100644 --- a/moon/apps/web/utils/queryClient.ts +++ b/moon/apps/web/utils/queryClient.ts @@ -1,6 +1,13 @@ import { InfiniteData, QueryClient, QueryKey } from '@tanstack/react-query' -import { MONO_API_URL, ORION_API_URL, RAILS_API_URL, RAILS_AUTH_URL, RAILS_INTERNAL_API_URL } from '@gitmono/config' +import { + CAMPSITE_API_SESSION_COOKIE, + MONO_API_URL, + ORION_API_URL, + RAILS_API_URL, + RAILS_AUTH_URL, + RAILS_INTERNAL_API_URL +} from '@gitmono/config' import { Api, ApiError, DataTag } from '@gitmono/types' import { ApiErrorResponse } from './types' @@ -63,8 +70,6 @@ export function reauthorizeSSOUrl({ type Method = 'DELETE' | 'POST' | 'PUT' -const ApiCookieName = '_campsite_api_session' - interface FetcherProps { method?: Method body?: unknown @@ -78,9 +83,9 @@ export async function fetcher(url: string, { method, body, cookies }: Fetcher let credentials: RequestCredentials | undefined if (cookies) { - const apiCookie = encodeURIComponent(cookies[ApiCookieName]) + const apiCookie = encodeURIComponent(cookies[CAMPSITE_API_SESSION_COOKIE]) - headers.append('Cookie', `${ApiCookieName}=${apiCookie}`) + headers.append('Cookie', `${CAMPSITE_API_SESSION_COOKIE}=${apiCookie}`) } else { credentials = 'include' } diff --git a/moon/packages/config/src/index.ts b/moon/packages/config/src/index.ts index 20e409f78..e0b906e75 100644 --- a/moon/packages/config/src/index.ts +++ b/moon/packages/config/src/index.ts @@ -32,6 +32,10 @@ export const ORION_API_URL = process.env.NEXT_PUBLIC_ORION_API_URL || 'https://o // eslint-disable-next-line turbo/no-undeclared-env-vars export const RAILS_AUTH_URL = process.env.NEXT_PUBLIC_AUTH_URL || 'https://auth.gitmega.com' +// eslint-disable-next-line turbo/no-undeclared-env-vars +export const CAMPSITE_API_SESSION_COOKIE = + process.env.NEXT_PUBLIC_CAMPSITE_API_SESSION_COOKIE || '_campsite_api_session' + /* Not using an env variable because we use this variable in the browser, which requires extra config with Next.js to send env variables to the browser. From 36794b5eeb6b7ae37ea968461c040765ddc37d02 Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Tue, 7 Jul 2026 15:59:40 +0800 Subject: [PATCH 3/3] fix ui build err --- mono/src/server/http_server.rs | 8 ++++---- mono/tests/campsite_api_store_tests.rs | 3 +-- mono/tests/login_user_extractor_tests.rs | 2 +- moon/apps/web/utils/apiCookieHeaders.ts | 5 +++-- moon/apps/web/utils/queryClient.ts | 7 +++++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/mono/src/server/http_server.rs b/mono/src/server/http_server.rs index 469220072..24d4eced0 100644 --- a/mono/src/server/http_server.rs +++ b/mono/src/server/http_server.rs @@ -396,12 +396,12 @@ pub async fn app(ctx: AppContext, host: String, port: u16) -> Router { git_object_cache, build_dispatch, Some(match oauth_config.api_store_backend { - common::config::OauthApiStoreBackend::Campsite => OAuthApiStore::Campsite( - CampsiteApiStore::new( + common::config::OauthApiStoreBackend::Campsite => { + OAuthApiStore::Campsite(CampsiteApiStore::new( oauth_config.campsite_api_domain.clone(), oauth_config.campsite_api_session_cookie.clone(), - ), - ), + )) + } common::config::OauthApiStoreBackend::Tinyship => { OAuthApiStore::Tinyship(TinyshipApiStore::new(oauth_config.tinyship_api_domain)) } diff --git a/mono/tests/campsite_api_store_tests.rs b/mono/tests/campsite_api_store_tests.rs index 2acfc25a9..f49356620 100644 --- a/mono/tests/campsite_api_store_tests.rs +++ b/mono/tests/campsite_api_store_tests.rs @@ -36,13 +36,12 @@ mod common; +use ::common::config::DEFAULT_CAMPSITE_API_SESSION_COOKIE; use anyhow::{Context, Result}; use common::*; use qlean::{Distro, GuestArch, Image, ImageConfig, MachineConfig, with_machine}; use serde_json::Value; -use common::config::DEFAULT_CAMPSITE_API_SESSION_COOKIE; - const TEST_COOKIE: &str = "test_session_cookie"; // ============================================================================ diff --git a/mono/tests/login_user_extractor_tests.rs b/mono/tests/login_user_extractor_tests.rs index e6131925e..9e1872756 100644 --- a/mono/tests/login_user_extractor_tests.rs +++ b/mono/tests/login_user_extractor_tests.rs @@ -34,9 +34,9 @@ mod common; use std::time::Duration; +use ::common::config::DEFAULT_CAMPSITE_API_SESSION_COOKIE; use anyhow::{Context, Result}; use common::*; -use common::config::DEFAULT_CAMPSITE_API_SESSION_COOKIE; use qlean::{Distro, GuestArch, Image, ImageConfig, MachineConfig, with_machine}; use serde_json::Value; diff --git a/moon/apps/web/utils/apiCookieHeaders.ts b/moon/apps/web/utils/apiCookieHeaders.ts index c7c46fe90..9e6b8601b 100644 --- a/moon/apps/web/utils/apiCookieHeaders.ts +++ b/moon/apps/web/utils/apiCookieHeaders.ts @@ -7,8 +7,9 @@ export const SsrSecretHeader: Record = { 'x-campsite-ssr-secret' export function apiCookieHeaders(cookies: NextApiRequestCookies) { let headers: Record = {} - if (cookies[CAMPSITE_API_SESSION_COOKIE]) { - const apiCookie = encodeURIComponent(cookies[CAMPSITE_API_SESSION_COOKIE]) + const sessionCookie = cookies[CAMPSITE_API_SESSION_COOKIE] + if (sessionCookie) { + const apiCookie = encodeURIComponent(sessionCookie) headers['Cookie'] = `${CAMPSITE_API_SESSION_COOKIE}=${apiCookie}` } diff --git a/moon/apps/web/utils/queryClient.ts b/moon/apps/web/utils/queryClient.ts index 7423108fb..b65444f3b 100644 --- a/moon/apps/web/utils/queryClient.ts +++ b/moon/apps/web/utils/queryClient.ts @@ -83,9 +83,12 @@ export async function fetcher(url: string, { method, body, cookies }: Fetcher let credentials: RequestCredentials | undefined if (cookies) { - const apiCookie = encodeURIComponent(cookies[CAMPSITE_API_SESSION_COOKIE]) + const sessionCookie = cookies[CAMPSITE_API_SESSION_COOKIE] + if (sessionCookie) { + const apiCookie = encodeURIComponent(sessionCookie) - headers.append('Cookie', `${CAMPSITE_API_SESSION_COOKIE}=${apiCookie}`) + headers.append('Cookie', `${CAMPSITE_API_SESSION_COOKIE}=${apiCookie}`) + } } else { credentials = 'include' }