feat(guild): file upload/retention + channel canvas
Files: - StoredFile entity + FilesModule: multipart upload (configurable FABRIC_BACKEND_GUILD_FILE_MAX_BYTES, default 100MB; no type limit), authenticated download (Bearer or ?access_token=), hourly + on-boot retention sweep (FABRIC_BACKEND_GUILD_FILE_TTL_DAYS, default 7). - ApiKeyGuard also accepts ?access_token= (browser <img>/<a>). Canvas: - ChannelCanvas entity (one active per channel) + CanvasModule: GET / PUT|POST (share-replace, caller becomes sharer) / PATCH (sharer-only in-place update, version++) / DELETE (sharer-only). Emits canvas.updated / canvas.removed to the channel room. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,11 @@ import { introspectGuildToken } from './center-auth.js';
|
||||
@Injectable()
|
||||
export class ApiKeyGuard implements CanActivate {
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const req = context.switchToHttp().getRequest<{ path?: string; headers: Record<string, string | string[] | undefined> }>();
|
||||
const req = context.switchToHttp().getRequest<{
|
||||
path?: string;
|
||||
headers: Record<string, string | string[] | undefined>;
|
||||
query?: Record<string, string | string[] | undefined>;
|
||||
}>();
|
||||
const path = req.path ?? '';
|
||||
|
||||
// allow health check without auth
|
||||
@@ -19,7 +23,13 @@ export class ApiKeyGuard implements CanActivate {
|
||||
|
||||
const auth = req.headers['authorization'];
|
||||
const authValue = Array.isArray(auth) ? auth[0] : auth;
|
||||
const token = authValue?.startsWith('Bearer ') ? authValue.slice(7) : '';
|
||||
let token = authValue?.startsWith('Bearer ') ? authValue.slice(7) : '';
|
||||
// Browsers can't set Authorization on <img>/<a> (file downloads); accept
|
||||
// the guild token via ?access_token= as a fallback. Still introspected.
|
||||
if (!token) {
|
||||
const qt = req.query?.['access_token'];
|
||||
token = (Array.isArray(qt) ? qt[0] : qt) ?? '';
|
||||
}
|
||||
if (!token) throw new UnauthorizedException('missing bearer token');
|
||||
|
||||
const result = await introspectGuildToken(token);
|
||||
|
||||
Reference in New Issue
Block a user