update admin panel, api, and shared-types

This commit is contained in:
mahmood
2026-10-25 11:43:45 +03:30
parent 2134de99e2
commit 5bd77c33d3
12 changed files with 150 additions and 91 deletions
@@ -176,27 +176,25 @@ export class AdminMiscController {
// The mobile home slider ("اسلایدر موبایل") is stored under the same
// `Setting` table as every other `const/:key` entry (key: mobile_main_slider),
// but it gets its own permission (`custom.config.statics.slider`, per
// acl.constants.ts / permissionConst.ts) rather than the broad `config.statics`
// that guards the rest of the "مدیریت ثابت ها" screen — uploading/replacing the
// public site banner is more sensitive than editing brand/tag lists, so it
// should not be reachable by everyone who can merely open that screen.
// and is open to everyone who can use the "مدیریت ثابت ها" screen
// (`config.statics`). The older `custom.config.statics.slider` rid stays in
// the catalogue so roles that still hold it keep saving, but gates nothing.
// Routes declared ahead of `const/:key` so Nest doesn't match `const/slider`
// (the legacy REST shape) to the generic key handler instead.
@Get('const/slider')
@RequirePermission('custom.config.statics.slider')
@RequirePermission('config.statics')
getSlider() {
return this.misc.getConst('mobile_main_slider');
}
@Put('const/slider')
@RequirePermission('custom.config.statics.slider')
@RequirePermission('config.statics')
setSlider(@Body() body: Record<string, unknown>) {
return this.misc.setConst('mobile_main_slider', body.val !== undefined ? body.val : body);
}
@Delete('const/slider')
@RequirePermission('custom.config.statics.slider')
@RequirePermission('config.statics')
removeSlider() {
return this.misc.removeConst('mobile_main_slider');
}
@@ -223,11 +221,8 @@ export class AdminMiscController {
return this.misc.removeConst(key);
}
// Blocks the generic `config.statics`-gated routes from reaching the slider
// setting by its raw key — otherwise anyone with `config.statics` could
// bypass the dedicated `const/slider` routes (guarded by the stricter
// `custom.config.statics.slider`) simply by calling `const/mobile_main_slider`
// directly, since the underlying Setting row is keyed the same either way.
// Keys with a dedicated route are refused here so each has exactly one way
// in: the slider (`const/slider`) and the accounting and pricing settings.
private assertNotSliderKey(key: string) {
if (key === 'mobile_main_slider' || isAccountingSettingKey(key) || key.trim().toLowerCase().startsWith(PRICING_SETTING_PREFIX)) {
throw new ForbiddenException('این تنظیم فقط از مسیر اختصاصی خود قابل دسترسی است');
@@ -6,8 +6,8 @@ import { Public } from '../auth/guards';
* Public read of the admin-managed home slider ("اسلایدر موبایل" in
* manage/Const.vue), served at GET /api/home/slider.
*
* The admin write path (const/slider, guarded by the dedicated
* `custom.config.statics.slider` permission — see admin-misc.controller.ts)
* The admin write path (const/slider, guarded by `config.statics` — see
* admin-misc.controller.ts)
* stores it under the same `Setting` row (key: mobile_main_slider) that this
* reads. Split into its own controller/prefix, rather than exposing it under
* `admin/misc`, so the shopper-facing site does not call an `/admin/...` path