Website Menu Builder
Overview
Menu Builder produces "menu pages" — mini-sites that open from LINE through LIFF or the in-app browser. Each menu carries its own public token, so end users can view it without signing in.
The module supports image uploads, cloning an entire existing menu, and reissuing a token when the original link has leaked to people who should not have it.
One thing worth knowing: this module's policy metadata uses PolicyModuleSystemModule rather
than a policy of its own, preserving the behaviour of the original NestJS implementation.
Business Flow
Administrator side
GET /api/menu-builderlists menus with pagination and filtering perQueryParamsMenuBuilderDto.POST /api/menu-buildercreates a menu; the system generates its public token automatically.POST /api/menu-builder/upload-imageuploads menu imagery, validating that the file really is an image.GET /api/menu-builder/:idandPUT /api/menu-builder/:idread and update the menu structure.POST /api/menu-builder/:id/clonecopies an entire menu and issues a fresh token — useful for A/B tests or seasonal variants.POST /api/menu-builder/:id/regenerate-tokenissues a new token, invalidating the previous link immediately.DELETE /api/menu-builder/:idperforms a soft delete by stamping thedeleted_atcolumn.
End-user side
- The user opens the link from LINE, which calls
GET /api/menu-builder/public/:token. This route has no JWT guard and returns the menu structure for client-web to render. - A second public path with similar behaviour exists in the publicmod module:
GET /api/public/menu/:token.
A note on soft deletes: the entity has a deleted_at column but does not declare it as
gorm.DeletedAt, so every read must add deleted_at IS NULL explicitly to match the original
TypeORM behaviour. The delete itself stamps deleted_at = now() without a filter, which
requires Unscoped().
Key Files & Functions
Core code lives in internal/modules/menubuilder/, comprising controller.go, service.go,
and dto.go.
| Method | Route | Handler | Guard / Policy |
|---|---|---|---|
| GET | /api/menu-builder/public/:token | h.findByPublicToken | public (no guard) |
| GET | /api/menu-builder | h.findAll | read system_module |
| GET | /api/menu-builder/:id | h.findOne | read system_module |
| POST | /api/menu-builder | h.create | create system_module |
| POST | /api/menu-builder/:id/clone | h.clone | create system_module |
| POST | /api/menu-builder/:id/regenerate-token | h.regenerateToken | update system_module |
| POST | /api/menu-builder/upload-image | h.uploadImage | create system_module |
| PUT | /api/menu-builder/:id | h.update | update system_module |
| DELETE | /api/menu-builder/:id | h.delete | delete system_module |
All routes except the public one are registered on the authed.Group("/menu-builder") group.
Connections to Other Services
- Access control — administrator routes require global
JwtAuth; the policy metadata isPolicyModuleSystemModuleand is not yet enforced. The public route has no guard. - Tables —
menu_builder,line_oa - Storage Service — holds uploaded menu images.
- Related modules — Public API (an alternative public path), Content Page (another mini-site format), and Content Link.