All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m52s
CI / Build container images (pull_request) Successful in 3m55s
CI / Auth e2e pack (pull_request) Successful in 7m52s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 16s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m55s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m34s
CI / Import/export fidelity gate (push) Successful in 58s
Chosen path: an instance master switch following the api.enabled/ mcp.enabled pattern — while off, both feed routes AND the feed-token management answer 404 (existence hidden). Default ON: feeds predate the switch, existing instances and their subscribed readers keep working; the VS-NfD reference configuration (#227) turns it off. Admin UI gets the toggle next to the API/MCP switches (i18n de+en). Moving the token out of the query string is documented as rejected: a path segment lands in the same proxy and request logs, and feed readers cannot send headers — that is why the credential is in the URL at all. What DID leak was our own request log (pino logs req.url): the req serializer now masks ?token= values (common/mask-token-param.ts), so no code path logs the credential. Refs #191 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ
64 lines
1.8 KiB
TypeScript
64 lines
1.8 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
HttpCode,
|
|
NotFoundException,
|
|
Param,
|
|
Post,
|
|
Req,
|
|
} from '@nestjs/common';
|
|
import {
|
|
createFeedTokenInputSchema,
|
|
type CreateFeedTokenInput,
|
|
type FeedTokenCreatedView,
|
|
type FeedTokenView,
|
|
} from '@dorfteich/shared';
|
|
|
|
import { AuthedRequest } from '../auth/auth.guard';
|
|
import { ZodValidationPipe } from '../common/zod-validation.pipe';
|
|
import { AuthenticatedOnly } from '../permissions/permission.decorators';
|
|
import { InstanceSettingsService } from '../settings/instance-settings.service';
|
|
import { FeedTokensService } from './feed-tokens.service';
|
|
|
|
/**
|
|
* Feed-token lifecycle for the settings UI (issue #149) —
|
|
* session-authenticated and owner-scoped, like the API-token controller.
|
|
*/
|
|
@Controller('users/me/feed-tokens')
|
|
@AuthenticatedOnly()
|
|
export class FeedTokensController {
|
|
constructor(
|
|
private readonly tokens: FeedTokensService,
|
|
private readonly settings: InstanceSettingsService,
|
|
) {}
|
|
|
|
/** Feed master switch (issue #191): disabled ⇒ the surface answers 404. */
|
|
private async assertFeedsEnabled(): Promise<void> {
|
|
if (!(await this.settings.get('feeds.enabled'))) throw new NotFoundException();
|
|
}
|
|
|
|
@Get()
|
|
async list(@Req() request: AuthedRequest): Promise<FeedTokenView[]> {
|
|
await this.assertFeedsEnabled();
|
|
return this.tokens.list(request.user!);
|
|
}
|
|
|
|
@Post()
|
|
async create(
|
|
@Body(new ZodValidationPipe(createFeedTokenInputSchema)) input: CreateFeedTokenInput,
|
|
@Req() request: AuthedRequest,
|
|
): Promise<FeedTokenCreatedView> {
|
|
await this.assertFeedsEnabled();
|
|
return this.tokens.create(request.user!, input);
|
|
}
|
|
|
|
@Delete(':id')
|
|
@HttpCode(204)
|
|
async remove(@Param('id') id: string, @Req() request: AuthedRequest): Promise<void> {
|
|
await this.assertFeedsEnabled();
|
|
await this.tokens.remove(request.user!, id);
|
|
}
|
|
}
|