All files / src/server/api shares.ts

77.27% Statements 17/22
68.75% Branches 11/16
88.88% Functions 8/9
77.27% Lines 17/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71          10x       10x             37x     37x       8x 8x           4x       21x                       2x 2x       2x         2x       4x 4x 2x   2x     2x      
import { z } from "zod";
import type { SortResultShare } from "@/types";
import type { Database } from "@/lib/db";
import { SortResultShareRepository } from "@/server/repositories/implementations/SortResultShareRepository";
 
export const CreateShareSchema = z.object({
  expiresAt: z.string().datetime().optional(),
});
 
const UpdateShareSchema = z.object({
  status: z.enum(["active", "revoked"]).optional(),
  expiresAt: z.string().datetime().optional(),
});
 
 
export class ShareController {
  constructor(private shareRepo: SortResultShareRepository) {}
 
  static fromDb(db: Database): ShareController {
    return new ShareController(new SortResultShareRepository(db));
  }
 
  async getByIdForResult(shareId: string, resultId: string): Promise<SortResultShare | null> {
    const share = await this.shareRepo.findById(shareId);
    return share && share.sortResultId === resultId ? share : null;
  }
 
  // Raw token lookup (no validation) — the shared-results page applies its own
  // status/expiry/finalized gating. viewSharedResult is the validating variant.
  async findByToken(token: string): Promise<SortResultShare | null> {
    return this.shareRepo.findByToken(token);
  }
 
  async listShares(resultId: string): Promise<SortResultShare[]> {
    return this.shareRepo.findByResult(resultId);
  }
 
  async getShare(shareId: string): Promise<SortResultShare> {
    const share = await this.shareRepo.findById(shareId);
    if (!share) {
      throw new Error("Share not found");
    }
    return share;
  }
 
  async updateShare(shareId: string, data: unknown): Promise<SortResultShare | null> {
    const parsed = UpdateShareSchema.parse(data);
    const updateData: Partial<SortResultShare> = {
      status: parsed.status,
      expiresAt: parsed.expiresAt ? new Date(parsed.expiresAt) : undefined,
    };
    return this.shareRepo.update(shareId, updateData);
  }
 
  // false when the share is already gone.
  async revokeShare(shareId: string): Promise<boolean> {
    return (await this.shareRepo.update(shareId, { status: "revoked" })) !== null;
  }
 
  async viewSharedResult(token: string): Promise<SortResultShare> {
    const share = await this.shareRepo.findByToken(token);
    if (!share || share.status !== "active") {
      throw new Error("Share not found");
    }
    Iif (share.expiresAt && share.expiresAt < new Date()) {
      throw new Error("Share expired");
    }
    return share;
  }
}