Files
gitmost/apps/server/src/core/share/share.module.ts
claude code agent 227 fdeede003b feat(share): custom /l/:alias pretty links (share_aliases table) (#205)
Add a retargetable, human-readable vanity link namespace /l/<alias> that
sits alongside the untouched /share/... routes.

- New share_aliases table (workspace-scoped, UNIQUE(workspace_id, alias),
  page_id nullable ON DELETE SET NULL so the address outlives its target).
- ShareAliasRepo + ShareAliasService (create / no-op / 409 reassign guard /
  availability / request-time readable-target resolution through the single
  existing share boundary).
- Public ShareAliasRedirectController (GET /l/:alias) issues a 302 (never 301,
  the target is mutable) to the canonical /share/:key/p/:slug page; unknown /
  dangling / no-longer-readable aliases serve the SPA index with no leak.
  'l/:alias' excluded from the global /api prefix.
- Authenticated ShareAliasController (set/remove/availability/for-page).
- Shared ASCII-only normalize/validate util (server + client copies).
- Client: Custom address block in the share modal (live normalize + debounced
  availability + copy + reassign confirmation dialog).
- Unit tests: util, repo SQL-shape, service semantics, migration/entity sanity
  (server jest) + client alias util (vitest).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 06:28:26 +03:00

27 lines
1.1 KiB
TypeScript

import { Module } from '@nestjs/common';
import { ShareController } from './share.controller';
import { ShareService } from './share.service';
import { TokenModule } from '../auth/token.module';
import { ShareSeoController } from './share-seo.controller';
import { TransclusionModule } from '../page/transclusion/transclusion.module';
import { AiModule } from '../../integrations/ai/ai.module';
import { ShareAliasService } from './share-alias.service';
import { ShareAliasController } from './share-alias.controller';
import { ShareAliasRedirectController } from './share-alias-redirect.controller';
@Module({
// AiModule (AiSettingsService) is used by the page-info route to surface
// whether the anonymous public-share assistant is enabled for the workspace.
imports: [TokenModule, TransclusionModule, AiModule],
controllers: [
ShareController,
ShareSeoController,
// Vanity /l/:alias: authenticated management + public 302 resolver.
ShareAliasController,
ShareAliasRedirectController,
],
providers: [ShareService, ShareAliasService],
exports: [ShareService, ShareAliasService],
})
export class ShareModule {}