mirror of
https://github.com/docmost/docmost.git
synced 2026-07-14 03:16:48 +10:00
refactor(base): fold /bases/inline-embed into /bases/create with parentPageId
The inline-embed endpoint was a thin wrapper around baseService.create:
its only differences from /bases/create were that it derived the
spaceId/workspaceId from a parent page, gated permission via
pageAccessService.validateCanEdit on that parent, and seeded inline
defaults (Title + Text 1 + Text 2 + one row). All of that fits
naturally on /bases/create as a parentPageId branch.
- CreateBaseDto: spaceId is now optional. Either spaceId or
parentPageId must be supplied; the controller validates the
cross-field requirement and 400s otherwise.
- /bases/create: with parentPageId, derive workspaceId/spaceId from
the parent, validateCanEdit on the parent, apply inline defaults.
Without parentPageId, gate on space-level Create, Page (the
standalone path).
- InlineEmbedBaseDto + createInlineEmbed deleted.
- Slash command in the editor now POSTs /bases/create with
{ parentPageId } — the server picks up the inline branch.
This commit is contained in:
@@ -529,7 +529,7 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
||||
.run();
|
||||
|
||||
try {
|
||||
const res = await api.post<{ id: string }>("/bases/inline-embed", {
|
||||
const res = await api.post<{ id: string }>("/bases/create", {
|
||||
parentPageId,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
ForbiddenException,
|
||||
@@ -19,7 +20,6 @@ import { UpdateBaseDto } from '../dto/update-base.dto';
|
||||
import { BaseIdDto } from '../dto/base.dto';
|
||||
import { ExportBaseCsvDto } from '../dto/export-base.dto';
|
||||
import { ResolvePagesDto } from '../dto/resolve-pages.dto';
|
||||
import { InlineEmbedBaseDto } from '../dto/inline-embed-base.dto';
|
||||
import { AuthUser } from '../../../common/decorators/auth-user.decorator';
|
||||
import { AuthWorkspace } from '../../../common/decorators/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from '../../../common/guards/jwt-auth.guard';
|
||||
@@ -56,9 +56,38 @@ export class BaseController {
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
// Bases are pages — use the same SpaceCaslSubject.Page check the
|
||||
// page controller uses for its own create endpoint, so a single
|
||||
// role definition (admin/writer/reader) governs both.
|
||||
// One endpoint, two modes:
|
||||
// - parentPageId set → inline embed inside that page. Derive
|
||||
// spaceId/workspaceId from the parent and gate on Edit access
|
||||
// to the parent (same check the editor uses for any other
|
||||
// content the user adds while editing). Seed two extra text
|
||||
// columns + one row so the freshly-inserted base looks like
|
||||
// a typical database on first paint.
|
||||
// - parentPageId unset → standalone base. Gate on space-level
|
||||
// Create, Page (the same check the page controller uses for
|
||||
// its own create endpoint).
|
||||
if (dto.parentPageId) {
|
||||
const parent = await this.pageRepo.findById(dto.parentPageId);
|
||||
if (!parent) {
|
||||
throw new NotFoundException('Parent page not found');
|
||||
}
|
||||
await this.pageAccessService.validateCanEdit(parent, user);
|
||||
|
||||
return this.baseService.create(
|
||||
user.id,
|
||||
parent.workspaceId,
|
||||
{
|
||||
...dto,
|
||||
spaceId: parent.spaceId,
|
||||
},
|
||||
{ extraTextProperties: 2, defaultRows: 1 },
|
||||
);
|
||||
}
|
||||
|
||||
if (!dto.spaceId) {
|
||||
throw new BadRequestException('spaceId or parentPageId is required');
|
||||
}
|
||||
|
||||
const ability = await this.spaceAbility.createForUser(user, dto.spaceId);
|
||||
if (ability.cannot(SpaceCaslAction.Create, SpaceCaslSubject.Page)) {
|
||||
throw new ForbiddenException();
|
||||
@@ -169,33 +198,4 @@ export class BaseController {
|
||||
return { items };
|
||||
}
|
||||
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('inline-embed')
|
||||
async createInlineEmbed(
|
||||
@Body() dto: InlineEmbedBaseDto,
|
||||
@AuthUser() user: User,
|
||||
) {
|
||||
const parent = await this.pageRepo.findById(dto.parentPageId);
|
||||
if (!parent) {
|
||||
throw new NotFoundException('Parent page not found');
|
||||
}
|
||||
|
||||
await this.pageAccessService.validateCanEdit(parent, user);
|
||||
|
||||
// Inline embeds land mid-document and need to be visually meaningful
|
||||
// on first paint — a single "Title" column with no rows looks broken.
|
||||
// Seed two extra text columns and one empty row so the freshly-
|
||||
// created base looks like a typical database (Title + Text 1 + Text 2,
|
||||
// one ready-to-fill row).
|
||||
return this.baseService.create(
|
||||
user.id,
|
||||
parent.workspaceId,
|
||||
{
|
||||
spaceId: parent.spaceId,
|
||||
parentPageId: dto.parentPageId,
|
||||
name: 'Untitled',
|
||||
},
|
||||
{ extraTextProperties: 2, defaultRows: 1 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,16 @@ export class CreateBaseDto {
|
||||
@IsOptional()
|
||||
icon?: string;
|
||||
|
||||
// Either `spaceId` or `parentPageId` must be supplied. When
|
||||
// parentPageId is given, the base is being inserted as an inline
|
||||
// embed inside that page — spaceId/workspaceId are derived from
|
||||
// the parent and the seeded shape includes a couple of default
|
||||
// columns + one row. When only spaceId is supplied, the base is a
|
||||
// standalone page in that space's tree. The controller validates
|
||||
// the cross-field requirement and picks the right permission gate.
|
||||
@IsUUID()
|
||||
spaceId!: string;
|
||||
@IsOptional()
|
||||
spaceId?: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { IsUUID } from 'class-validator';
|
||||
|
||||
export class InlineEmbedBaseDto {
|
||||
@IsUUID()
|
||||
parentPageId!: string;
|
||||
}
|
||||
Reference in New Issue
Block a user