1
This commit is contained in:
214
server-node/src/routes/rpg-profile/rpgProfileRoutes.ts
Normal file
214
server-node/src/routes/rpg-profile/rpgProfileRoutes.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type {
|
||||
PlatformBrowseHistoryBatchSyncRequest,
|
||||
PlatformBrowseHistoryResponse,
|
||||
PlatformBrowseHistoryWriteEntry,
|
||||
ProfileDashboardSummary,
|
||||
ProfilePlayStatsResponse,
|
||||
ProfileWalletLedgerResponse,
|
||||
RuntimeSettings,
|
||||
} from '../../../../packages/shared/src/contracts/runtime.js';
|
||||
import { PLATFORM_THEMES } from '../../../../packages/shared/src/contracts/runtime.js';
|
||||
import type { AppContext } from '../../context.js';
|
||||
import { asyncHandler, sendApiResponse } from '../../http.js';
|
||||
import { requireJwtAuth } from '../../middleware/auth.js';
|
||||
import { routeMeta } from '../../middleware/routeMeta.js';
|
||||
|
||||
const platformBrowseHistoryEntrySchema = z.object({
|
||||
ownerUserId: z.string().trim().min(1),
|
||||
profileId: z.string().trim().min(1),
|
||||
worldName: z.string().trim().min(1),
|
||||
subtitle: z.string().trim().optional().default(''),
|
||||
summaryText: z.string().trim().optional().default(''),
|
||||
coverImageSrc: z.string().trim().nullable().optional().default(null),
|
||||
themeMode: z.string().trim().optional().default('mythic'),
|
||||
authorDisplayName: z.string().trim().optional().default('玩家'),
|
||||
visitedAt: z.string().trim().optional().default(''),
|
||||
});
|
||||
|
||||
const platformBrowseHistoryBatchSchema = z.object({
|
||||
entries: z.array(platformBrowseHistoryEntrySchema).max(100),
|
||||
});
|
||||
|
||||
const settingsSchema = z.object({
|
||||
musicVolume: z.number().min(0).max(1),
|
||||
platformTheme: z.enum(PLATFORM_THEMES),
|
||||
});
|
||||
|
||||
export const RPG_PROFILE_ROUTE_BASE_PATH = '/api/runtime/profile';
|
||||
export const RPG_PROFILE_LEGACY_ROUTE_BASE_PATH = '/api/profile';
|
||||
|
||||
function routeCompatPaths(path: string) {
|
||||
return [path, path.replace('runtime/', '')] as const;
|
||||
}
|
||||
|
||||
export function createRpgProfileRoutes(context: AppContext) {
|
||||
const router = Router();
|
||||
const requireAuth = requireJwtAuth(context.config, context.userRepository);
|
||||
|
||||
routeCompatPaths('/api/runtime/profile/dashboard').forEach((path, index) => {
|
||||
router.get(
|
||||
path.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({
|
||||
operation:
|
||||
index === 0
|
||||
? 'profile.dashboard.get'
|
||||
: 'profile.dashboard.get.compat',
|
||||
}),
|
||||
asyncHandler(async (request, response) => {
|
||||
sendApiResponse<ProfileDashboardSummary>(
|
||||
response,
|
||||
await context.rpgProfileDashboardRepository.getProfileDashboard(
|
||||
request.userId!,
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
routeCompatPaths('/api/runtime/profile/wallet-ledger').forEach((path, index) => {
|
||||
router.get(
|
||||
path.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({
|
||||
operation:
|
||||
index === 0
|
||||
? 'profile.walletLedger.list'
|
||||
: 'profile.walletLedger.list.compat',
|
||||
}),
|
||||
asyncHandler(async (request, response) => {
|
||||
sendApiResponse<ProfileWalletLedgerResponse>(response, {
|
||||
entries:
|
||||
await context.rpgProfileDashboardRepository.listProfileWalletLedger(
|
||||
request.userId!,
|
||||
),
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
routeCompatPaths('/api/runtime/profile/play-stats').forEach((path, index) => {
|
||||
router.get(
|
||||
path.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({
|
||||
operation:
|
||||
index === 0
|
||||
? 'profile.playStats.get'
|
||||
: 'profile.playStats.get.compat',
|
||||
}),
|
||||
asyncHandler(async (request, response) => {
|
||||
sendApiResponse<ProfilePlayStatsResponse>(
|
||||
response,
|
||||
await context.rpgProfileDashboardRepository.getProfilePlayStats(
|
||||
request.userId!,
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
routeCompatPaths('/api/runtime/profile/browse-history').forEach((path, index) => {
|
||||
router.get(
|
||||
path.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({
|
||||
operation:
|
||||
index === 0
|
||||
? 'profile.browseHistory.list'
|
||||
: 'profile.browseHistory.list.compat',
|
||||
}),
|
||||
asyncHandler(async (request, response) => {
|
||||
sendApiResponse<PlatformBrowseHistoryResponse>(response, {
|
||||
entries: await context.rpgBrowseHistoryRepository.listPlatformBrowseHistory(
|
||||
request.userId!,
|
||||
),
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
path.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({
|
||||
operation:
|
||||
index === 0
|
||||
? 'profile.browseHistory.upsert'
|
||||
: 'profile.browseHistory.upsert.compat',
|
||||
}),
|
||||
asyncHandler(async (request, response) => {
|
||||
const rawBody =
|
||||
request.body && typeof request.body === 'object' ? request.body : {};
|
||||
const payload = (
|
||||
'entries' in rawBody
|
||||
? platformBrowseHistoryBatchSchema.parse(rawBody)
|
||||
: platformBrowseHistoryEntrySchema.parse(rawBody)
|
||||
) as
|
||||
| PlatformBrowseHistoryBatchSyncRequest
|
||||
| PlatformBrowseHistoryWriteEntry;
|
||||
|
||||
const entries = 'entries' in payload ? payload.entries : [payload];
|
||||
|
||||
sendApiResponse<PlatformBrowseHistoryResponse>(response, {
|
||||
entries:
|
||||
await context.rpgBrowseHistoryRepository.upsertPlatformBrowseHistoryEntries(
|
||||
request.userId!,
|
||||
entries,
|
||||
),
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
router.delete(
|
||||
path.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({
|
||||
operation:
|
||||
index === 0
|
||||
? 'profile.browseHistory.clear'
|
||||
: 'profile.browseHistory.clear.compat',
|
||||
}),
|
||||
asyncHandler(async (request, response) => {
|
||||
await context.rpgBrowseHistoryRepository.clearPlatformBrowseHistory(
|
||||
request.userId!,
|
||||
);
|
||||
sendApiResponse<PlatformBrowseHistoryResponse>(response, {
|
||||
entries: [],
|
||||
});
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/api/runtime/settings'.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({ operation: 'runtime.settings.get' }),
|
||||
asyncHandler(async (request, response) => {
|
||||
sendApiResponse(
|
||||
response,
|
||||
await context.rpgProfileDashboardRepository.getSettings(request.userId!),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/api/runtime/settings'.replace('/api/', '/'),
|
||||
requireAuth,
|
||||
routeMeta({ operation: 'runtime.settings.put' }),
|
||||
asyncHandler(async (request, response) => {
|
||||
const payload = settingsSchema.parse(request.body) as RuntimeSettings;
|
||||
sendApiResponse(
|
||||
response,
|
||||
await context.rpgProfileDashboardRepository.putSettings(
|
||||
request.userId!,
|
||||
payload,
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
return router;
|
||||
}
|
||||
Reference in New Issue
Block a user