Implement Labrinth Canary API flag (#5531)

This commit is contained in:
aecsocket
2026-03-11 15:28:09 +00:00
committed by GitHub
parent 086508be23
commit 3b21944a75
7 changed files with 71 additions and 1 deletions

View File

@@ -43,6 +43,7 @@ export const DEFAULT_FEATURE_FLAGS = validateValues({
hidePreviewBanner: false,
i18nDebug: false,
showDiscoverProjectButtons: false,
labrinthApiCanary: false,
} as const)
export type FeatureFlag = keyof typeof DEFAULT_FEATURE_FLAGS

View File

@@ -5,6 +5,7 @@
let cachedRateLimitKey = undefined
let rateLimitKeyPromise = undefined
const LABRINTH_CANARY_COOKIE = 'labrinth-canary=always'
async function getRateLimitKey(config) {
if (config.rateLimitKey) return config.rateLimitKey
@@ -38,6 +39,15 @@ export const useBaseFetch = async (url, options = {}, skipAuth = false) => {
options.headers['x-ratelimit-key'] = await getRateLimitKey(config)
}
if (useFeatureFlags().value.labrinthApiCanary) {
const existingCookie = options.headers.cookie
if (!existingCookie?.split('; ').includes(LABRINTH_CANARY_COOKIE)) {
options.headers.cookie = existingCookie
? `${existingCookie}; ${LABRINTH_CANARY_COOKIE}`
: LABRINTH_CANARY_COOKIE
}
}
if (!skipAuth) {
const auth = await useAuth()

View File

@@ -2,7 +2,9 @@ import {
type AbstractFeature,
type AuthConfig,
AuthFeature,
CanaryCookieFeature,
CircuitBreakerFeature,
LABRINTH_CANARY_COOKIE,
NodeAuthFeature,
nodeAuthState,
NuxtCircuitBreakerStorage,
@@ -28,7 +30,11 @@ export function createModrinthClient(
auth: Ref<{ token: string | undefined }>,
config: { apiBaseUrl: string; archonBaseUrl: string; rateLimitKey?: string },
): NuxtModrinthClient {
const flags = useFeatureFlags()
const optionalFeatures = [
new CanaryCookieFeature({
getCookie: () => (flags.value.labrinthApiCanary ? LABRINTH_CANARY_COOKIE : undefined),
}) as AbstractFeature,
import.meta.dev ? (new VerboseLoggingFeature() as AbstractFeature) : undefined,
].filter(Boolean) as AbstractFeature[]

View File

@@ -25,7 +25,11 @@ export default defineNuxtRouteMiddleware(async (to) => {
const queryClient = useAppQueryClient()
const authToken = useCookie('auth-token')
const client = useServerModrinthClient({ authToken: authToken.value || undefined })
const flags = useFeatureFlags()
const client = useServerModrinthClient({
authToken: authToken.value || undefined,
canaryCookie: flags.value.labrinthApiCanary,
})
const tags = useGeneratedState()
const projectId = to.params.id as string

View File

@@ -1,7 +1,9 @@
import {
type AuthConfig,
AuthFeature,
CanaryCookieFeature,
type FeatureConfig,
LABRINTH_CANARY_COOKIE,
type NuxtClientConfig,
NuxtModrinthClient,
} from '@modrinth/api-client'
@@ -20,6 +22,7 @@ async function getRateLimitKeyFromSecretsStore(): Promise<string | undefined> {
export interface ServerModrinthClientOptions {
event?: H3Event
authToken?: string
canaryCookie?: boolean
}
export function useServerModrinthClient(options?: ServerModrinthClientOptions): NuxtModrinthClient {
@@ -37,6 +40,10 @@ export function useServerModrinthClient(options?: ServerModrinthClientOptions):
)
}
if (options?.canaryCookie) {
features.push(new CanaryCookieFeature({ getCookie: () => LABRINTH_CANARY_COOKIE }))
}
const clientConfig: NuxtClientConfig = {
labrinthBaseUrl: apiBaseUrl,
rateLimitKey: config.rateLimitKey || getRateLimitKeyFromSecretsStore,

View File

@@ -0,0 +1,37 @@
import { AbstractFeature, type FeatureConfig } from '../core/abstract-feature'
export const LABRINTH_CANARY_COOKIE = 'labrinth-canary=always'
export interface CanaryCookieConfig extends FeatureConfig {
getCookie?: () => string | undefined | Promise<string | undefined>
}
export class CanaryCookieFeature extends AbstractFeature {
declare protected config: CanaryCookieConfig
constructor(config?: CanaryCookieConfig) {
super(config)
}
shouldApply(context: Parameters<AbstractFeature['shouldApply']>[0]): boolean {
return super.shouldApply(context) && context.options.api === 'labrinth'
}
async execute<T>(next: () => Promise<T>, context: Parameters<AbstractFeature['execute']>[1]) {
const cookie = this.config.getCookie ? await this.config.getCookie() : LABRINTH_CANARY_COOKIE
if (!cookie) {
return next()
}
const headers = { ...(context.options.headers ?? {}) }
const existingCookie = headers.cookie ?? headers.Cookie
if (!existingCookie?.split('; ').includes(cookie)) {
headers.cookie = existingCookie ? `${existingCookie}; ${cookie}` : cookie
delete headers.Cookie
context.options.headers = headers
}
return next()
}
}

View File

@@ -9,6 +9,11 @@ export {
} from './core/abstract-websocket'
export { ModrinthApiError, ModrinthServerError } from './core/errors'
export { type AuthConfig, AuthFeature } from './features/auth'
export {
type CanaryCookieConfig,
CanaryCookieFeature,
LABRINTH_CANARY_COOKIE,
} from './features/canary-cookie'
export {
type CircuitBreakerConfig,
CircuitBreakerFeature,