feat: Wave 2 — entities, security guards, API key encryption (15 files)
All checks were successful
Test Asgard Runner / test (push) Successful in 2s
All checks were successful
Test Asgard Runner / test (push) Successful in 2s
Entities:
- Create 5 new TypeORM entities: webstore_config, webstore_categories,
webstore_items, webstore_transactions, module_store (all verified against live DB)
- Fix wipe-profile entity: remove incorrect default {} for pre/post wipe configs
Security:
- Add @RequirePermission guards to 7 controllers (36 endpoints total):
team, webstore, notifications, alerts, analytics, settings, schedules
- Encrypt panel API key with AES-256-GCM in setup service (was plaintext)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
52
backend-nest/src/entities/module-store.entity.ts
Normal file
52
backend-nest/src/entities/module-store.entity.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
|
||||
@Entity('module_store')
|
||||
export class ModuleStore {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'varchar', unique: true })
|
||||
module_slug: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
module_name: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
long_description: string | null;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 2, default: 0 })
|
||||
price: number;
|
||||
|
||||
@Column({ type: 'varchar', default: 'one_time' })
|
||||
price_type: string;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 2, nullable: true })
|
||||
monthly_price: number | null;
|
||||
|
||||
@Column({ type: 'varchar', default: '1.0.0' })
|
||||
version: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
download_path: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
thumbnail_url: string | null;
|
||||
|
||||
@Column({ type: 'text', array: true, nullable: true, default: () => "'{}'" })
|
||||
screenshots: string[] | null;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
category: string | null;
|
||||
|
||||
@Column({ type: 'boolean', default: true })
|
||||
is_active: boolean;
|
||||
|
||||
@Column({ type: 'timestamptz', default: () => 'NOW()' })
|
||||
created_at: Date;
|
||||
|
||||
@Column({ type: 'timestamptz', default: () => 'NOW()' })
|
||||
updated_at: Date;
|
||||
}
|
||||
24
backend-nest/src/entities/webstore-category.entity.ts
Normal file
24
backend-nest/src/entities/webstore-category.entity.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { License } from './license.entity';
|
||||
|
||||
@Entity('webstore_categories')
|
||||
export class WebstoreCategory {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid' })
|
||||
license_id: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
category_name: string;
|
||||
|
||||
@Column({ type: 'integer', default: 0 })
|
||||
display_order: number;
|
||||
|
||||
@Column({ type: 'boolean', default: true })
|
||||
is_active: boolean;
|
||||
|
||||
@ManyToOne(() => License, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'license_id' })
|
||||
license: License;
|
||||
}
|
||||
39
backend-nest/src/entities/webstore-config.entity.ts
Normal file
39
backend-nest/src/entities/webstore-config.entity.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { License } from './license.entity';
|
||||
|
||||
@Entity('webstore_config')
|
||||
export class WebstoreConfig {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid' })
|
||||
license_id: string;
|
||||
|
||||
@Column({ type: 'boolean', default: false })
|
||||
is_active: boolean;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
paypal_client_id: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
paypal_secret: string | null;
|
||||
|
||||
@Column({ type: 'varchar', default: 'sandbox' })
|
||||
paypal_mode: string;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
store_name: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
store_description: string | null;
|
||||
|
||||
@Column({ type: 'varchar', default: 'USD' })
|
||||
currency: string;
|
||||
|
||||
@Column({ type: 'timestamptz', default: () => 'NOW()' })
|
||||
created_at: Date;
|
||||
|
||||
@ManyToOne(() => License, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'license_id' })
|
||||
license: License;
|
||||
}
|
||||
47
backend-nest/src/entities/webstore-item.entity.ts
Normal file
47
backend-nest/src/entities/webstore-item.entity.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { License } from './license.entity';
|
||||
import { WebstoreCategory } from './webstore-category.entity';
|
||||
|
||||
@Entity('webstore_items')
|
||||
export class WebstoreItem {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid' })
|
||||
license_id: string;
|
||||
|
||||
@Column({ type: 'uuid' })
|
||||
category_id: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
item_name: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string | null;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 2 })
|
||||
price: number;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
image_url: string | null;
|
||||
|
||||
@Column({ type: 'varchar', default: 'kit' })
|
||||
item_type: string;
|
||||
|
||||
@Column({ type: 'jsonb' })
|
||||
delivery_config: Record<string, any>;
|
||||
|
||||
@Column({ type: 'boolean', default: true })
|
||||
is_active: boolean;
|
||||
|
||||
@Column({ type: 'timestamptz', default: () => 'NOW()' })
|
||||
created_at: Date;
|
||||
|
||||
@ManyToOne(() => License, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'license_id' })
|
||||
license: License;
|
||||
|
||||
@ManyToOne(() => WebstoreCategory, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'category_id' })
|
||||
category: WebstoreCategory;
|
||||
}
|
||||
47
backend-nest/src/entities/webstore-transaction.entity.ts
Normal file
47
backend-nest/src/entities/webstore-transaction.entity.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { License } from './license.entity';
|
||||
import { WebstoreItem } from './webstore-item.entity';
|
||||
|
||||
@Entity('webstore_transactions')
|
||||
export class WebstoreTransaction {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid' })
|
||||
license_id: string;
|
||||
|
||||
@Column({ type: 'uuid' })
|
||||
item_id: string;
|
||||
|
||||
@Column({ type: 'varchar' })
|
||||
buyer_steam_id: string;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
buyer_name: string | null;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 2 })
|
||||
amount: number;
|
||||
|
||||
@Column({ type: 'varchar', default: 'USD' })
|
||||
currency: string;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
paypal_transaction_id: string | null;
|
||||
|
||||
@Column({ type: 'varchar', default: 'pending' })
|
||||
status: string;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true })
|
||||
delivered_at: Date | null;
|
||||
|
||||
@Column({ type: 'timestamptz', default: () => 'NOW()' })
|
||||
created_at: Date;
|
||||
|
||||
@ManyToOne(() => License, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'license_id' })
|
||||
license: License;
|
||||
|
||||
@ManyToOne(() => WebstoreItem, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'item_id' })
|
||||
item: WebstoreItem;
|
||||
}
|
||||
@@ -15,10 +15,10 @@ export class WipeProfile {
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string | null;
|
||||
|
||||
@Column({ type: 'jsonb', default: {} })
|
||||
@Column({ type: 'jsonb' })
|
||||
pre_wipe_config: Record<string, any>;
|
||||
|
||||
@Column({ type: 'jsonb', default: {} })
|
||||
@Column({ type: 'jsonb' })
|
||||
post_wipe_config: Record<string, any>;
|
||||
|
||||
@Column({ type: 'timestamptz', default: () => 'NOW()' })
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ApiTags, ApiBearerAuth, ApiOperation, ApiQuery } from '@nestjs/swagger'
|
||||
import { AlertsService } from './alerts.service';
|
||||
import { UpdateAlertConfigDto } from './dto/update-alert-config.dto';
|
||||
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
|
||||
@ApiTags('alerts')
|
||||
@ApiBearerAuth()
|
||||
@@ -11,12 +12,14 @@ export class AlertsController {
|
||||
constructor(private readonly alertsService: AlertsService) {}
|
||||
|
||||
@Get('config')
|
||||
@RequirePermission('alerts.view')
|
||||
@ApiOperation({ summary: 'Get alert configuration' })
|
||||
async getConfig(@CurrentTenant() licenseId: string) {
|
||||
return this.alertsService.getConfig(licenseId);
|
||||
}
|
||||
|
||||
@Put('config')
|
||||
@RequirePermission('alerts.manage')
|
||||
@ApiOperation({ summary: 'Update alert configuration' })
|
||||
async updateConfig(
|
||||
@CurrentTenant() licenseId: string,
|
||||
@@ -26,6 +29,7 @@ export class AlertsController {
|
||||
}
|
||||
|
||||
@Get('history')
|
||||
@RequirePermission('alerts.view')
|
||||
@ApiOperation({ summary: 'Get alert history' })
|
||||
@ApiQuery({ name: 'limit', required: false, type: Number, description: 'Max records to return (default: 50)' })
|
||||
async getHistory(
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Controller, Get, Query, Header } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth, ApiOperation, ApiQuery } from '@nestjs/swagger';
|
||||
import { AnalyticsService } from './analytics.service';
|
||||
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
|
||||
@ApiTags('analytics')
|
||||
@ApiBearerAuth()
|
||||
@@ -10,6 +11,7 @@ export class AnalyticsController {
|
||||
constructor(private readonly analyticsService: AnalyticsService) {}
|
||||
|
||||
@Get('summary')
|
||||
@RequirePermission('analytics.view')
|
||||
@ApiOperation({ summary: 'Get analytics summary for time range' })
|
||||
@ApiQuery({ name: 'range', required: false, type: Number, description: 'Hours to analyze (default: 24)' })
|
||||
async getSummary(
|
||||
@@ -21,6 +23,7 @@ export class AnalyticsController {
|
||||
}
|
||||
|
||||
@Get('timeseries')
|
||||
@RequirePermission('analytics.view')
|
||||
@ApiOperation({ summary: 'Get timeseries data for charts' })
|
||||
@ApiQuery({ name: 'range', required: false, type: Number })
|
||||
@ApiQuery({ name: 'granularity', required: false, enum: ['raw', 'hourly'] })
|
||||
@@ -35,6 +38,7 @@ export class AnalyticsController {
|
||||
}
|
||||
|
||||
@Get('wipes/performance')
|
||||
@RequirePermission('analytics.view')
|
||||
@ApiOperation({ summary: 'Get wipe performance metrics' })
|
||||
@ApiQuery({ name: 'range', required: false, type: Number, description: 'Hours (default: 720 = 30 days)' })
|
||||
async getWipePerformance(
|
||||
@@ -46,6 +50,7 @@ export class AnalyticsController {
|
||||
}
|
||||
|
||||
@Get('maps')
|
||||
@RequirePermission('analytics.view')
|
||||
@ApiOperation({ summary: 'Get map usage analytics' })
|
||||
@ApiQuery({ name: 'range', required: false, type: Number })
|
||||
async getMapAnalytics(
|
||||
@@ -57,6 +62,7 @@ export class AnalyticsController {
|
||||
}
|
||||
|
||||
@Get('players')
|
||||
@RequirePermission('analytics.view')
|
||||
@ApiOperation({ summary: 'Get player analytics' })
|
||||
@ApiQuery({ name: 'range', required: false, type: Number })
|
||||
@ApiQuery({ name: 'metric', required: false, enum: ['sessions', 'retention'] })
|
||||
@@ -71,6 +77,7 @@ export class AnalyticsController {
|
||||
}
|
||||
|
||||
@Get('retention')
|
||||
@RequirePermission('analytics.view')
|
||||
@ApiOperation({ summary: 'Get player retention across wipes' })
|
||||
@ApiQuery({ name: 'wipe_count', required: false, type: Number, description: 'Number of recent wipes (default: 5)' })
|
||||
async getRetention(
|
||||
@@ -82,6 +89,7 @@ export class AnalyticsController {
|
||||
}
|
||||
|
||||
@Get('export')
|
||||
@RequirePermission('analytics.view')
|
||||
@ApiOperation({ summary: 'Export analytics data as CSV' })
|
||||
@ApiQuery({ name: 'range', required: false, type: Number })
|
||||
@Header('Content-Type', 'text/csv')
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { NotificationsService } from './notifications.service';
|
||||
import { UpdateConfigDto } from './dto/update-config.dto';
|
||||
|
||||
@@ -18,6 +19,7 @@ export class NotificationsController {
|
||||
constructor(private readonly notificationsService: NotificationsService) {}
|
||||
|
||||
@Get('config')
|
||||
@RequirePermission('notifications.view')
|
||||
@ApiOperation({
|
||||
summary: 'Get notification configuration',
|
||||
description: 'Returns notification settings for this license',
|
||||
@@ -32,6 +34,7 @@ export class NotificationsController {
|
||||
}
|
||||
|
||||
@Put('config')
|
||||
@RequirePermission('notifications.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Update notification configuration',
|
||||
description: 'Update notification settings for this license',
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { SchedulesService } from './schedules.service';
|
||||
import { CreateTaskDto } from './dto/create-task.dto';
|
||||
import { UpdateTaskDto } from './dto/update-task.dto';
|
||||
@@ -28,6 +29,7 @@ export class SchedulesController {
|
||||
constructor(private readonly schedulesService: SchedulesService) {}
|
||||
|
||||
@Get('tasks')
|
||||
@RequirePermission('schedules.view')
|
||||
@ApiOperation({
|
||||
summary: 'Get all scheduled tasks',
|
||||
description: 'Returns all scheduled tasks for this license',
|
||||
@@ -41,6 +43,7 @@ export class SchedulesController {
|
||||
}
|
||||
|
||||
@Post('tasks')
|
||||
@RequirePermission('schedules.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Create a scheduled task',
|
||||
description: 'Create a new scheduled task (restart, announcement, command, or plugin reload)',
|
||||
@@ -61,6 +64,7 @@ export class SchedulesController {
|
||||
}
|
||||
|
||||
@Put('tasks/:id')
|
||||
@RequirePermission('schedules.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Update a scheduled task',
|
||||
description: 'Update task configuration, schedule, or settings',
|
||||
@@ -82,6 +86,7 @@ export class SchedulesController {
|
||||
}
|
||||
|
||||
@Delete('tasks/:id')
|
||||
@RequirePermission('schedules.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Delete a scheduled task',
|
||||
description: 'Remove a scheduled task and unregister from scheduler',
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { SettingsService } from './settings.service';
|
||||
import { UpdatePublicSiteDto } from './dto/update-public-site.dto';
|
||||
import { UpdateDomainDto } from './dto/update-domain.dto';
|
||||
@@ -19,6 +20,7 @@ export class SettingsController {
|
||||
constructor(private readonly settingsService: SettingsService) {}
|
||||
|
||||
@Get('public-site')
|
||||
@RequirePermission('settings.view')
|
||||
@ApiOperation({
|
||||
summary: 'Get public site configuration',
|
||||
description: 'Returns public site settings for this license',
|
||||
@@ -32,6 +34,7 @@ export class SettingsController {
|
||||
}
|
||||
|
||||
@Put('public-site')
|
||||
@RequirePermission('settings.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Update public site configuration',
|
||||
description: 'Update public site settings for this license',
|
||||
@@ -48,6 +51,7 @@ export class SettingsController {
|
||||
}
|
||||
|
||||
@Put('domain')
|
||||
@RequirePermission('settings.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Update domain settings',
|
||||
description: 'Update subdomain or custom domain for this license',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { ServerConnection } from '../../entities/server-connection.entity';
|
||||
@@ -12,6 +13,7 @@ import * as crypto from 'crypto';
|
||||
@Injectable()
|
||||
export class SetupService {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
@InjectRepository(ServerConnection)
|
||||
private readonly connectionRepo: Repository<ServerConnection>,
|
||||
@InjectRepository(ServerConfig)
|
||||
@@ -51,8 +53,17 @@ export class SetupService {
|
||||
|
||||
// Store encrypted API key if provided
|
||||
if (dto.panel_api_key) {
|
||||
// Stub - would encrypt in production
|
||||
connection.panel_api_key_encrypted = dto.panel_api_key;
|
||||
const encryptionKey = this.configService.get<string>('encryption.key', '');
|
||||
const keyBuffer = Buffer.from(encryptionKey, 'hex');
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv('aes-256-gcm', keyBuffer, iv);
|
||||
const encrypted = Buffer.concat([
|
||||
cipher.update(dto.panel_api_key, 'utf8'),
|
||||
cipher.final(),
|
||||
]);
|
||||
const authTag = cipher.getAuthTag();
|
||||
connection.panel_api_key_encrypted =
|
||||
`${iv.toString('hex')}:${encrypted.toString('hex')}:${authTag.toString('hex')}`;
|
||||
}
|
||||
|
||||
connection.updated_at = new Date();
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
|
||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { TeamService } from './team.service';
|
||||
import { InviteMemberDto } from './dto/invite-member.dto';
|
||||
import { CreateRoleDto } from './dto/create-role.dto';
|
||||
@@ -30,6 +31,7 @@ export class TeamController {
|
||||
constructor(private readonly teamService: TeamService) {}
|
||||
|
||||
@Get()
|
||||
@RequirePermission('team.view')
|
||||
@ApiOperation({
|
||||
summary: 'Get team members and roles',
|
||||
description: 'Returns all team members with their roles and all available roles',
|
||||
@@ -43,6 +45,7 @@ export class TeamController {
|
||||
}
|
||||
|
||||
@Post('invite')
|
||||
@RequirePermission('team.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Invite a team member',
|
||||
description: 'Invite a user by email and assign them a role',
|
||||
@@ -68,6 +71,7 @@ export class TeamController {
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@RequirePermission('team.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Remove a team member',
|
||||
description: 'Remove a team member by ID',
|
||||
@@ -88,6 +92,7 @@ export class TeamController {
|
||||
}
|
||||
|
||||
@Post('roles')
|
||||
@RequirePermission('team.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Create a custom role',
|
||||
description: 'Create a new custom role for this license',
|
||||
@@ -108,6 +113,7 @@ export class TeamController {
|
||||
}
|
||||
|
||||
@Put('roles/:id')
|
||||
@RequirePermission('team.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Update a role',
|
||||
description: 'Update role permissions and details',
|
||||
@@ -133,6 +139,7 @@ export class TeamController {
|
||||
}
|
||||
|
||||
@Delete('roles/:id')
|
||||
@RequirePermission('team.manage')
|
||||
@ApiOperation({
|
||||
summary: 'Delete a role',
|
||||
description: 'Delete a custom role (cannot delete system roles or roles in use)',
|
||||
|
||||
@@ -7,6 +7,7 @@ import { CreateItemDto } from './dto/create-item.dto';
|
||||
import { PurchaseDto } from './dto/purchase.dto';
|
||||
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
|
||||
import { Public } from '../../common/decorators/public.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
|
||||
@ApiTags('webstore')
|
||||
@Controller()
|
||||
@@ -15,6 +16,7 @@ export class WebstoreController {
|
||||
|
||||
// Admin Routes
|
||||
@Get('webstore/config')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get webstore configuration' })
|
||||
async getConfig(@CurrentTenant() licenseId: string) {
|
||||
@@ -23,6 +25,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Put('webstore/config')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Update webstore configuration' })
|
||||
async updateConfig(
|
||||
@@ -34,6 +37,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Get('webstore/categories')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get all store categories' })
|
||||
async getCategories(@CurrentTenant() licenseId: string) {
|
||||
@@ -41,6 +45,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Post('webstore/categories')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Create a new category' })
|
||||
async createCategory(
|
||||
@@ -51,6 +56,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Put('webstore/categories/:id')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Update a category' })
|
||||
@ApiParam({ name: 'id', description: 'Category ID' })
|
||||
@@ -63,6 +69,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Delete('webstore/categories/:id')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Delete a category' })
|
||||
@ApiParam({ name: 'id', description: 'Category ID' })
|
||||
@@ -75,6 +82,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Get('webstore/items')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get all store items' })
|
||||
async getItems(@CurrentTenant() licenseId: string) {
|
||||
@@ -82,6 +90,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Post('webstore/items')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Create a new store item' })
|
||||
async createItem(
|
||||
@@ -92,6 +101,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Put('webstore/items/:id')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Update a store item' })
|
||||
@ApiParam({ name: 'id', description: 'Item ID' })
|
||||
@@ -104,6 +114,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Delete('webstore/items/:id')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Delete a store item' })
|
||||
@ApiParam({ name: 'id', description: 'Item ID' })
|
||||
@@ -116,6 +127,7 @@ export class WebstoreController {
|
||||
}
|
||||
|
||||
@Get('webstore/transactions')
|
||||
@RequirePermission('store.manage')
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get all store transactions' })
|
||||
async getTransactions(@CurrentTenant() licenseId: string) {
|
||||
|
||||
Reference in New Issue
Block a user