Files
fluxer/fluxer_api/src/api/instance/InstanceController.ts
T

155 lines
5.4 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import {Config} from '@app/api/Config';
import type {GifService} from '@app/api/gif/GifService';
import type {IGifProvider} from '@app/api/gif/IGifProvider';
import {
type DiscoveryValidators,
isDiscoveryNotModified,
nextDiscoveryValidators,
} from '@app/api/instance/DiscoveryValidators';
import type {InstanceCaptchaEffectiveConfig} from '@app/api/instance/InstanceConfigRepository';
import {RateLimitMiddleware} from '@app/api/middleware/RateLimitMiddleware';
import {OpenAPI} from '@app/api/middleware/ResponseTypeMiddleware';
import {RateLimitConfigs} from '@app/api/RateLimitConfig';
import type {HonoEnv} from '@app/api/types/HonoEnv';
import {API_CODE_VERSION} from '@fluxer/constants/src/AppConstants';
import {buildDiscoveryResponse, type DiscoveryStaticInput} from '@fluxer/instance_bootstrap/src/BuildDiscovery';
import type {InstanceAppPublic} from '@fluxer/instance_bootstrap/src/Types';
import {toDomainMigrationDiscovery} from '@fluxer/schema/src/domains/admin/DomainMigrationSchemas';
import {WellKnownFluxerResponse} from '@fluxer/schema/src/domains/instance/InstanceSchemas';
import type {Hono} from 'hono';
let discoveryValidators: DiscoveryValidators | null = null;
function buildDiscoveryStaticInput(
gifService: GifService | undefined,
appPublic: InstanceAppPublic,
runtime: {
captcha: InstanceCaptchaEffectiveConfig;
emailEnabled: boolean;
},
): DiscoveryStaticInput {
const apiClientEndpoint = Config.endpoints.apiClient;
const apiPublicEndpoint = Config.endpoints.apiPublic;
let gifProvider: IGifProvider | undefined;
if (gifService !== undefined) {
gifProvider = gifService.getProvider();
}
let gifProviderName = 'klipy';
let gifDisplayName = 'Klipy';
let gifAttributionRequired = false;
if (gifProvider !== undefined) {
gifProviderName = gifProvider.meta.name;
gifDisplayName = gifProvider.meta.displayName;
gifAttributionRequired = gifProvider.meta.attributionRequired;
}
return {
apiCodeVersion: API_CODE_VERSION,
endpoints: {
api: apiClientEndpoint,
api_client: apiClientEndpoint,
api_public: apiPublicEndpoint,
gateway: Config.endpoints.gateway,
media: Config.endpoints.media,
static_cdn: Config.endpoints.staticCdn,
marketing: Config.endpoints.marketing,
admin: Config.endpoints.admin,
invite: Config.endpoints.invite,
gift: Config.endpoints.gift,
webapp: Config.endpoints.webApp,
},
captcha: {
provider: runtime.captcha.provider,
hcaptcha_site_key: runtime.captcha.provider === 'hcaptcha' ? runtime.captcha.hcaptcha_site_key : null,
turnstile_site_key: runtime.captcha.provider === 'turnstile' ? runtime.captcha.turnstile_site_key : null,
},
features: {
voice_enabled: Config.voice.enabled,
stripe_enabled: Config.stripe.enabled,
self_hosted: Config.instance.selfHosted,
presigned_attachment_uploads: Config.presignedAttachmentUploadsEnabled,
emails_enabled: runtime.emailEnabled,
},
gif: {
provider: gifProviderName,
display_name: gifDisplayName,
attribution_required: gifAttributionRequired,
},
push: {
public_vapid_key: Config.push.publicVapidKey ?? null,
},
appPublic,
};
}
export function InstanceController(app: Hono<HonoEnv>) {
app.get(
'/.well-known/fluxer',
RateLimitMiddleware(RateLimitConfigs.INSTANCE_INFO),
OpenAPI({
operationId: 'get_well_known_fluxer',
summary: 'Get instance discovery document',
responseSchema: WellKnownFluxerResponse,
statusCode: 200,
security: [],
tags: ['Instance'],
description:
'Returns the instance discovery document including API endpoints, feature flags, and limits. This is the canonical discovery endpoint for all Fluxer clients.',
}),
async (ctx) => {
ctx.header('Access-Control-Allow-Origin', '*');
const gifService = ctx.get('gifService') as GifService | undefined;
const limits = ctx.get('limitConfigService').getConfigWireFormat();
const sso = await ctx.get('ssoService').getPublicStatus();
const instanceConfigRepository = ctx.get('instanceConfigRepository');
const [registration, community, services, appPublicConfig, captcha, email, domainMigration] = await Promise.all([
instanceConfigRepository.getRegistrationPublicConfig(),
instanceConfigRepository.getInstanceCommunityPublicConfig(),
instanceConfigRepository.getResolvedServicesConfig(),
instanceConfigRepository.getAppPublicConfig(),
instanceConfigRepository.getEffectiveCaptchaConfig(),
instanceConfigRepository.getEffectiveEmailConfig(),
instanceConfigRepository.getDomainMigrationConfig(),
]);
const discovery = buildDiscoveryResponse(
buildDiscoveryStaticInput(
gifService,
{
...appPublicConfig,
setup: {
...appPublicConfig.setup,
admin_url: Config.endpoints.admin || null,
},
},
{
captcha,
emailEnabled: email.enabled,
},
),
{
sso,
registration,
community,
services,
limits,
},
);
const response = {...discovery, domain_migration: toDomainMigrationDiscovery(domainMigration)};
discoveryValidators = nextDiscoveryValidators(response, discoveryValidators);
ctx.header('ETag', discoveryValidators.etag);
ctx.header('Last-Modified', discoveryValidators.lastModified.toUTCString());
if (
isDiscoveryNotModified(
discoveryValidators,
ctx.req.header('If-None-Match'),
ctx.req.header('If-Modified-Since'),
)
) {
return ctx.body(null, 304);
}
return ctx.json(response);
},
);
}