Implement Labrinth Canary API flag (#5531)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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[]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
37
packages/api-client/src/features/canary-cookie.ts
Normal file
37
packages/api-client/src/features/canary-cookie.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user