|
|
|
|
@@ -1,29 +1,33 @@
|
|
|
|
|
import { TRPCError } from '@trpc/server';
|
|
|
|
|
import Consola from 'consola';
|
|
|
|
|
import fs from 'fs';
|
|
|
|
|
import { z } from 'zod';
|
|
|
|
|
import Consola from 'consola';
|
|
|
|
|
import { getDefaultBoardAsync } from '~/server/db/queries/userSettings';
|
|
|
|
|
import { configExists } from '~/tools/config/configExists';
|
|
|
|
|
import { getConfig } from '~/tools/config/getConfig';
|
|
|
|
|
import { getFrontendConfig } from '~/tools/config/getFrontendConfig';
|
|
|
|
|
import { writeConfig } from '~/tools/config/writeConfig';
|
|
|
|
|
import { generateDefaultApp } from '~/tools/shared/app';
|
|
|
|
|
import { configNameSchema } from '~/validations/boards';
|
|
|
|
|
|
|
|
|
|
import { adminProcedure, createTRPCRouter, protectedProcedure } from '../trpc';
|
|
|
|
|
import { writeConfig } from '~/tools/config/writeConfig';
|
|
|
|
|
import { configNameSchema } from '~/validations/boards';
|
|
|
|
|
|
|
|
|
|
export const boardRouter = createTRPCRouter({
|
|
|
|
|
all: protectedProcedure
|
|
|
|
|
.meta({ openapi: { method: 'GET', path: '/boards/all', tags: ['board'] } })
|
|
|
|
|
.input(z.void())
|
|
|
|
|
.output(z.array(z.object({
|
|
|
|
|
name: z.string(),
|
|
|
|
|
allowGuests: z.boolean(),
|
|
|
|
|
countApps: z.number().min(0),
|
|
|
|
|
countWidgets: z.number().min(0),
|
|
|
|
|
countCategories: z.number().min(0),
|
|
|
|
|
isDefaultForUser: z.boolean(),
|
|
|
|
|
})))
|
|
|
|
|
.output(
|
|
|
|
|
z.array(
|
|
|
|
|
z.object({
|
|
|
|
|
name: z.string(),
|
|
|
|
|
allowGuests: z.boolean(),
|
|
|
|
|
countApps: z.number().min(0),
|
|
|
|
|
countWidgets: z.number().min(0),
|
|
|
|
|
countCategories: z.number().min(0),
|
|
|
|
|
isDefaultForUser: z.boolean(),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.query(async ({ ctx }) => {
|
|
|
|
|
const files = fs.readdirSync('./data/configs').filter((file) => file.endsWith('.json'));
|
|
|
|
|
|
|
|
|
|
@@ -44,7 +48,7 @@ export const boardRouter = createTRPCRouter({
|
|
|
|
|
countCategories: config.categories.length,
|
|
|
|
|
isDefaultForUser: name === defaultBoard,
|
|
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
addAppsForContainers: adminProcedure
|
|
|
|
|
@@ -58,9 +62,9 @@ export const boardRouter = createTRPCRouter({
|
|
|
|
|
name: z.string(),
|
|
|
|
|
icon: z.string().optional(),
|
|
|
|
|
port: z.number().optional(),
|
|
|
|
|
}),
|
|
|
|
|
})
|
|
|
|
|
),
|
|
|
|
|
}),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.mutation(async ({ input }) => {
|
|
|
|
|
if (!configExists(input.boardName)) {
|
|
|
|
|
@@ -101,12 +105,14 @@ export const boardRouter = createTRPCRouter({
|
|
|
|
|
const targetPath = `data/configs/${input.boardName}.json`;
|
|
|
|
|
fs.writeFileSync(targetPath, JSON.stringify(newConfig, null, 2), 'utf8');
|
|
|
|
|
}),
|
|
|
|
|
renameBoard: protectedProcedure
|
|
|
|
|
renameBoard: adminProcedure
|
|
|
|
|
.meta({ openapi: { method: 'PUT', path: '/boards/rename', tags: ['board'] } })
|
|
|
|
|
.input(z.object({
|
|
|
|
|
oldName: z.string(),
|
|
|
|
|
newName: z.string().min(1),
|
|
|
|
|
}))
|
|
|
|
|
.input(
|
|
|
|
|
z.object({
|
|
|
|
|
oldName: configNameSchema,
|
|
|
|
|
newName: configNameSchema,
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.output(z.void())
|
|
|
|
|
.mutation(async ({ input }) => {
|
|
|
|
|
if (input.oldName === 'default') {
|
|
|
|
|
@@ -141,15 +147,19 @@ export const boardRouter = createTRPCRouter({
|
|
|
|
|
fs.unlinkSync(targetPath);
|
|
|
|
|
Consola.info(`Deleted ${input.oldName} from file system`);
|
|
|
|
|
}),
|
|
|
|
|
duplicateBoard: protectedProcedure
|
|
|
|
|
duplicateBoard: adminProcedure
|
|
|
|
|
.meta({ openapi: { method: 'POST', path: '/boards/duplicate', tags: ['board'] } })
|
|
|
|
|
.input(z.object({
|
|
|
|
|
boardName: z.string(),
|
|
|
|
|
}))
|
|
|
|
|
.input(
|
|
|
|
|
z.object({
|
|
|
|
|
boardName: z.string(),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.output(z.void())
|
|
|
|
|
.mutation(async ({ input }) => {
|
|
|
|
|
if (!configExists(input.boardName)) {
|
|
|
|
|
Consola.error(`Tried to duplicate ${input.boardName} but this configuration does not exist.`);
|
|
|
|
|
Consola.error(
|
|
|
|
|
`Tried to duplicate ${input.boardName} but this configuration does not exist.`
|
|
|
|
|
);
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: 'NOT_FOUND',
|
|
|
|
|
message: 'Board not found',
|
|
|
|
|
@@ -164,7 +174,7 @@ export const boardRouter = createTRPCRouter({
|
|
|
|
|
config.configProperties.name = targetName;
|
|
|
|
|
writeConfig(config);
|
|
|
|
|
|
|
|
|
|
Consola.info(`Wrote config to name '${targetName}'`)
|
|
|
|
|
Consola.info(`Wrote config to name '${targetName}'`);
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@@ -185,7 +195,7 @@ const attemptGenerateDuplicateName = (baseName: string, maxAttempts: number) =>
|
|
|
|
|
code: 'CONFLICT',
|
|
|
|
|
message: 'Board conflicts with an existing board',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const generateDuplicateName = (baseName: string, increment: number) => {
|
|
|
|
|
const result = duplicationName.exec(baseName);
|
|
|
|
|
@@ -197,4 +207,4 @@ const generateDuplicateName = (baseName: string, increment: number) => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return `${baseName} (2)`;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|