mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-14 07:55:18 +00:00
refactor(settings): migrate user settings to webServerSettings schema and update related components
This commit is contained in:
+11
-5
@@ -7,9 +7,12 @@ interface Props {
|
|||||||
serverId?: string;
|
serverId?: string;
|
||||||
}
|
}
|
||||||
export const ToggleDockerCleanup = ({ serverId }: Props) => {
|
export const ToggleDockerCleanup = ({ serverId }: Props) => {
|
||||||
const { data, refetch } = api.user.get.useQuery(undefined, {
|
const { data, refetch } = api.settings.getWebServerSettings.useQuery(
|
||||||
enabled: !serverId,
|
undefined,
|
||||||
});
|
{
|
||||||
|
enabled: !serverId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const { data: server, refetch: refetchServer } = api.server.one.useQuery(
|
const { data: server, refetch: refetchServer } = api.server.one.useQuery(
|
||||||
{
|
{
|
||||||
@@ -22,7 +25,7 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => {
|
|||||||
|
|
||||||
const enabled = serverId
|
const enabled = serverId
|
||||||
? server?.enableDockerCleanup
|
? server?.enableDockerCleanup
|
||||||
: data?.user.enableDockerCleanup;
|
: data?.enableDockerCleanup;
|
||||||
|
|
||||||
const { mutateAsync } = api.settings.updateDockerCleanup.useMutation();
|
const { mutateAsync } = api.settings.updateDockerCleanup.useMutation();
|
||||||
|
|
||||||
@@ -30,7 +33,10 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => {
|
|||||||
try {
|
try {
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
enableDockerCleanup: checked,
|
enableDockerCleanup: checked,
|
||||||
serverId: serverId,
|
...(serverId && { serverId }),
|
||||||
|
} as {
|
||||||
|
enableDockerCleanup: boolean;
|
||||||
|
serverId?: string;
|
||||||
});
|
});
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
await refetchServer();
|
await refetchServer();
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ type AddServerDomain = z.infer<typeof addServerDomain>;
|
|||||||
|
|
||||||
export const WebDomain = () => {
|
export const WebDomain = () => {
|
||||||
const { t } = useTranslation("settings");
|
const { t } = useTranslation("settings");
|
||||||
const { data, refetch } = api.user.get.useQuery();
|
const { data, refetch } = api.settings.getWebServerSettings.useQuery();
|
||||||
const { mutateAsync, isLoading } =
|
const { mutateAsync, isLoading } =
|
||||||
api.settings.assignDomainServer.useMutation();
|
api.settings.assignDomainServer.useMutation();
|
||||||
|
|
||||||
@@ -82,15 +82,15 @@ export const WebDomain = () => {
|
|||||||
});
|
});
|
||||||
const https = form.watch("https");
|
const https = form.watch("https");
|
||||||
const domain = form.watch("domain") || "";
|
const domain = form.watch("domain") || "";
|
||||||
const host = data?.user?.host || "";
|
const host = data?.host || "";
|
||||||
const hasChanged = domain !== host;
|
const hasChanged = domain !== host;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
form.reset({
|
form.reset({
|
||||||
domain: data?.user?.host || "",
|
domain: data?.host || "",
|
||||||
certificateType: data?.user?.certificateType,
|
certificateType: data?.certificateType || "none",
|
||||||
letsEncryptEmail: data?.user?.letsEncryptEmail || "",
|
letsEncryptEmail: data?.letsEncryptEmail || "",
|
||||||
https: data?.user?.https || false,
|
https: data?.https || false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [form, form.reset, data]);
|
}, [form, form.reset, data]);
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import { UpdateServer } from "./web-server/update-server";
|
|||||||
|
|
||||||
export const WebServer = () => {
|
export const WebServer = () => {
|
||||||
const { t } = useTranslation("settings");
|
const { t } = useTranslation("settings");
|
||||||
const { data } = api.user.get.useQuery();
|
const { data: webServerSettings } =
|
||||||
|
api.settings.getWebServerSettings.useQuery();
|
||||||
|
|
||||||
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ export const WebServer = () => {
|
|||||||
|
|
||||||
<div className="flex items-center flex-wrap justify-between gap-4">
|
<div className="flex items-center flex-wrap justify-between gap-4">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
Server IP: {data?.user.serverIp}
|
Server IP: {webServerSettings?.serverIp}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
Version: {dokployVersion}
|
Version: {dokployVersion}
|
||||||
|
|||||||
@@ -46,15 +46,15 @@ interface Props {
|
|||||||
export const UpdateServerIp = ({ children }: Props) => {
|
export const UpdateServerIp = ({ children }: Props) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const { data } = api.user.get.useQuery();
|
const { data, refetch } = api.settings.getWebServerSettings.useQuery();
|
||||||
const { data: ip } = api.server.publicIp.useQuery();
|
const { data: ip } = api.server.publicIp.useQuery();
|
||||||
|
|
||||||
const { mutateAsync, isLoading, error, isError } =
|
const { mutateAsync, isLoading, error, isError } =
|
||||||
api.user.update.useMutation();
|
api.settings.updateServerIp.useMutation();
|
||||||
|
|
||||||
const form = useForm<Schema>({
|
const form = useForm<Schema>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
serverIp: data?.user.serverIp || "",
|
serverIp: data?.serverIp || "",
|
||||||
},
|
},
|
||||||
resolver: zodResolver(schema),
|
resolver: zodResolver(schema),
|
||||||
});
|
});
|
||||||
@@ -62,13 +62,11 @@ export const UpdateServerIp = ({ children }: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
form.reset({
|
form.reset({
|
||||||
serverIp: data.user.serverIp || "",
|
serverIp: data.serverIp || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [form, form.reset, data]);
|
}, [form, form.reset, data]);
|
||||||
|
|
||||||
const utils = api.useUtils();
|
|
||||||
|
|
||||||
const setCurrentIp = () => {
|
const setCurrentIp = () => {
|
||||||
if (!ip) return;
|
if (!ip) return;
|
||||||
form.setValue("serverIp", ip);
|
form.setValue("serverIp", ip);
|
||||||
@@ -80,7 +78,7 @@ export const UpdateServerIp = ({ children }: Props) => {
|
|||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Server IP Updated");
|
toast.success("Server IP Updated");
|
||||||
await utils.user.get.invalidate();
|
await refetch();
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
CREATE TABLE "webServerSettings" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"serverIp" text,
|
||||||
|
"certificateType" "certificateType" DEFAULT 'none' NOT NULL,
|
||||||
|
"https" boolean DEFAULT false NOT NULL,
|
||||||
|
"host" text,
|
||||||
|
"letsEncryptEmail" text,
|
||||||
|
"sshPrivateKey" text,
|
||||||
|
"enableDockerCleanup" boolean DEFAULT true NOT NULL,
|
||||||
|
"logCleanupCron" text DEFAULT '0 0 * * *',
|
||||||
|
"metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb NOT NULL,
|
||||||
|
"cleanupCacheApplications" boolean DEFAULT false NOT NULL,
|
||||||
|
"cleanupCacheOnPreviews" boolean DEFAULT false NOT NULL,
|
||||||
|
"cleanupCacheOnCompose" boolean DEFAULT false NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now(),
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Migrate data from user table to webServerSettings
|
||||||
|
-- Get the owner user's data and insert into webServerSettings
|
||||||
|
INSERT INTO "webServerSettings" (
|
||||||
|
"id",
|
||||||
|
"serverIp",
|
||||||
|
"certificateType",
|
||||||
|
"https",
|
||||||
|
"host",
|
||||||
|
"letsEncryptEmail",
|
||||||
|
"sshPrivateKey",
|
||||||
|
"enableDockerCleanup",
|
||||||
|
"logCleanupCron",
|
||||||
|
"metricsConfig",
|
||||||
|
"cleanupCacheApplications",
|
||||||
|
"cleanupCacheOnPreviews",
|
||||||
|
"cleanupCacheOnCompose",
|
||||||
|
"created_at",
|
||||||
|
"updated_at"
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
gen_random_uuid()::text as "id",
|
||||||
|
u."serverIp",
|
||||||
|
COALESCE(u."certificateType", 'none') as "certificateType",
|
||||||
|
COALESCE(u."https", false) as "https",
|
||||||
|
u."host",
|
||||||
|
u."letsEncryptEmail",
|
||||||
|
u."sshPrivateKey",
|
||||||
|
COALESCE(u."enableDockerCleanup", true) as "enableDockerCleanup",
|
||||||
|
COALESCE(u."logCleanupCron", '0 0 * * *') as "logCleanupCron",
|
||||||
|
COALESCE(
|
||||||
|
u."metricsConfig",
|
||||||
|
'{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb
|
||||||
|
) as "metricsConfig",
|
||||||
|
COALESCE(u."cleanupCacheApplications", false) as "cleanupCacheApplications",
|
||||||
|
COALESCE(u."cleanupCacheOnPreviews", false) as "cleanupCacheOnPreviews",
|
||||||
|
COALESCE(u."cleanupCacheOnCompose", false) as "cleanupCacheOnCompose",
|
||||||
|
NOW() as "created_at",
|
||||||
|
NOW() as "updated_at"
|
||||||
|
FROM "user" u
|
||||||
|
INNER JOIN "member" m ON u."id" = m."user_id"
|
||||||
|
WHERE m."role" = 'owner'
|
||||||
|
ORDER BY m."created_at" ASC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- If no owner found, create a default entry
|
||||||
|
INSERT INTO "webServerSettings" (
|
||||||
|
"id",
|
||||||
|
"serverIp",
|
||||||
|
"certificateType",
|
||||||
|
"https",
|
||||||
|
"host",
|
||||||
|
"letsEncryptEmail",
|
||||||
|
"sshPrivateKey",
|
||||||
|
"enableDockerCleanup",
|
||||||
|
"logCleanupCron",
|
||||||
|
"metricsConfig",
|
||||||
|
"cleanupCacheApplications",
|
||||||
|
"cleanupCacheOnPreviews",
|
||||||
|
"cleanupCacheOnCompose",
|
||||||
|
"created_at",
|
||||||
|
"updated_at"
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
gen_random_uuid()::text as "id",
|
||||||
|
NULL as "serverIp",
|
||||||
|
'none' as "certificateType",
|
||||||
|
false as "https",
|
||||||
|
NULL as "host",
|
||||||
|
NULL as "letsEncryptEmail",
|
||||||
|
NULL as "sshPrivateKey",
|
||||||
|
true as "enableDockerCleanup",
|
||||||
|
'0 0 * * *' as "logCleanupCron",
|
||||||
|
'{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb as "metricsConfig",
|
||||||
|
false as "cleanupCacheApplications",
|
||||||
|
false as "cleanupCacheOnPreviews",
|
||||||
|
false as "cleanupCacheOnCompose",
|
||||||
|
NOW() as "created_at",
|
||||||
|
NOW() as "updated_at"
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM "webServerSettings"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "serverIp";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "certificateType";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "https";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "host";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "letsEncryptEmail";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "sshPrivateKey";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "enableDockerCleanup";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "logCleanupCron";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "metricsConfig";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "cleanupCacheApplications";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "cleanupCacheOnPreviews";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "cleanupCacheOnCompose";
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -932,6 +932,13 @@
|
|||||||
"when": 1765346573500,
|
"when": 1765346573500,
|
||||||
"tag": "0132_clean_layla_miller",
|
"tag": "0132_clean_layla_miller",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 133,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1766301478005,
|
||||||
|
"tag": "0133_striped_the_order",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
findUserById,
|
getWebServerSettings,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
setupWebMonitoring,
|
setupWebMonitoring,
|
||||||
updateUser,
|
updateWebServerSettings,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { apiUpdateWebServerMonitoring } from "@/server/db/schema";
|
import { apiUpdateWebServerMonitoring } from "@/server/db/schema";
|
||||||
@@ -11,7 +11,7 @@ import { adminProcedure, createTRPCRouter } from "../trpc";
|
|||||||
export const adminRouter = createTRPCRouter({
|
export const adminRouter = createTRPCRouter({
|
||||||
setupMonitoring: adminProcedure
|
setupMonitoring: adminProcedure
|
||||||
.input(apiUpdateWebServerMonitoring)
|
.input(apiUpdateWebServerMonitoring)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input }) => {
|
||||||
try {
|
try {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@@ -19,15 +19,8 @@ export const adminRouter = createTRPCRouter({
|
|||||||
message: "Feature disabled on cloud",
|
message: "Feature disabled on cloud",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const user = await findUserById(ctx.user.ownerId);
|
|
||||||
if (user.id !== ctx.user.ownerId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to setup the monitoring",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateUser(user.id, {
|
await updateWebServerSettings({
|
||||||
metricsConfig: {
|
metricsConfig: {
|
||||||
server: {
|
server: {
|
||||||
type: "Dokploy",
|
type: "Dokploy",
|
||||||
@@ -52,8 +45,9 @@ export const adminRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentServer = await setupWebMonitoring(user.id);
|
await setupWebMonitoring();
|
||||||
return currentServer;
|
const settings = await getWebServerSettings();
|
||||||
|
return settings;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import {
|
|||||||
DEFAULT_UPDATE_DATA,
|
DEFAULT_UPDATE_DATA,
|
||||||
execAsync,
|
execAsync,
|
||||||
findServerById,
|
findServerById,
|
||||||
findUserById,
|
|
||||||
getDokployImage,
|
getDokployImage,
|
||||||
getDokployImageTag,
|
getDokployImageTag,
|
||||||
getLogCleanupStatus,
|
getLogCleanupStatus,
|
||||||
getUpdateData,
|
getUpdateData,
|
||||||
|
getWebServerSettings,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
parseRawConfig,
|
parseRawConfig,
|
||||||
paths,
|
paths,
|
||||||
@@ -40,7 +40,7 @@ import {
|
|||||||
updateLetsEncryptEmail,
|
updateLetsEncryptEmail,
|
||||||
updateServerById,
|
updateServerById,
|
||||||
updateServerTraefik,
|
updateServerTraefik,
|
||||||
updateUser,
|
updateWebServerSettings,
|
||||||
writeConfig,
|
writeConfig,
|
||||||
writeMainConfig,
|
writeMainConfig,
|
||||||
writeTraefikConfigInPath,
|
writeTraefikConfigInPath,
|
||||||
@@ -77,6 +77,13 @@ import {
|
|||||||
} from "../trpc";
|
} from "../trpc";
|
||||||
|
|
||||||
export const settingsRouter = createTRPCRouter({
|
export const settingsRouter = createTRPCRouter({
|
||||||
|
getWebServerSettings: protectedProcedure.query(async () => {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const settings = await getWebServerSettings();
|
||||||
|
return settings;
|
||||||
|
}),
|
||||||
reloadServer: adminProcedure.mutation(async () => {
|
reloadServer: adminProcedure.mutation(async () => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return true;
|
return true;
|
||||||
@@ -209,11 +216,11 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveSSHPrivateKey: adminProcedure
|
saveSSHPrivateKey: adminProcedure
|
||||||
.input(apiSaveSSHKey)
|
.input(apiSaveSSHKey)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input }) => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await updateUser(ctx.user.ownerId, {
|
await updateWebServerSettings({
|
||||||
sshPrivateKey: input.sshPrivateKey,
|
sshPrivateKey: input.sshPrivateKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -221,36 +228,36 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
assignDomainServer: adminProcedure
|
assignDomainServer: adminProcedure
|
||||||
.input(apiAssignDomain)
|
.input(apiAssignDomain)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ input }) => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const user = await updateUser(ctx.user.ownerId, {
|
const settings = await updateWebServerSettings({
|
||||||
host: input.host,
|
host: input.host,
|
||||||
letsEncryptEmail: input.letsEncryptEmail,
|
letsEncryptEmail: input.letsEncryptEmail,
|
||||||
certificateType: input.certificateType,
|
certificateType: input.certificateType,
|
||||||
https: input.https,
|
https: input.https,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!settings) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
message: "User not found",
|
message: "Web server settings not found",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateServerTraefik(user, input.host);
|
updateServerTraefik(settings, input.host);
|
||||||
if (input.letsEncryptEmail) {
|
if (input.letsEncryptEmail) {
|
||||||
updateLetsEncryptEmail(input.letsEncryptEmail);
|
updateLetsEncryptEmail(input.letsEncryptEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return settings;
|
||||||
}),
|
}),
|
||||||
cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => {
|
cleanSSHPrivateKey: adminProcedure.mutation(async () => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await updateUser(ctx.user.ownerId, {
|
await updateWebServerSettings({
|
||||||
sshPrivateKey: null,
|
sshPrivateKey: null,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -310,11 +317,11 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!IS_CLOUD) {
|
} else if (!IS_CLOUD) {
|
||||||
const userUpdated = await updateUser(ctx.user.ownerId, {
|
const settingsUpdated = await updateWebServerSettings({
|
||||||
enableDockerCleanup: input.enableDockerCleanup,
|
enableDockerCleanup: input.enableDockerCleanup,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (userUpdated?.enableDockerCleanup) {
|
if (settingsUpdated?.enableDockerCleanup) {
|
||||||
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
|
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
|
||||||
console.log(
|
console.log(
|
||||||
`Docker Cleanup ${new Date().toLocaleString()}] Running...`,
|
`Docker Cleanup ${new Date().toLocaleString()}] Running...`,
|
||||||
@@ -488,13 +495,28 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return readConfigInPath(input.path, input.serverId);
|
return readConfigInPath(input.path, input.serverId);
|
||||||
}),
|
}),
|
||||||
getIp: protectedProcedure.query(async ({ ctx }) => {
|
getIp: protectedProcedure.query(async () => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const user = await findUserById(ctx.user.ownerId);
|
const settings = await getWebServerSettings();
|
||||||
return user.serverIp;
|
return settings?.serverIp || "";
|
||||||
}),
|
}),
|
||||||
|
updateServerIp: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
serverIp: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const settings = await updateWebServerSettings({
|
||||||
|
serverIp: input.serverIp,
|
||||||
|
});
|
||||||
|
return settings;
|
||||||
|
}),
|
||||||
|
|
||||||
getOpenApiDocument: protectedProcedure.query(
|
getOpenApiDocument: protectedProcedure.query(
|
||||||
async ({ ctx }): Promise<unknown> => {
|
async ({ ctx }): Promise<unknown> => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
findUserById,
|
findUserById,
|
||||||
getDokployUrl,
|
getDokployUrl,
|
||||||
getUserByToken,
|
getUserByToken,
|
||||||
|
getWebServerSettings,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
removeUserById,
|
removeUserById,
|
||||||
sendEmailNotification,
|
sendEmailNotification,
|
||||||
@@ -214,10 +215,11 @@ export const userRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
getMetricsToken: protectedProcedure.query(async ({ ctx }) => {
|
getMetricsToken: protectedProcedure.query(async ({ ctx }) => {
|
||||||
const user = await findUserById(ctx.user.ownerId);
|
const user = await findUserById(ctx.user.ownerId);
|
||||||
|
const settings = await getWebServerSettings();
|
||||||
return {
|
return {
|
||||||
serverIp: user.serverIp,
|
serverIp: settings?.serverIp,
|
||||||
enabledFeatures: user.enablePaidFeatures,
|
enabledFeatures: user.enablePaidFeatures,
|
||||||
metricsConfig: user?.metricsConfig,
|
metricsConfig: settings?.metricsConfig,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
remove: protectedProcedure
|
remove: protectedProcedure
|
||||||
|
|||||||
@@ -35,3 +35,4 @@ export * from "./ssh-key";
|
|||||||
export * from "./user";
|
export * from "./user";
|
||||||
export * from "./utils";
|
export * from "./utils";
|
||||||
export * from "./volume-backups";
|
export * from "./volume-backups";
|
||||||
|
export * from "./web-server-settings";
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { relations } from "drizzle-orm";
|
|||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
integer,
|
integer,
|
||||||
jsonb,
|
|
||||||
pgTable,
|
pgTable,
|
||||||
text,
|
text,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -15,7 +14,6 @@ import { account, apikey, organization } from "./account";
|
|||||||
import { backups } from "./backups";
|
import { backups } from "./backups";
|
||||||
import { projects } from "./project";
|
import { projects } from "./project";
|
||||||
import { schedules } from "./schedule";
|
import { schedules } from "./schedule";
|
||||||
import { certificateType } from "./shared";
|
|
||||||
/**
|
/**
|
||||||
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
|
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
|
||||||
* database instance for multiple projects.
|
* database instance for multiple projects.
|
||||||
@@ -51,73 +49,10 @@ export const user = pgTable("user", {
|
|||||||
banExpires: timestamp("ban_expires"),
|
banExpires: timestamp("ban_expires"),
|
||||||
updatedAt: timestamp("updated_at").notNull(),
|
updatedAt: timestamp("updated_at").notNull(),
|
||||||
// Admin
|
// Admin
|
||||||
serverIp: text("serverIp"),
|
|
||||||
certificateType: certificateType("certificateType").notNull().default("none"),
|
|
||||||
https: boolean("https").notNull().default(false),
|
|
||||||
host: text("host"),
|
|
||||||
letsEncryptEmail: text("letsEncryptEmail"),
|
|
||||||
sshPrivateKey: text("sshPrivateKey"),
|
|
||||||
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(true),
|
|
||||||
logCleanupCron: text("logCleanupCron").default("0 0 * * *"),
|
|
||||||
role: text("role").notNull().default("user"),
|
role: text("role").notNull().default("user"),
|
||||||
// Metrics
|
// Metrics
|
||||||
enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false),
|
enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false),
|
||||||
allowImpersonation: boolean("allowImpersonation").notNull().default(false),
|
allowImpersonation: boolean("allowImpersonation").notNull().default(false),
|
||||||
metricsConfig: jsonb("metricsConfig")
|
|
||||||
.$type<{
|
|
||||||
server: {
|
|
||||||
type: "Dokploy" | "Remote";
|
|
||||||
refreshRate: number;
|
|
||||||
port: number;
|
|
||||||
token: string;
|
|
||||||
urlCallback: string;
|
|
||||||
retentionDays: number;
|
|
||||||
cronJob: string;
|
|
||||||
thresholds: {
|
|
||||||
cpu: number;
|
|
||||||
memory: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
containers: {
|
|
||||||
refreshRate: number;
|
|
||||||
services: {
|
|
||||||
include: string[];
|
|
||||||
exclude: string[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}>()
|
|
||||||
.notNull()
|
|
||||||
.default({
|
|
||||||
server: {
|
|
||||||
type: "Dokploy",
|
|
||||||
refreshRate: 60,
|
|
||||||
port: 4500,
|
|
||||||
token: "",
|
|
||||||
retentionDays: 2,
|
|
||||||
cronJob: "",
|
|
||||||
urlCallback: "",
|
|
||||||
thresholds: {
|
|
||||||
cpu: 0,
|
|
||||||
memory: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
containers: {
|
|
||||||
refreshRate: 60,
|
|
||||||
services: {
|
|
||||||
include: [],
|
|
||||||
exclude: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
cleanupCacheApplications: boolean("cleanupCacheApplications")
|
|
||||||
.notNull()
|
|
||||||
.default(false),
|
|
||||||
cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews")
|
|
||||||
.notNull()
|
|
||||||
.default(false),
|
|
||||||
cleanupCacheOnCompose: boolean("cleanupCacheOnCompose")
|
|
||||||
.notNull()
|
|
||||||
.default(false),
|
|
||||||
stripeCustomerId: text("stripeCustomerId"),
|
stripeCustomerId: text("stripeCustomerId"),
|
||||||
stripeSubscriptionId: text("stripeSubscriptionId"),
|
stripeSubscriptionId: text("stripeSubscriptionId"),
|
||||||
serversQuantity: integer("serversQuantity").notNull().default(0),
|
serversQuantity: integer("serversQuantity").notNull().default(0),
|
||||||
@@ -203,33 +138,6 @@ export const apiFindOneUserByAuth = createSchema
|
|||||||
// authId: true,
|
// authId: true,
|
||||||
})
|
})
|
||||||
.required();
|
.required();
|
||||||
export const apiSaveSSHKey = createSchema
|
|
||||||
.pick({
|
|
||||||
sshPrivateKey: true,
|
|
||||||
})
|
|
||||||
.required();
|
|
||||||
|
|
||||||
export const apiAssignDomain = createSchema
|
|
||||||
.pick({
|
|
||||||
host: true,
|
|
||||||
certificateType: true,
|
|
||||||
letsEncryptEmail: true,
|
|
||||||
https: true,
|
|
||||||
})
|
|
||||||
.required()
|
|
||||||
.partial({
|
|
||||||
letsEncryptEmail: true,
|
|
||||||
https: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const apiUpdateDockerCleanup = createSchema
|
|
||||||
.pick({
|
|
||||||
enableDockerCleanup: true,
|
|
||||||
})
|
|
||||||
.required()
|
|
||||||
.extend({
|
|
||||||
serverId: z.string().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const apiTraefikConfig = z.object({
|
export const apiTraefikConfig = z.object({
|
||||||
traefikConfig: z.string().min(1),
|
traefikConfig: z.string().min(1),
|
||||||
@@ -298,32 +206,6 @@ export const apiReadStatsLogs = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiUpdateWebServerMonitoring = z.object({
|
|
||||||
metricsConfig: z
|
|
||||||
.object({
|
|
||||||
server: z.object({
|
|
||||||
refreshRate: z.number().min(2),
|
|
||||||
port: z.number().min(1),
|
|
||||||
token: z.string(),
|
|
||||||
urlCallback: z.string().url(),
|
|
||||||
retentionDays: z.number().min(1),
|
|
||||||
cronJob: z.string().min(1),
|
|
||||||
thresholds: z.object({
|
|
||||||
cpu: z.number().min(0),
|
|
||||||
memory: z.number().min(0),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
containers: z.object({
|
|
||||||
refreshRate: z.number().min(2),
|
|
||||||
services: z.object({
|
|
||||||
include: z.array(z.string()).optional(),
|
|
||||||
exclude: z.array(z.string()).optional(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.required(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const apiUpdateUser = createSchema.partial().extend({
|
export const apiUpdateUser = createSchema.partial().extend({
|
||||||
email: z
|
email: z
|
||||||
.string()
|
.string()
|
||||||
@@ -334,29 +216,4 @@ export const apiUpdateUser = createSchema.partial().extend({
|
|||||||
currentPassword: z.string().optional(),
|
currentPassword: z.string().optional(),
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
lastName: z.string().optional(),
|
lastName: z.string().optional(),
|
||||||
metricsConfig: z
|
|
||||||
.object({
|
|
||||||
server: z.object({
|
|
||||||
type: z.enum(["Dokploy", "Remote"]),
|
|
||||||
refreshRate: z.number(),
|
|
||||||
port: z.number(),
|
|
||||||
token: z.string(),
|
|
||||||
urlCallback: z.string(),
|
|
||||||
retentionDays: z.number(),
|
|
||||||
cronJob: z.string(),
|
|
||||||
thresholds: z.object({
|
|
||||||
cpu: z.number(),
|
|
||||||
memory: z.number(),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
containers: z.object({
|
|
||||||
refreshRate: z.number(),
|
|
||||||
services: z.object({
|
|
||||||
include: z.array(z.string()),
|
|
||||||
exclude: z.array(z.string()),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
logCleanupCron: z.string().optional().nullable(),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import { relations } from "drizzle-orm";
|
||||||
|
import { boolean, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||||
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { certificateType } from "./shared";
|
||||||
|
|
||||||
|
export const webServerSettings = pgTable("webServerSettings", {
|
||||||
|
id: text("id")
|
||||||
|
.notNull()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => nanoid()),
|
||||||
|
// Web Server Configuration
|
||||||
|
serverIp: text("serverIp"),
|
||||||
|
certificateType: certificateType("certificateType").notNull().default("none"),
|
||||||
|
https: boolean("https").notNull().default(false),
|
||||||
|
host: text("host"),
|
||||||
|
letsEncryptEmail: text("letsEncryptEmail"),
|
||||||
|
sshPrivateKey: text("sshPrivateKey"),
|
||||||
|
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(true),
|
||||||
|
logCleanupCron: text("logCleanupCron").default("0 0 * * *"),
|
||||||
|
// Metrics Configuration
|
||||||
|
metricsConfig: jsonb("metricsConfig")
|
||||||
|
.$type<{
|
||||||
|
server: {
|
||||||
|
type: "Dokploy" | "Remote";
|
||||||
|
refreshRate: number;
|
||||||
|
port: number;
|
||||||
|
token: string;
|
||||||
|
urlCallback: string;
|
||||||
|
retentionDays: number;
|
||||||
|
cronJob: string;
|
||||||
|
thresholds: {
|
||||||
|
cpu: number;
|
||||||
|
memory: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
containers: {
|
||||||
|
refreshRate: number;
|
||||||
|
services: {
|
||||||
|
include: string[];
|
||||||
|
exclude: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}>()
|
||||||
|
.notNull()
|
||||||
|
.default({
|
||||||
|
server: {
|
||||||
|
type: "Dokploy",
|
||||||
|
refreshRate: 60,
|
||||||
|
port: 4500,
|
||||||
|
token: "",
|
||||||
|
retentionDays: 2,
|
||||||
|
cronJob: "",
|
||||||
|
urlCallback: "",
|
||||||
|
thresholds: {
|
||||||
|
cpu: 0,
|
||||||
|
memory: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
containers: {
|
||||||
|
refreshRate: 60,
|
||||||
|
services: {
|
||||||
|
include: [],
|
||||||
|
exclude: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
// Cache Cleanup Configuration
|
||||||
|
cleanupCacheApplications: boolean("cleanupCacheApplications")
|
||||||
|
.notNull()
|
||||||
|
.default(false),
|
||||||
|
cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews")
|
||||||
|
.notNull()
|
||||||
|
.default(false),
|
||||||
|
cleanupCacheOnCompose: boolean("cleanupCacheOnCompose")
|
||||||
|
.notNull()
|
||||||
|
.default(false),
|
||||||
|
createdAt: timestamp("created_at").defaultNow(),
|
||||||
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const webServerSettingsRelations = relations(
|
||||||
|
webServerSettings,
|
||||||
|
() => ({}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const createSchema = createInsertSchema(webServerSettings, {
|
||||||
|
id: z.string().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiUpdateWebServerSettings = createSchema.partial().extend({
|
||||||
|
serverIp: z.string().optional(),
|
||||||
|
certificateType: z.enum(["letsencrypt", "none", "custom"]).optional(),
|
||||||
|
https: z.boolean().optional(),
|
||||||
|
host: z.string().optional(),
|
||||||
|
letsEncryptEmail: z.string().email().optional().nullable(),
|
||||||
|
sshPrivateKey: z.string().optional(),
|
||||||
|
enableDockerCleanup: z.boolean().optional(),
|
||||||
|
logCleanupCron: z.string().optional().nullable(),
|
||||||
|
metricsConfig: z
|
||||||
|
.object({
|
||||||
|
server: z.object({
|
||||||
|
type: z.enum(["Dokploy", "Remote"]),
|
||||||
|
refreshRate: z.number(),
|
||||||
|
port: z.number(),
|
||||||
|
token: z.string(),
|
||||||
|
urlCallback: z.string(),
|
||||||
|
retentionDays: z.number(),
|
||||||
|
cronJob: z.string(),
|
||||||
|
thresholds: z.object({
|
||||||
|
cpu: z.number(),
|
||||||
|
memory: z.number(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
containers: z.object({
|
||||||
|
refreshRate: z.number(),
|
||||||
|
services: z.object({
|
||||||
|
include: z.array(z.string()),
|
||||||
|
exclude: z.array(z.string()),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
cleanupCacheApplications: z.boolean().optional(),
|
||||||
|
cleanupCacheOnPreviews: z.boolean().optional(),
|
||||||
|
cleanupCacheOnCompose: z.boolean().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiAssignDomain = z
|
||||||
|
.object({
|
||||||
|
host: z.string(),
|
||||||
|
certificateType: z.enum(["letsencrypt", "none", "custom"]),
|
||||||
|
letsEncryptEmail: z.string().email().optional().nullable(),
|
||||||
|
https: z.boolean().optional(),
|
||||||
|
})
|
||||||
|
.required()
|
||||||
|
.partial({
|
||||||
|
letsEncryptEmail: true,
|
||||||
|
https: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiSaveSSHKey = z
|
||||||
|
.object({
|
||||||
|
sshPrivateKey: z.string(),
|
||||||
|
})
|
||||||
|
.required();
|
||||||
|
|
||||||
|
export const apiUpdateDockerCleanup = z.object({
|
||||||
|
enableDockerCleanup: z.boolean(),
|
||||||
|
serverId: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const apiUpdateWebServerMonitoring = z.object({
|
||||||
|
metricsConfig: z
|
||||||
|
.object({
|
||||||
|
server: z.object({
|
||||||
|
refreshRate: z.number().min(2),
|
||||||
|
port: z.number().min(1),
|
||||||
|
token: z.string(),
|
||||||
|
urlCallback: z.string().url(),
|
||||||
|
retentionDays: z.number().min(1),
|
||||||
|
cronJob: z.string().min(1),
|
||||||
|
thresholds: z.object({
|
||||||
|
cpu: z.number().min(0),
|
||||||
|
memory: z.number().min(0),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
containers: z.object({
|
||||||
|
refreshRate: z.number().min(2),
|
||||||
|
services: z.object({
|
||||||
|
include: z.array(z.string()).optional(),
|
||||||
|
exclude: z.array(z.string()).optional(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
|
});
|
||||||
@@ -41,6 +41,7 @@ export * from "./services/settings";
|
|||||||
export * from "./services/ssh-key";
|
export * from "./services/ssh-key";
|
||||||
export * from "./services/user";
|
export * from "./services/user";
|
||||||
export * from "./services/volume-backups";
|
export * from "./services/volume-backups";
|
||||||
|
export * from "./services/web-server-settings";
|
||||||
export * from "./setup/config-paths";
|
export * from "./setup/config-paths";
|
||||||
export * from "./setup/monitoring-setup";
|
export * from "./setup/monitoring-setup";
|
||||||
export * from "./setup/postgres-setup";
|
export * from "./setup/postgres-setup";
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { IS_CLOUD } from "../constants";
|
|||||||
import { db } from "../db";
|
import { db } from "../db";
|
||||||
import * as schema from "../db/schema";
|
import * as schema from "../db/schema";
|
||||||
import { getUserByToken } from "../services/admin";
|
import { getUserByToken } from "../services/admin";
|
||||||
import { updateUser } from "../services/user";
|
import { getWebServerSettings } from "../services/web-server-settings";
|
||||||
import { getHubSpotUTK, submitToHubSpot } from "../utils/tracking/hubspot";
|
import { getHubSpotUTK, submitToHubSpot } from "../utils/tracking/hubspot";
|
||||||
import { sendEmail } from "../verification/send-verification-email";
|
import { sendEmail } from "../verification/send-verification-email";
|
||||||
import { getPublicIpWithFallback } from "../wss/utils";
|
import { getPublicIpWithFallback } from "../wss/utils";
|
||||||
@@ -43,11 +43,13 @@ const { handler, api } = betterAuth({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (admin?.user) {
|
if (admin?.user) {
|
||||||
|
const settings = await getWebServerSettings();
|
||||||
|
if (!settings) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
...(admin.user.serverIp
|
...(settings.serverIp ? [`http://${settings.serverIp}:3000`] : []),
|
||||||
? [`http://${admin.user.serverIp}:3000`]
|
...(settings.host ? [`https://${settings.host}`] : []),
|
||||||
: []),
|
|
||||||
...(admin.user.host ? [`https://${admin.user.host}`] : []),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
@@ -122,7 +124,10 @@ const { handler, api } = betterAuth({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!IS_CLOUD) {
|
if (!IS_CLOUD) {
|
||||||
await updateUser(user.id, {
|
const { updateWebServerSettings } = await import(
|
||||||
|
"../services/web-server-settings"
|
||||||
|
);
|
||||||
|
await updateWebServerSettings({
|
||||||
serverIp: await getPublicIpWithFallback(),
|
serverIp: await getPublicIpWithFallback(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { IS_CLOUD } from "../constants";
|
import { IS_CLOUD } from "../constants";
|
||||||
|
import { getWebServerSettings } from "./web-server-settings";
|
||||||
|
|
||||||
export const findUserById = async (userId: string) => {
|
export const findUserById = async (userId: string) => {
|
||||||
const userResult = await db.query.user.findFirst({
|
const userResult = await db.query.user.findFirst({
|
||||||
@@ -107,11 +108,11 @@ export const getDokployUrl = async () => {
|
|||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return "https://app.dokploy.com";
|
return "https://app.dokploy.com";
|
||||||
}
|
}
|
||||||
const owner = await findOwner();
|
const settings = await getWebServerSettings();
|
||||||
|
|
||||||
if (owner.user.host) {
|
if (settings.host) {
|
||||||
const protocol = owner.user.https ? "https" : "http";
|
const protocol = settings.https ? "https" : "http";
|
||||||
return `${protocol}://${owner.user.host}`;
|
return `${protocol}://${settings.host}`;
|
||||||
}
|
}
|
||||||
return `http://${owner.user.serverIp}:${process.env.PORT}`;
|
return `http://${settings.serverIp}:${process.env.PORT}`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { generateObject } from "ai";
|
|||||||
import { desc, eq } from "drizzle-orm";
|
import { desc, eq } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { IS_CLOUD } from "../constants";
|
import { IS_CLOUD } from "../constants";
|
||||||
import { findOrganizationById } from "./admin";
|
|
||||||
import { findServerById } from "./server";
|
import { findServerById } from "./server";
|
||||||
|
import { getWebServerSettings } from "./web-server-settings";
|
||||||
|
|
||||||
export const getAiSettingsByOrganizationId = async (organizationId: string) => {
|
export const getAiSettingsByOrganizationId = async (organizationId: string) => {
|
||||||
const aiSettings = await db.query.ai.findMany({
|
const aiSettings = await db.query.ai.findMany({
|
||||||
@@ -79,8 +79,8 @@ export const suggestVariants = async ({
|
|||||||
|
|
||||||
let ip = "";
|
let ip = "";
|
||||||
if (!IS_CLOUD) {
|
if (!IS_CLOUD) {
|
||||||
const organization = await findOrganizationById(organizationId);
|
const settings = await getWebServerSettings();
|
||||||
ip = organization?.owner.serverIp || "";
|
ip = settings?.serverIp || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { promisify } from "node:util";
|
|||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
import { generateRandomDomain } from "@dokploy/server/templates";
|
import { generateRandomDomain } from "@dokploy/server/templates";
|
||||||
import { manageDomain } from "@dokploy/server/utils/traefik/domain";
|
import { manageDomain } from "@dokploy/server/utils/traefik/domain";
|
||||||
|
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { type apiCreateDomain, domains } from "../db/schema";
|
import { type apiCreateDomain, domains } from "../db/schema";
|
||||||
import { findUserById } from "./admin";
|
|
||||||
import { findApplicationById } from "./application";
|
import { findApplicationById } from "./application";
|
||||||
import { detectCDNProvider } from "./cdn";
|
import { detectCDNProvider } from "./cdn";
|
||||||
import { findServerById } from "./server";
|
import { findServerById } from "./server";
|
||||||
@@ -61,9 +61,9 @@ export const generateTraefikMeDomain = async (
|
|||||||
projectName: appName,
|
projectName: appName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const admin = await findUserById(userId);
|
const settings = await getWebServerSettings();
|
||||||
return generateRandomDomain({
|
return generateRandomDomain({
|
||||||
serverIp: admin?.serverIp || "",
|
serverIp: settings?.serverIp || "",
|
||||||
projectName: appName,
|
projectName: appName,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -253,8 +253,11 @@ const generateWildcardDomain = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ip) {
|
if (!ip) {
|
||||||
const admin = await findUserById(userId);
|
const { getWebServerSettings } = await import(
|
||||||
ip = admin?.serverIp || "";
|
"../services/web-server-settings"
|
||||||
|
);
|
||||||
|
const settings = await getWebServerSettings();
|
||||||
|
ip = settings?.serverIp || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const slugIp = ip.replaceAll(".", "-");
|
const slugIp = ip.replaceAll(".", "-");
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { db } from "@dokploy/server/db";
|
||||||
|
import { webServerSettings } from "@dokploy/server/db/schema";
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the web server settings (singleton - only one row should exist)
|
||||||
|
*/
|
||||||
|
export const getWebServerSettings = async () => {
|
||||||
|
const settings = await db.query.webServerSettings.findFirst({
|
||||||
|
orderBy: (settings, { asc }) => [asc(settings.createdAt)],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!settings) {
|
||||||
|
// Create default settings if none exist
|
||||||
|
const [newSettings] = await db
|
||||||
|
.insert(webServerSettings)
|
||||||
|
.values({})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return newSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update web server settings
|
||||||
|
*/
|
||||||
|
export const updateWebServerSettings = async (
|
||||||
|
updates: Partial<typeof webServerSettings.$inferInsert>,
|
||||||
|
) => {
|
||||||
|
const current = await getWebServerSettings();
|
||||||
|
|
||||||
|
if (!current) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Web server settings not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [updated] = await db
|
||||||
|
.update(webServerSettings)
|
||||||
|
.set({
|
||||||
|
...updates,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(webServerSettings.id, current.id))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { findServerById } from "@dokploy/server/services/server";
|
import { findServerById } from "@dokploy/server/services/server";
|
||||||
|
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
||||||
import type { ContainerCreateOptions } from "dockerode";
|
import type { ContainerCreateOptions } from "dockerode";
|
||||||
import { IS_CLOUD } from "../constants";
|
import { IS_CLOUD } from "../constants";
|
||||||
import { findUserById } from "../services/admin";
|
|
||||||
import { getDokployImageTag } from "../services/settings";
|
import { getDokployImageTag } from "../services/settings";
|
||||||
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
|
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
|
||||||
import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
|
import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
|
||||||
@@ -83,8 +83,8 @@ export const setupMonitoring = async (serverId: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupWebMonitoring = async (userId: string) => {
|
export const setupWebMonitoring = async () => {
|
||||||
const user = await findUserById(userId);
|
const webServerSettings = await getWebServerSettings();
|
||||||
|
|
||||||
const containerName = "dokploy-monitoring";
|
const containerName = "dokploy-monitoring";
|
||||||
let imageName = "dokploy/monitoring:latest";
|
let imageName = "dokploy/monitoring:latest";
|
||||||
@@ -99,7 +99,7 @@ export const setupWebMonitoring = async (userId: string) => {
|
|||||||
|
|
||||||
const settings: ContainerCreateOptions = {
|
const settings: ContainerCreateOptions = {
|
||||||
name: containerName,
|
name: containerName,
|
||||||
Env: [`METRICS_CONFIG=${JSON.stringify(user?.metricsConfig)}`],
|
Env: [`METRICS_CONFIG=${JSON.stringify(webServerSettings?.metricsConfig)}`],
|
||||||
Image: imageName,
|
Image: imageName,
|
||||||
HostConfig: {
|
HostConfig: {
|
||||||
// Memory: 100 * 1024 * 1024, // 100MB en bytes
|
// Memory: 100 * 1024 * 1024, // 100MB en bytes
|
||||||
@@ -110,9 +110,9 @@ export const setupWebMonitoring = async (userId: string) => {
|
|||||||
Name: "always",
|
Name: "always",
|
||||||
},
|
},
|
||||||
PortBindings: {
|
PortBindings: {
|
||||||
[`${user?.metricsConfig?.server?.port}/tcp`]: [
|
[`${webServerSettings?.metricsConfig?.server?.port}/tcp`]: [
|
||||||
{
|
{
|
||||||
HostPort: user?.metricsConfig?.server?.port.toString(),
|
HostPort: webServerSettings?.metricsConfig?.server?.port.toString(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -126,7 +126,7 @@ export const setupWebMonitoring = async (userId: string) => {
|
|||||||
// NetworkMode: "host",
|
// NetworkMode: "host",
|
||||||
},
|
},
|
||||||
ExposedPorts: {
|
ExposedPorts: {
|
||||||
[`${user?.metricsConfig?.server?.port}/tcp`]: {},
|
[`${webServerSettings?.metricsConfig?.server?.port}/tcp`]: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const docker = await getRemoteDocker();
|
const docker = await getRemoteDocker();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import { findOwner } from "@dokploy/server/services/admin";
|
import { updateWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
||||||
import { updateUser } from "@dokploy/server/services/user";
|
|
||||||
import { scheduledJobs, scheduleJob } from "node-schedule";
|
import { scheduledJobs, scheduleJob } from "node-schedule";
|
||||||
import { execAsync } from "../process/execAsync";
|
import { execAsync } from "../process/execAsync";
|
||||||
|
|
||||||
@@ -29,12 +28,9 @@ export const startLogCleanup = async (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const owner = await findOwner();
|
await updateWebServerSettings({
|
||||||
if (owner) {
|
logCleanupCron: cronExpression,
|
||||||
await updateUser(owner.user.id, {
|
});
|
||||||
logCleanupCron: cronExpression,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -51,12 +47,9 @@ export const stopLogCleanup = async (): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update database
|
// Update database
|
||||||
const owner = await findOwner();
|
await updateWebServerSettings({
|
||||||
if (owner) {
|
logCleanupCron: null,
|
||||||
await updateUser(owner.user.id, {
|
});
|
||||||
logCleanupCron: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -65,12 +58,14 @@ export const stopLogCleanup = async (): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
||||||
|
|
||||||
export const getLogCleanupStatus = async (): Promise<{
|
export const getLogCleanupStatus = async (): Promise<{
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
cronExpression: string | null;
|
cronExpression: string | null;
|
||||||
}> => {
|
}> => {
|
||||||
const owner = await findOwner();
|
const settings = await getWebServerSettings();
|
||||||
const cronExpression = owner?.user.logCleanupCron ?? null;
|
const cronExpression = settings?.logCleanupCron ?? null;
|
||||||
return {
|
return {
|
||||||
enabled: cronExpression !== null,
|
enabled: cronExpression !== null,
|
||||||
cronExpression,
|
cronExpression,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import path from "node:path";
|
|||||||
import { member } from "@dokploy/server/db/schema";
|
import { member } from "@dokploy/server/db/schema";
|
||||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||||
import { getAllServers } from "@dokploy/server/services/server";
|
import { getAllServers } from "@dokploy/server/services/server";
|
||||||
|
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { scheduleJob } from "node-schedule";
|
import { scheduleJob } from "node-schedule";
|
||||||
import { db } from "../../db/index";
|
import { db } from "../../db/index";
|
||||||
@@ -25,7 +26,9 @@ export const initCronJobs = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (admin?.user?.enableDockerCleanup) {
|
const webServerSettings = await getWebServerSettings();
|
||||||
|
|
||||||
|
if (webServerSettings?.enableDockerCleanup) {
|
||||||
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
|
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
|
||||||
console.log(
|
console.log(
|
||||||
`Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`,
|
`Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`,
|
||||||
@@ -82,9 +85,9 @@ export const initCronJobs = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (admin?.user?.logCleanupCron) {
|
if (webServerSettings?.logCleanupCron) {
|
||||||
console.log("Starting log requests cleanup", admin.user.logCleanupCron);
|
console.log("Starting log requests cleanup", webServerSettings.logCleanupCron);
|
||||||
await startLogCleanup(admin.user.logCleanupCron);
|
await startLogCleanup(webServerSettings.logCleanupCron);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import type { User } from "@dokploy/server/services/user";
|
import type { webServerSettings } from "@dokploy/server/db/schema/web-server-settings";
|
||||||
import { parse, stringify } from "yaml";
|
import { parse, stringify } from "yaml";
|
||||||
import {
|
import {
|
||||||
loadOrCreateConfig,
|
loadOrCreateConfig,
|
||||||
@@ -12,10 +12,10 @@ import type { FileConfig } from "./file-types";
|
|||||||
import type { MainTraefikConfig } from "./types";
|
import type { MainTraefikConfig } from "./types";
|
||||||
|
|
||||||
export const updateServerTraefik = (
|
export const updateServerTraefik = (
|
||||||
user: User | null,
|
settings: typeof webServerSettings.$inferSelect | null,
|
||||||
newHost: string | null,
|
newHost: string | null,
|
||||||
) => {
|
) => {
|
||||||
const { https, certificateType } = user || {};
|
const { https, certificateType } = settings || {};
|
||||||
const appName = "dokploy";
|
const appName = "dokploy";
|
||||||
const config: FileConfig = loadOrCreateConfig(appName);
|
const config: FileConfig = loadOrCreateConfig(appName);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user