docs: audit housekeeping hotel workflows
This commit is contained in:
1 parent
4d9845b52f
commit
949e14c109
2 files changed
+779
No files matched your search
@@ -0,0 +1,120 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { PERMS } from "@/lib/permission-slugs";
|
||||||
|
import { ownedLegacyPages } from "./discover-legacy-pages";
|
||||||
|
import { hotelMigrationEntries } from "./hotel";
|
||||||
|
import { validateMigrationEntries } from "./validate-matrix";
|
||||||
|
|
||||||
|
const HOTEL_PREFIXES = [
|
||||||
|
"/admin/rooms",
|
||||||
|
"/admin/navigation",
|
||||||
|
"/admin/radio",
|
||||||
|
"/admin/studio",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
describe("hotelMigrationEntries", () => {
|
||||||
|
it("maps all hotel tools below the hotel target root", () => {
|
||||||
|
expect(
|
||||||
|
validateMigrationEntries(
|
||||||
|
ownedLegacyPages(HOTEL_PREFIXES),
|
||||||
|
hotelMigrationEntries,
|
||||||
|
),
|
||||||
|
).toEqual([]);
|
||||||
|
expect(
|
||||||
|
hotelMigrationEntries.every(
|
||||||
|
(row) =>
|
||||||
|
row.targetPath === null || row.targetPath.startsWith("/admin/hotel/"),
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("preserves room ids while merging only the detail aliases", () => {
|
||||||
|
expect(
|
||||||
|
hotelMigrationEntries
|
||||||
|
.filter((row) => row.legacyPath.startsWith("/admin/rooms"))
|
||||||
|
.map(({ legacyPath, targetPath, decision }) => ({
|
||||||
|
legacyPath,
|
||||||
|
targetPath,
|
||||||
|
decision,
|
||||||
|
})),
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
legacyPath: "/admin/rooms",
|
||||||
|
targetPath: "/admin/hotel/rooms",
|
||||||
|
decision: "REBUILD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
legacyPath: "/admin/rooms/:id",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id",
|
||||||
|
decision: "MERGE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
legacyPath: "/admin/rooms/:id/furni",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id/furni",
|
||||||
|
decision: "REBUILD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
legacyPath: "/admin/rooms/edit/:id",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id",
|
||||||
|
decision: "MERGE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
legacyPath: "/admin/rooms/show/:id",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id",
|
||||||
|
decision: "MERGE",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps every audited workflow planned without parity claims", () => {
|
||||||
|
expect(
|
||||||
|
hotelMigrationEntries.every(
|
||||||
|
(row) => row.status === "PLANNED" && row.parityEvidence.length === 0,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requires privileged audit and an explicit capability for every mutation", () => {
|
||||||
|
const mutationRows = hotelMigrationEntries.filter(
|
||||||
|
(row) => row.dependencies.mutations.length > 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mutationRows.length).toBeGreaterThan(0);
|
||||||
|
expect(
|
||||||
|
mutationRows.every(
|
||||||
|
(row) =>
|
||||||
|
row.capabilities.mutate.length > 0 &&
|
||||||
|
row.auditRequirement === "PRIVILEGED_MUTATION",
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("records the actual permissions for room, radio, and Studio mutations", () => {
|
||||||
|
expect(hotelMigrationEntries).toContainEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
legacyPath: "/admin/rooms/edit/:id",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ROOMS_EDIT],
|
||||||
|
mutate: [PERMS.ROOMS_EDIT, PERMS.ROOMS_DELETE],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(hotelMigrationEntries).toContainEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
legacyPath: "/admin/radio/api-keys",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(hotelMigrationEntries).toContainEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
legacyPath: "/admin/studio/sync",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,659 @@
|
|||||||
|
import { PERMS } from "@/lib/permission-slugs";
|
||||||
|
import type { MigrationEntry } from "./types";
|
||||||
|
|
||||||
|
type PlannedHotelEntry = Omit<
|
||||||
|
MigrationEntry,
|
||||||
|
"targetDomain" | "requiredTests" | "parityEvidence" | "status" | "notes"
|
||||||
|
> & {
|
||||||
|
requiredTests?: MigrationEntry["requiredTests"];
|
||||||
|
notes?: MigrationEntry["notes"];
|
||||||
|
};
|
||||||
|
|
||||||
|
function plannedHotelEntry(entry: PlannedHotelEntry): MigrationEntry {
|
||||||
|
return {
|
||||||
|
...entry,
|
||||||
|
targetDomain: "hotel",
|
||||||
|
requiredTests: entry.requiredTests ?? ["integration", "e2e", "visual"],
|
||||||
|
parityEvidence: [],
|
||||||
|
status: "PLANNED",
|
||||||
|
notes: entry.notes ?? [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hotelMigrationEntries: readonly MigrationEntry[] = [
|
||||||
|
// Batch 1: room directory, consolidated room detail, furniture, and navigator.
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/rooms",
|
||||||
|
sourceFile: "src/app/admin/rooms/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/rooms",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: { read: [PERMS.ROOMS_VIEW], mutate: [] },
|
||||||
|
dependencies: {
|
||||||
|
queries: ["fetchAdminList", "Rooms", "GET /api/admin/export?type=rooms"],
|
||||||
|
mutations: [],
|
||||||
|
},
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/rooms/:id",
|
||||||
|
sourceFile: "src/app/admin/rooms/[id]/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id",
|
||||||
|
decision: "MERGE",
|
||||||
|
capabilities: { read: [PERMS.ROOMS_VIEW], mutate: [] },
|
||||||
|
dependencies: { queries: [], mutations: [] },
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "COMPLETE",
|
||||||
|
accessibility: "COMPLETE",
|
||||||
|
requiredTests: ["e2e"],
|
||||||
|
notes: ["Redirect-only alias for the room detail workflow"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/rooms/:id/furni",
|
||||||
|
sourceFile: "src/app/admin/rooms/[id]/furni/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id/furni",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ROOMS_VIEW],
|
||||||
|
mutate: [PERMS.ROOMS_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["Items", "ItemsBase", "Rooms", "User", "loadRoom"],
|
||||||
|
mutations: [
|
||||||
|
"bulkDeleteRoomItems",
|
||||||
|
"deleteRoomItem",
|
||||||
|
"RCON kickall",
|
||||||
|
"RCON reloadroom",
|
||||||
|
"roomRconAction",
|
||||||
|
"updateRoomItem",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Preserve the furniture suffix and room id in the target route"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/rooms/edit/:id",
|
||||||
|
sourceFile: "src/app/admin/rooms/edit/[id]/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id",
|
||||||
|
decision: "MERGE",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ROOMS_EDIT],
|
||||||
|
mutate: [PERMS.ROOMS_EDIT, PERMS.ROOMS_DELETE],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["Rooms", "User", "loadRoom"],
|
||||||
|
mutations: [
|
||||||
|
"deleteRoom",
|
||||||
|
"RCON kickall",
|
||||||
|
"RCON reloadroom",
|
||||||
|
"RCON updateroom",
|
||||||
|
"roomRconAction",
|
||||||
|
"updateRoom",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Merge editing and RCON controls into the room detail workflow"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/rooms/show/:id",
|
||||||
|
sourceFile: "src/app/admin/rooms/show/[id]/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/rooms/:id",
|
||||||
|
decision: "MERGE",
|
||||||
|
capabilities: { read: [PERMS.ROOMS_VIEW], mutate: [] },
|
||||||
|
dependencies: { queries: ["Rooms", "User", "loadRoom"], mutations: [] },
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"Merge the canonical read-only detail alias without changing its id",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/navigation",
|
||||||
|
sourceFile: "src/app/admin/navigation/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/navigation",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: { read: [PERMS.PAGES_VIEW], mutate: [] },
|
||||||
|
dependencies: {
|
||||||
|
queries: [
|
||||||
|
"NavigatorFlatcats",
|
||||||
|
"NavigatorPubliccats",
|
||||||
|
"navigator_publics raw SQL",
|
||||||
|
"Rooms",
|
||||||
|
],
|
||||||
|
mutations: [],
|
||||||
|
},
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Batch 2: radio overview, monitoring, moderation, and runtime configuration.
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio",
|
||||||
|
sourceFile: "src/app/admin/radio/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/overview",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["RadioApplications", "RadioSchedules", "RadioShouts"],
|
||||||
|
mutations: ["deleteShout"],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/api-keys",
|
||||||
|
sourceFile: "src/app/admin/radio/api-keys/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/api-keys",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["RadioApiKeys"],
|
||||||
|
mutations: ["createApiKey", "deleteApiKey", "toggleApiKey"],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"API-key generation and lifecycle changes are credential mutations",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/autodj",
|
||||||
|
sourceFile: "src/app/admin/radio/autodj/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/autodj",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["RadioAutoDjPlaylist"],
|
||||||
|
mutations: ["createTrack", "deleteTrack", "toggleTrack"],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/banners",
|
||||||
|
sourceFile: "src/app/admin/radio/banners/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/banners",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["RadioBanners"],
|
||||||
|
mutations: [
|
||||||
|
"createRadioBanner",
|
||||||
|
"deleteRadioBanner",
|
||||||
|
"updateRadioBanner",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/embed",
|
||||||
|
sourceFile: "src/app/admin/radio/embed/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/embed",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: { read: [PERMS.RADIO_VIEW], mutate: [] },
|
||||||
|
dependencies: {
|
||||||
|
queries: ["resolveHotelName", "siteSettings.get"],
|
||||||
|
mutations: [],
|
||||||
|
},
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/history",
|
||||||
|
sourceFile: "src/app/admin/radio/history/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/history",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: { read: [PERMS.RADIO_VIEW], mutate: [] },
|
||||||
|
dependencies: { queries: ["RadioHistory", "User"], mutations: [] },
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/moderation",
|
||||||
|
sourceFile: "src/app/admin/radio/moderation/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/moderation",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["RadioShouts"],
|
||||||
|
mutations: ["deleteShout"],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Shout deletion is a radio moderation mutation"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/monitoring",
|
||||||
|
sourceFile: "src/app/admin/radio/monitoring/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/monitoring",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: { read: [PERMS.RADIO_VIEW], mutate: [] },
|
||||||
|
dependencies: {
|
||||||
|
queries: [
|
||||||
|
"fetch radio_listeners_api_url",
|
||||||
|
"fetch radio_now_playing_api_url",
|
||||||
|
"siteSettings.get",
|
||||||
|
],
|
||||||
|
mutations: [],
|
||||||
|
},
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Monitoring probes operator-configured external HTTP endpoints"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/points",
|
||||||
|
sourceFile: "src/app/admin/radio/points/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/points",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["siteSettings.get", "WebsiteSetting"],
|
||||||
|
mutations: ["savePoints", "siteSettings.reload"],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/ranks",
|
||||||
|
sourceFile: "src/app/admin/radio/ranks/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/ranks",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["RadioRanks"],
|
||||||
|
mutations: ["createRadioRank", "deleteRadioRank", "updateRadioRank"],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/radio/settings",
|
||||||
|
sourceFile: "src/app/admin/radio/settings/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/radio/settings",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.RADIO_VIEW],
|
||||||
|
mutate: [PERMS.RADIO_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["WebsiteSetting"],
|
||||||
|
mutations: [
|
||||||
|
"saveRadioSetting",
|
||||||
|
"saveRadioSettings",
|
||||||
|
"siteSettings.reload",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"Settings include external-service credentials, runtime flags, webhooks, and custom code",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Batch 3: Studio audit and asset import workflows.
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio",
|
||||||
|
sourceFile: "src/app/admin/studio/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/furni",
|
||||||
|
decision: "MERGE",
|
||||||
|
capabilities: { read: [PERMS.ASSETS_IMPORT], mutate: [] },
|
||||||
|
dependencies: { queries: [], mutations: [] },
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "COMPLETE",
|
||||||
|
accessibility: "COMPLETE",
|
||||||
|
requiredTests: ["e2e"],
|
||||||
|
notes: ["Redirect-only entry into the Studio furniture workflow"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/audit",
|
||||||
|
sourceFile: "src/app/admin/studio/audit/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/audit",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: { read: [PERMS.ASSETS_IMPORT], mutate: [] },
|
||||||
|
dependencies: {
|
||||||
|
queries: ["POST /api/admin/import/audit", "runCatalogAudit"],
|
||||||
|
mutations: [],
|
||||||
|
},
|
||||||
|
auditRequirement: "NONE",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"The POST endpoint performs a read-only database and filesystem audit",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/badges",
|
||||||
|
sourceFile: "src/app/admin/studio/badges/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/badges",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT, PERMS.CATALOG_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: [
|
||||||
|
"GET /api/admin/import/badges",
|
||||||
|
"getBadgeData",
|
||||||
|
"searchBadges",
|
||||||
|
"WebsiteBadges",
|
||||||
|
],
|
||||||
|
mutations: [
|
||||||
|
"importBadgeSynced",
|
||||||
|
"POST /api/admin/import/badges",
|
||||||
|
"updateBadge",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"Imports external badge assets into the configured gamedata filesystem",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/clone",
|
||||||
|
sourceFile: "src/app/admin/studio/clone/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/clone",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: [
|
||||||
|
"GET /api/admin/import/clone",
|
||||||
|
"GET /api/admin/import/clone/icon",
|
||||||
|
"getCloneList",
|
||||||
|
"listSources",
|
||||||
|
],
|
||||||
|
mutations: [
|
||||||
|
"DELETE /api/admin/import/clone",
|
||||||
|
"POST /api/admin/import/clone",
|
||||||
|
"POST /api/admin/import/clone/batch",
|
||||||
|
"cloneSingleFurni",
|
||||||
|
"rcon.updateCatalog",
|
||||||
|
"rcon.updateItems",
|
||||||
|
"syncAssetsToGamedataBundle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"Clone imports combine external HTTP, database, filesystem, and RCON mutations",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/clothing",
|
||||||
|
sourceFile: "src/app/admin/studio/clothing/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/clothing",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: [
|
||||||
|
"GET /api/admin/import/clothing",
|
||||||
|
"GET /api/admin/import/clothing/sets",
|
||||||
|
"getClothingSetList",
|
||||||
|
"getFigureList",
|
||||||
|
],
|
||||||
|
mutations: [
|
||||||
|
"DELETE /api/admin/import/clothing",
|
||||||
|
"POST /api/admin/import/clothing/batch",
|
||||||
|
"POST /api/admin/import/clothing/sets/batch",
|
||||||
|
"importClothingSet",
|
||||||
|
"importSingleFigure",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Clothing imports download and delete asset files"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/effects",
|
||||||
|
sourceFile: "src/app/admin/studio/effects/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/effects",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["GET /api/admin/import/effects", "getEffectList"],
|
||||||
|
mutations: [
|
||||||
|
"DELETE /api/admin/import/effects",
|
||||||
|
"importSingleEffect",
|
||||||
|
"POST /api/admin/import/effects/batch",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Effect imports download and delete asset files"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/furni",
|
||||||
|
sourceFile: "src/app/admin/studio/furni/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/furni",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: [
|
||||||
|
"GET /api/admin/import/clone",
|
||||||
|
"GET /api/admin/import/furni",
|
||||||
|
"getHabboGamedataHotel",
|
||||||
|
"getTreeFlat",
|
||||||
|
"previewAutoCatalog",
|
||||||
|
"siteSettings.getBool",
|
||||||
|
],
|
||||||
|
mutations: [
|
||||||
|
"deleteImportedFurni",
|
||||||
|
"PATCH /api/admin/import/furni",
|
||||||
|
"POST /api/admin/import/furni",
|
||||||
|
"POST /api/admin/import/furni/batch",
|
||||||
|
"PUT /api/admin/import/furni/nitro-editor",
|
||||||
|
"rcon.updateCatalog",
|
||||||
|
"rcon.updateItems",
|
||||||
|
"setFurnidataTranslateEnabled",
|
||||||
|
"syncAssetsToGamedataBundle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"Furniture imports combine official Habbo HTTP sources, catalog and item tables, asset files, and RCON reloads",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/maintenance",
|
||||||
|
sourceFile: "src/app/admin/studio/maintenance/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/maintenance",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.CATALOG_EDIT],
|
||||||
|
mutate: [PERMS.CATALOG_EDIT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["getFurniHealthAction", "previewAlignIdsAction"],
|
||||||
|
mutations: [
|
||||||
|
"applyAlignIdsAction",
|
||||||
|
"fixCatalogOffersAction",
|
||||||
|
"fixEverythingAction",
|
||||||
|
"fixSpriteIdsAction",
|
||||||
|
"reconcileIdsAction",
|
||||||
|
"removeDuplicateItemsBaseAction",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["The current page relies on action-level catalog-edit enforcement"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/pets",
|
||||||
|
sourceFile: "src/app/admin/studio/pets/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/pets",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: [
|
||||||
|
"GET /api/admin/import/pets",
|
||||||
|
"GET /api/admin/import/pets/icon",
|
||||||
|
"getPetList",
|
||||||
|
],
|
||||||
|
mutations: [
|
||||||
|
"DELETE /api/admin/import/pets",
|
||||||
|
"importSinglePet",
|
||||||
|
"POST /api/admin/import/pets/batch",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Pet imports download and delete asset files"],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/repair-icons",
|
||||||
|
sourceFile: "src/app/admin/studio/repair-icons/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/repair-icons",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: [],
|
||||||
|
mutations: [
|
||||||
|
"POST /api/admin/import/furni/repair-icons",
|
||||||
|
"repairMissingIcons",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"Repairs icon files from local bundles or configured external sources",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/sync",
|
||||||
|
sourceFile: "src/app/admin/studio/sync/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/sync",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: ["ItemsBase", "listSources"],
|
||||||
|
mutations: [
|
||||||
|
"appendFurniEntriesBatch",
|
||||||
|
"cloneSingleFurni",
|
||||||
|
"POST /api/admin/import/clone/sync-all",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: [
|
||||||
|
"Full source sync mutates item data and asset files from external clone sources",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
plannedHotelEntry({
|
||||||
|
surface: "admin",
|
||||||
|
legacyPath: "/admin/studio/upload",
|
||||||
|
sourceFile: "src/app/admin/studio/upload/page.tsx",
|
||||||
|
targetPath: "/admin/hotel/studio/upload",
|
||||||
|
decision: "REBUILD",
|
||||||
|
capabilities: {
|
||||||
|
read: [PERMS.ASSETS_IMPORT],
|
||||||
|
mutate: [PERMS.ASSETS_IMPORT],
|
||||||
|
},
|
||||||
|
dependencies: {
|
||||||
|
queries: [],
|
||||||
|
mutations: ["POST /api/admin/import/furni/upload", "uploadSingleFurni"],
|
||||||
|
},
|
||||||
|
auditRequirement: "PRIVILEGED_MUTATION",
|
||||||
|
localization: "PARTIAL",
|
||||||
|
accessibility: "PARTIAL",
|
||||||
|
notes: ["Uploads mutate database, catalog, FurnitureData, and asset files"],
|
||||||
|
}),
|
||||||
|
];
|
||||||
Reference in new issue
Block a user