import { Inject, Body, Controller, Delete, Get, Param, Post, Req } from "@nestjs/common"; import type { Request } from "express"; import { z } from "zod"; import { ShareService } from "./share.service.js"; import { ZodValidate } from "../../common/pipes/zod-validation.pipe.js"; import { requireActor } from "../../common/types.js"; import { ApiError } from "../../common/errors.js"; const CreateShareDto = z.object({ workspaceId: z.string().min(1), targetType: z.enum(["page", "database"]), targetId: z.string().min(1), kind: z.enum(["link", "public", "invite"]).default("link"), level: z.enum(["VIEW", "COMMENT", "EDIT"]).default("VIEW"), email: z.string().email().optional(), expiresAt: z.string().datetime({ offset: true }).optional() }); type CreateShareInput = z.infer; @Controller() export class ShareController { constructor(@Inject(ShareService) private readonly shares: ShareService) {} @Post("shares") create( @Body(ZodValidate(CreateShareDto)) body: CreateShareInput, @Req() req: Request ) { return this.shares.create(requireActor(req), body, req); } @Get("shares/:workspaceId") list(@Param("workspaceId") workspaceId: string, @Req() req: Request) { return this.shares.list(workspaceId, requireActor(req)); } @Delete("shares/:id") remove(@Param("id") id: string, @Req() req: Request) { return this.shares.remove(id, requireActor(req), req); } /** * PUBLIC read endpoint (auth guard whitelists /share/:slug). * Unknown / revoked / expired / non-public → 404 (never 403). */ @Get("share/:slug") async publicGet(@Param("slug") slug: string) { const result = await this.shares.publicGet(slug); if (!result) throw ApiError.notFound("Share not found."); return result; } }