diff --git a/prisma/migrations/20250516022401_version_checking/migration.sql b/prisma/migrations/20250516022401_version_checking/migration.sql
new file mode 100644
index 00000000..8ac0d0dc
--- /dev/null
+++ b/prisma/migrations/20250516022401_version_checking/migration.sql
@@ -0,0 +1,3 @@
+-- AlterTable
+ALTER TABLE "Zipline" ADD COLUMN "featuresVersionAPI" TEXT NOT NULL DEFAULT 'https://zipline-version.diced.sh',
+ADD COLUMN "featuresVersionChecking" BOOLEAN NOT NULL DEFAULT true;
diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml
index 648c57fd..044d57cd 100644
--- a/prisma/migrations/migration_lock.toml
+++ b/prisma/migrations/migration_lock.toml
@@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
-provider = "postgresql"
\ No newline at end of file
+provider = "postgresql"
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 772bfe5e..7390fd6c 100755
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -58,6 +58,9 @@ model Zipline {
featuresMetricsAdminOnly Boolean @default(false)
featuresMetricsShowUserSpecific Boolean @default(true)
+ featuresVersionChecking Boolean @default(true)
+ featuresVersionAPI String @default("https://zipline-version.diced.sh")
+
invitesEnabled Boolean @default(true)
invitesLength Int @default(6)
diff --git a/src/components/pages/serverSettings/parts/ServerSettingsFeatures.tsx b/src/components/pages/serverSettings/parts/ServerSettingsFeatures.tsx
index 7b0ac9a6..591ac9c9 100644
--- a/src/components/pages/serverSettings/parts/ServerSettingsFeatures.tsx
+++ b/src/components/pages/serverSettings/parts/ServerSettingsFeatures.tsx
@@ -1,5 +1,15 @@
import { Response } from '@/lib/api/response';
-import { Button, LoadingOverlay, NumberInput, Paper, SimpleGrid, Switch, Title } from '@mantine/core';
+import {
+ Anchor,
+ Button,
+ LoadingOverlay,
+ NumberInput,
+ Paper,
+ SimpleGrid,
+ Switch,
+ TextInput,
+ Title,
+} from '@mantine/core';
import { useForm } from '@mantine/form';
import { IconDeviceFloppy } from '@tabler/icons-react';
import { useRouter } from 'next/router';
@@ -25,6 +35,8 @@ export default function ServerSettingsFeatures({
featuresMetricsEnabled: true,
featuresMetricsAdminOnly: false,
featuresMetricsShowUserSpecific: true,
+ featuresVersionChecking: true,
+ featuresVersionAPI: 'https://zipline-version.diced.sh/',
},
});
@@ -43,6 +55,8 @@ export default function ServerSettingsFeatures({
featuresMetricsEnabled: data?.featuresMetricsEnabled ?? true,
featuresMetricsAdminOnly: data?.featuresMetricsAdminOnly ?? false,
featuresMetricsShowUserSpecific: data?.featuresMetricsShowUserSpecific ?? true,
+ featuresVersionChecking: data?.featuresVersionChecking ?? true,
+ featuresVersionAPI: data?.featuresVersionAPI ?? 'https://zipline-version.diced.sh/',
});
}, [data]);
@@ -107,7 +121,7 @@ export default function ServerSettingsFeatures({
description='Shows metrics specific to each user, for all users.'
{...form.getInputProps('featuresMetricsShowUserSpecific', { type: 'checkbox' })}
/>
-
+
+
+
+
+ The URL of the version checking server. The default is{' '}
+
+ https://zipline-version.diced.sh
+
+ . Visit the{' '}
+
+ GitHub
+ {' '}
+ to host your own version checking server.
+ >
+ }
+ placeholder='https://zipline-version.diced.sh/'
+ {...form.getInputProps('featuresVersionAPI')}
+ />
}>
diff --git a/src/lib/config/read.ts b/src/lib/config/read.ts
index 42e9bfa9..f4d6fbe2 100755
--- a/src/lib/config/read.ts
+++ b/src/lib/config/read.ts
@@ -211,6 +211,9 @@ export const DATABASE_TO_PROP = {
featuresMetricsAdminOnly: 'features.metrics.adminOnly',
featuresMetricsShowUserSpecific: 'features.metrics.showUserSpecific',
+ featuresVersionChecking: 'features.versionChecking',
+ featuresVersionAPI: 'features.versionAPI',
+
invitesEnabled: 'invites.enabled',
invitesLength: 'invites.length',
diff --git a/src/lib/config/validate.ts b/src/lib/config/validate.ts
index 824b5a84..046e7e60 100755
--- a/src/lib/config/validate.ts
+++ b/src/lib/config/validate.ts
@@ -160,6 +160,8 @@ export const schema = z.object({
adminOnly: z.boolean().default(false),
showUserSpecific: z.boolean().default(true),
}),
+ versionChecking: z.boolean().default(true),
+ versionAPI: z.string().url().default('https://zipline-version.diced.sh/'),
}),
invites: z.object({
enabled: z.boolean().default(true),
diff --git a/src/server/routes/api/server/settings.ts b/src/server/routes/api/server/settings.ts
index b287aa02..76b3dedb 100644
--- a/src/server/routes/api/server/settings.ts
+++ b/src/server/routes/api/server/settings.ts
@@ -166,6 +166,9 @@ export default fastifyPlugin(
featuresMetricsAdminOnly: z.boolean(),
featuresMetricsShowUserSpecific: z.boolean(),
+ featuresVersionChecking: z.boolean(),
+ featuresVersionAPI: z.string().url(),
+
invitesEnabled: z.boolean(),
invitesLength: z.number().min(1).max(64),
diff --git a/src/server/routes/api/version.ts b/src/server/routes/api/version.ts
index 28d703d2..bc496448 100755
--- a/src/server/routes/api/version.ts
+++ b/src/server/routes/api/version.ts
@@ -1,6 +1,8 @@
+import { config } from '@/lib/config';
+import { log } from '@/lib/logger';
+import { getVersion } from '@/lib/version';
import { userMiddleware } from '@/server/middleware/user';
import fastifyPlugin from 'fastify-plugin';
-import { getVersion } from '@/lib/version';
export type ApiVersionResponse = {
details: ReturnType;
@@ -27,25 +29,37 @@ interface VersionAPI {
};
}
+const logger = log('api').c('version');
+
export const PATH = '/api/version';
export default fastifyPlugin(
(server, _, done) => {
server.get(PATH, { preHandler: [userMiddleware] }, async (_, res) => {
+ if (!config.features.versionChecking) return res.notFound();
+
const details = getVersion();
- const params = new URLSearchParams([['details', JSON.stringify(details)]]);
- const resp = await fetch(`https://zipline-version.diced.sh/?${params.toString()}`);
+ const url = new URL(config.features.versionAPI);
+ url.pathname = '/';
+ url.searchParams.set('details', JSON.stringify(details));
- if (!resp.ok) {
- return res.internalServerError('failed to fetch version details: ' + (await resp.text()));
+ try {
+ const resp = await fetch(url);
+
+ if (!resp.ok) {
+ return res.internalServerError('failed to fetch version details: ' + (await resp.text()));
+ }
+
+ const data: VersionAPI = await resp.json();
+
+ return res.send({
+ data,
+ details,
+ });
+ } catch (e) {
+ logger.error('failed to fetch version details').error(e as Error);
+ return res.internalServerError('failed to fetch version details: ' + (e as Error).message);
}
-
- const data: VersionAPI = await resp.json();
-
- return res.send({
- data,
- details,
- });
});
done();