mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Compare commits
46 Commits
v0.19.0
...
fix/adjust
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6de744f91 | ||
|
|
b64ddf1119 | ||
|
|
2f074ac734 | ||
|
|
96e3721b4b | ||
|
|
b8e5cae88f | ||
|
|
fa20444a14 | ||
|
|
668ccabec8 | ||
|
|
aa07a0c574 | ||
|
|
0b64b43376 | ||
|
|
5c65dc9a21 | ||
|
|
58262606d4 | ||
|
|
f73959db41 | ||
|
|
e6c664e65f | ||
|
|
36cc157566 | ||
|
|
7e070623cc | ||
|
|
b2c0a685f8 | ||
|
|
c14528886d | ||
|
|
29eb490e2d | ||
|
|
6166963b00 | ||
|
|
f544efed35 | ||
|
|
598d095241 | ||
|
|
457a8e05fd | ||
|
|
3ca057c44a | ||
|
|
ad3a0198e9 | ||
|
|
ab5f62604c | ||
|
|
bf9e886b9a | ||
|
|
f5cd0fbdd8 | ||
|
|
8859cc97b4 | ||
|
|
3bdd5e4dd0 | ||
|
|
b0c710aa92 | ||
|
|
c83d0a95b7 | ||
|
|
71ca5babfd | ||
|
|
f342613503 | ||
|
|
efd176451f | ||
|
|
a7fd64e019 | ||
|
|
21c8b98f9c | ||
|
|
6ff06576d0 | ||
|
|
24cc08a1ac | ||
|
|
e039826d50 | ||
|
|
a947b4915a | ||
|
|
fc1d9ad202 | ||
|
|
5489e3b0a5 | ||
|
|
e43b907a8d | ||
|
|
62fae661a1 | ||
|
|
6846e0e5a3 | ||
|
|
49d4cea06f |
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: Create a bug report
|
||||
labels: ["bug"]
|
||||
labels: ["needs-triage🔍"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -62,6 +62,7 @@ body:
|
||||
- "Docker"
|
||||
- "Remote server"
|
||||
- "Local Development"
|
||||
- "Cloud Version"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
@@ -138,11 +138,18 @@ curl -sSL https://nixpacks.com/install.sh -o install.sh \
|
||||
&& ./install.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
# Install Railpack
|
||||
curl -sSL https://railpack.com/install.sh | sh
|
||||
```
|
||||
|
||||
```bash
|
||||
# Install Buildpacks
|
||||
curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.32.1/pack-v0.32.1-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Pull Request
|
||||
|
||||
- The `main` branch is the source of truth and should always reflect the latest stable release.
|
||||
|
||||
@@ -55,6 +55,10 @@ RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \
|
||||
&& ./install.sh \
|
||||
&& pnpm install -g tsx
|
||||
|
||||
# Install Railpack
|
||||
ARG RAILPACK_VERSION=0.0.37
|
||||
RUN curl -sSL https://railpack.com/install.sh | bash
|
||||
|
||||
# Install buildpacks
|
||||
COPY --from=buildpacksio/pack:0.35.0 /usr/local/bin/pack /usr/local/bin/pack
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
@@ -25,6 +26,7 @@ enum BuildType {
|
||||
paketo_buildpacks = "paketo_buildpacks",
|
||||
nixpacks = "nixpacks",
|
||||
static = "static",
|
||||
railpack = "railpack",
|
||||
}
|
||||
|
||||
const mySchema = z.discriminatedUnion("buildType", [
|
||||
@@ -53,6 +55,9 @@ const mySchema = z.discriminatedUnion("buildType", [
|
||||
z.object({
|
||||
buildType: z.literal("static"),
|
||||
}),
|
||||
z.object({
|
||||
buildType: z.literal("railpack"),
|
||||
}),
|
||||
]);
|
||||
|
||||
type AddTemplate = z.infer<typeof mySchema>;
|
||||
@@ -173,6 +178,15 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
|
||||
Dockerfile
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="railpack" />
|
||||
</FormControl>
|
||||
<FormLabel className="font-normal">
|
||||
Railpack{" "}
|
||||
<Badge className="ml-1 text-xs px-1">New</Badge>
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
<FormItem className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value="nixpacks" />
|
||||
|
||||
@@ -200,7 +200,7 @@ export const ContainerFreeMonitoring = ({
|
||||
}, [appName]);
|
||||
|
||||
return (
|
||||
<div className="rounded-xl bg-background shadow-md flex flex-col gap-4">
|
||||
<div className="rounded-xl bg-background flex flex-col gap-4">
|
||||
<header className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Monitoring</h1>
|
||||
|
||||
@@ -383,9 +383,8 @@ export const AddTemplate = ({ projectId }: Props) => {
|
||||
side="top"
|
||||
>
|
||||
<span>
|
||||
If ot server is selected, the application
|
||||
will be deployed on the server where the
|
||||
user is logged in.
|
||||
If no server is selected, the application will be
|
||||
deployed on the server where the user is logged in.
|
||||
</span>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
@@ -39,12 +39,12 @@ import { S3_PROVIDERS } from "./constants";
|
||||
|
||||
const addDestination = z.object({
|
||||
name: z.string().min(1, "Name is required"),
|
||||
provider: z.string().optional(),
|
||||
accessKeyId: z.string(),
|
||||
secretAccessKey: z.string(),
|
||||
bucket: z.string(),
|
||||
provider: z.string().min(1, "Provider is required"),
|
||||
accessKeyId: z.string().min(1, "Access Key Id is required"),
|
||||
secretAccessKey: z.string().min(1, "Secret Access Key is required"),
|
||||
bucket: z.string().min(1, "Bucket is required"),
|
||||
region: z.string(),
|
||||
endpoint: z.string(),
|
||||
endpoint: z.string().min(1, "Endpoint is required"),
|
||||
serverId: z.string().optional(),
|
||||
});
|
||||
|
||||
@@ -129,6 +129,63 @@ export const HandleDestinations = ({ destinationId }: Props) => {
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const handleTestConnection = async (serverId?: string) => {
|
||||
const result = await form.trigger([
|
||||
"provider",
|
||||
"accessKeyId",
|
||||
"secretAccessKey",
|
||||
"bucket",
|
||||
"endpoint",
|
||||
]);
|
||||
|
||||
if (!result) {
|
||||
const errors = form.formState.errors;
|
||||
const errorFields = Object.entries(errors)
|
||||
.map(([field, error]) => `${field}: ${error?.message}`)
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
|
||||
toast.error("Please fill all required fields", {
|
||||
description: errorFields,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCloud && !serverId) {
|
||||
toast.error("Please select a server");
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = form.getValues("provider");
|
||||
const accessKey = form.getValues("accessKeyId");
|
||||
const secretKey = form.getValues("secretAccessKey");
|
||||
const bucket = form.getValues("bucket");
|
||||
const endpoint = form.getValues("endpoint");
|
||||
const region = form.getValues("region");
|
||||
|
||||
const connectionString = `:s3,provider=${provider},access_key_id=${accessKey},secret_access_key=${secretKey},endpoint=${endpoint}${region ? `,region=${region}` : ""}:${bucket}`;
|
||||
|
||||
await testConnection({
|
||||
provider,
|
||||
accessKey,
|
||||
bucket,
|
||||
endpoint,
|
||||
name: "Test",
|
||||
region,
|
||||
secretAccessKey: secretKey,
|
||||
serverId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Connection Success");
|
||||
})
|
||||
.catch((e) => {
|
||||
toast.error("Error connecting to provider", {
|
||||
description: `${e.message}\n\nTry manually: rclone ls ${connectionString}`,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger className="" asChild>
|
||||
@@ -349,26 +406,9 @@ export const HandleDestinations = ({ destinationId }: Props) => {
|
||||
<Button
|
||||
type="button"
|
||||
variant={"secondary"}
|
||||
isLoading={isLoading}
|
||||
isLoading={isLoadingConnection}
|
||||
onClick={async () => {
|
||||
await testConnection({
|
||||
provider: form.getValues("provider") || "",
|
||||
accessKey: form.getValues("accessKeyId"),
|
||||
bucket: form.getValues("bucket"),
|
||||
endpoint: form.getValues("endpoint"),
|
||||
name: "Test",
|
||||
region: form.getValues("region"),
|
||||
secretAccessKey: form.getValues("secretAccessKey"),
|
||||
serverId: form.getValues("serverId"),
|
||||
})
|
||||
.then(async () => {
|
||||
toast.success("Connection Success");
|
||||
})
|
||||
.catch((e) => {
|
||||
toast.error("Error connecting the provider", {
|
||||
description: e.message,
|
||||
});
|
||||
});
|
||||
await handleTestConnection(form.getValues("serverId"));
|
||||
}}
|
||||
>
|
||||
Test Connection
|
||||
@@ -380,21 +420,7 @@ export const HandleDestinations = ({ destinationId }: Props) => {
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={async () => {
|
||||
await testConnection({
|
||||
provider: form.getValues("provider") || "",
|
||||
accessKey: form.getValues("accessKeyId"),
|
||||
bucket: form.getValues("bucket"),
|
||||
endpoint: form.getValues("endpoint"),
|
||||
name: "Test",
|
||||
region: form.getValues("region"),
|
||||
secretAccessKey: form.getValues("secretAccessKey"),
|
||||
})
|
||||
.then(async () => {
|
||||
toast.success("Connection Success");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error connecting the provider");
|
||||
});
|
||||
await handleTestConnection();
|
||||
}}
|
||||
>
|
||||
Test connection
|
||||
|
||||
@@ -56,9 +56,17 @@ export const ShowDestinations = () => {
|
||||
className="flex items-center justify-between bg-sidebar p-1 w-full rounded-lg"
|
||||
>
|
||||
<div className="flex items-center justify-between p-3.5 rounded-lg bg-background border w-full">
|
||||
<span className="text-sm">
|
||||
{index + 1}. {destination.name}
|
||||
</span>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-sm">
|
||||
{index + 1}. {destination.name}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Created at:{" "}
|
||||
{new Date(
|
||||
destination.createdAt,
|
||||
).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-row gap-1">
|
||||
<HandleDestinations
|
||||
destinationId={destination.destinationId}
|
||||
|
||||
@@ -45,21 +45,12 @@ const Schema = z.object({
|
||||
|
||||
type Schema = z.infer<typeof Schema>;
|
||||
|
||||
interface Model {
|
||||
id: string;
|
||||
object: string;
|
||||
created: number;
|
||||
owned_by: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
aiId?: string;
|
||||
}
|
||||
|
||||
export const HandleAi = ({ aiId }: Props) => {
|
||||
const [models, setModels] = useState<Model[]>([]);
|
||||
const utils = api.useUtils();
|
||||
const [isLoadingModels, setIsLoadingModels] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const { data, refetch } = api.ai.one.useQuery(
|
||||
@@ -73,6 +64,7 @@ export const HandleAi = ({ aiId }: Props) => {
|
||||
const { mutateAsync, isLoading } = aiId
|
||||
? api.ai.update.useMutation()
|
||||
: api.ai.create.useMutation();
|
||||
|
||||
const form = useForm<Schema>({
|
||||
resolver: zodResolver(Schema),
|
||||
defaultValues: {
|
||||
@@ -94,50 +86,33 @@ export const HandleAi = ({ aiId }: Props) => {
|
||||
});
|
||||
}, [aiId, form, data]);
|
||||
|
||||
const fetchModels = async (apiUrl: string, apiKey: string) => {
|
||||
setIsLoadingModels(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/models`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch models");
|
||||
}
|
||||
const res = await response.json();
|
||||
setModels(res.data);
|
||||
const apiUrl = form.watch("apiUrl");
|
||||
const apiKey = form.watch("apiKey");
|
||||
|
||||
// Set default model to gpt-4 if present
|
||||
const defaultModel = res.data.find(
|
||||
(model: Model) => model.id === "gpt-4",
|
||||
);
|
||||
if (defaultModel) {
|
||||
form.setValue("model", defaultModel.id);
|
||||
return defaultModel.id;
|
||||
}
|
||||
} catch (error) {
|
||||
setError("Failed to fetch models. Please check your API URL and Key.");
|
||||
setModels([]);
|
||||
} finally {
|
||||
setIsLoadingModels(false);
|
||||
}
|
||||
};
|
||||
const { data: models, isLoading: isLoadingServerModels } =
|
||||
api.ai.getModels.useQuery(
|
||||
{
|
||||
apiUrl: apiUrl ?? "",
|
||||
apiKey: apiKey ?? "",
|
||||
},
|
||||
{
|
||||
enabled: !!apiUrl && !!apiKey,
|
||||
onError: (error) => {
|
||||
setError(`Failed to fetch models: ${error.message}`);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const apiUrl = form.watch("apiUrl");
|
||||
const apiKey = form.watch("apiKey");
|
||||
if (apiUrl && apiKey) {
|
||||
form.setValue("model", "");
|
||||
fetchModels(apiUrl, apiKey);
|
||||
}
|
||||
}, [form.watch("apiUrl"), form.watch("apiKey")]);
|
||||
|
||||
const onSubmit = async (data: Schema) => {
|
||||
try {
|
||||
console.log("Form data:", data);
|
||||
console.log("Current model value:", form.getValues("model"));
|
||||
await mutateAsync({
|
||||
...data,
|
||||
aiId: aiId || "",
|
||||
@@ -148,8 +123,9 @@ export const HandleAi = ({ aiId }: Props) => {
|
||||
refetch();
|
||||
setOpen(false);
|
||||
} catch (error) {
|
||||
console.error("Submit error:", error);
|
||||
toast.error("Failed to save AI settings");
|
||||
toast.error("Failed to save AI settings", {
|
||||
description: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -232,13 +208,13 @@ export const HandleAi = ({ aiId }: Props) => {
|
||||
)}
|
||||
/>
|
||||
|
||||
{isLoadingModels && (
|
||||
{isLoadingServerModels && (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Loading models...
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!isLoadingModels && models.length > 0 && (
|
||||
{!isLoadingServerModels && models && models.length > 0 && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="model"
|
||||
|
||||
@@ -64,12 +64,12 @@ export const Enable2FA = () => {
|
||||
const handlePasswordSubmit = async (formData: PasswordForm) => {
|
||||
setIsPasswordLoading(true);
|
||||
try {
|
||||
const { data: enableData } = await authClient.twoFactor.enable({
|
||||
const { data: enableData, error } = await authClient.twoFactor.enable({
|
||||
password: formData.password,
|
||||
});
|
||||
|
||||
if (!enableData) {
|
||||
throw new Error("No data received from server");
|
||||
throw new Error(error?.message || "Unknown error");
|
||||
}
|
||||
|
||||
if (enableData.backupCodes) {
|
||||
@@ -95,7 +95,7 @@ export const Enable2FA = () => {
|
||||
error instanceof Error ? error.message : "Error setting up 2FA",
|
||||
);
|
||||
passwordForm.setError("password", {
|
||||
message: "Error verifying password",
|
||||
message: `Error verifying password: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
});
|
||||
} finally {
|
||||
setIsPasswordLoading(false);
|
||||
|
||||
@@ -139,6 +139,15 @@ export const ValidateServer = ({ serverId }: Props) => {
|
||||
: "Not Created"
|
||||
}
|
||||
/>
|
||||
<StatusRow
|
||||
label="Railpack Installed"
|
||||
isEnabled={data?.railpack?.enabled}
|
||||
description={
|
||||
data?.railpack?.enabled
|
||||
? `Installed: ${data?.railpack?.version}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1055,10 +1055,6 @@ export default function Page({ children }: Props) {
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator className="block" />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{activeItem?.title}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
|
||||
@@ -72,6 +72,14 @@ export const UserNav = () => {
|
||||
</div>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
router.push("/dashboard/settings/profile");
|
||||
}}
|
||||
>
|
||||
Profile
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
@@ -126,14 +134,6 @@ export const UserNav = () => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
router.push("/dashboard/settings/profile");
|
||||
}}
|
||||
>
|
||||
Profile
|
||||
</DropdownMenuItem>
|
||||
{data?.role === "owner" && (
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
|
||||
@@ -37,7 +37,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => {
|
||||
)}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator className="block" />
|
||||
{_index + 1 < list.length && <BreadcrumbSeparator className="block" />}
|
||||
</Fragment>
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
|
||||
1
apps/dokploy/drizzle/0069_legal_bill_hollister.sql
Normal file
1
apps/dokploy/drizzle/0069_legal_bill_hollister.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TYPE "public"."buildType" ADD VALUE 'railpack';
|
||||
1
apps/dokploy/drizzle/0070_useful_serpent_society.sql
Normal file
1
apps/dokploy/drizzle/0070_useful_serpent_society.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "destination" ADD COLUMN "createdAt" timestamp DEFAULT now() NOT NULL;
|
||||
5119
apps/dokploy/drizzle/meta/0069_snapshot.json
Normal file
5119
apps/dokploy/drizzle/meta/0069_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
5126
apps/dokploy/drizzle/meta/0070_snapshot.json
Normal file
5126
apps/dokploy/drizzle/meta/0070_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -484,6 +484,20 @@
|
||||
"when": 1740897756774,
|
||||
"tag": "0068_complex_rhino",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 69,
|
||||
"version": "7",
|
||||
"when": 1741152916611,
|
||||
"tag": "0069_legal_bill_hollister",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 70,
|
||||
"version": "7",
|
||||
"when": 1741322697251,
|
||||
"tag": "0070_useful_serpent_society",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dokploy",
|
||||
"version": "v0.19.0",
|
||||
"version": "v0.19.1",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
|
||||
@@ -228,15 +228,15 @@ const Service = (
|
||||
>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="environment">Environment</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="deployments">Deployments</TabsTrigger>
|
||||
<TabsTrigger value="domains">Domains</TabsTrigger>
|
||||
<TabsTrigger value="preview-deployments">
|
||||
Preview Deployments
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="domains">Domains</TabsTrigger>
|
||||
<TabsTrigger value="deployments">Deployments</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -224,12 +224,12 @@ const Service = (
|
||||
>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="environment">Environment</TabsTrigger>
|
||||
<TabsTrigger value="domains">Domains</TabsTrigger>
|
||||
<TabsTrigger value="deployments">Deployments</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="deployments">Deployments</TabsTrigger>
|
||||
<TabsTrigger value="domains">Domains</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -197,11 +197,11 @@ const Mariadb = (
|
||||
>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="environment">Environment</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -198,11 +198,11 @@ const Mongo = (
|
||||
>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="environment">Environment</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -200,13 +200,13 @@ const MySql = (
|
||||
<TabsTrigger value="environment">
|
||||
Environment
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">
|
||||
Monitoring
|
||||
</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -197,11 +197,11 @@ const Postgresql = (
|
||||
>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="environment">Environment</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -197,10 +197,10 @@ const Redis = (
|
||||
>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="environment">Environment</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
{((data?.serverId && isCloud) || !data?.server) && (
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
)}
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
28
apps/dokploy/public/templates/datalens.svg
Normal file
28
apps/dokploy/public/templates/datalens.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="163" height="32" fill="none" viewBox="0 0 163 32">
|
||||
<g clip-path="url(#a)">
|
||||
<rect width="32" height="32" fill="#FF6928" rx="16" />
|
||||
<path fill="#fff" fill-rule="evenodd" d="M7.556 29.778C-3 29.778-11.556 23.609-11.556 16c0-7.61 8.557-13.778 19.112-13.778 11.17 0 19.11 5.905 19.11 13.778s-7.94 13.778-19.11 13.778Zm0-8c-10.17 0-17.334-2.587-17.334-5.778 0-3.191 7.163-5.778 17.334-5.778 10.17 0 17.333 2.128 17.333 5.778 0 3.65-7.163 5.778-17.333 5.778Z" clip-rule="evenodd" />
|
||||
<g filter="url(#b)" opacity=".3">
|
||||
<path fill="#FF6928" d="M1.893 11.25C4.192 6.285 5.753 3.667 10.495.177L7.9-5.684-5.308-2.17l-.438 15.1 7.639-1.678Z" />
|
||||
</g>
|
||||
<g filter="url(#c)" opacity=".3">
|
||||
<path fill="#FF6928" d="M12.434 29.6c-4.607-2.955-6.988-4.86-9.798-10.033l-6.16 1.773 1.679 13.563 14.898 2.493-.62-7.796Z" />
|
||||
</g>
|
||||
</g>
|
||||
<path fill="currentColor" d="M46.18 22.416c4.02 0 6.03-2.171 6.03-6.514v-.227c0-2.143-.493-3.745-1.48-4.807-.966-1.081-2.502-1.622-4.607-1.622h-1.82v13.17h1.877ZM39.694 5.662h6.656c3.47 0 6.144.892 8.022 2.674 1.763 1.764 2.645 4.19 2.645 7.282v.227c0 3.13-.891 5.585-2.674 7.367C52.485 25.071 49.811 26 46.322 26h-6.628V5.662Zm23.788 20.65c-1.441 0-2.608-.35-3.499-1.052-.986-.777-1.479-1.905-1.479-3.384 0-1.65.72-2.883 2.162-3.698 1.29-.72 3.148-1.081 5.575-1.081h1.678V16.5c0-.949-.18-1.64-.54-2.077-.342-.436-.968-.654-1.878-.654-1.46 0-2.304.701-2.531 2.105h-3.897c.114-1.669.806-2.958 2.076-3.869 1.157-.815 2.693-1.223 4.608-1.223 1.916 0 3.414.417 4.494 1.252 1.157.91 1.736 2.332 1.736 4.266V26h-4.011v-1.792c-1.005 1.403-2.503 2.105-4.494 2.105Zm1.223-2.872c.93 0 1.697-.237 2.304-.711.607-.474.91-1.119.91-1.934v-1.252h-1.593c-1.251 0-2.2.161-2.844.484-.626.322-.939.863-.939 1.621 0 1.195.72 1.792 2.162 1.792Zm15.734 2.844c-3.204 0-4.807-1.564-4.807-4.693v-7.538h-1.905v-2.93h1.905V7.91h4.096v3.215h3.13v2.93h-3.13v7.167c0 1.176.55 1.764 1.65 1.764.607 0 1.129-.095 1.565-.285v3.186c-.759.266-1.593.398-2.504.398Zm8.92.029c-1.442 0-2.608-.35-3.5-1.053-.985-.777-1.478-1.905-1.478-3.384 0-1.65.72-2.883 2.161-3.698 1.29-.72 3.148-1.081 5.576-1.081h1.678V16.5c0-.949-.18-1.64-.54-2.077-.342-.436-.968-.654-1.878-.654-1.46 0-2.304.701-2.532 2.105H84.95c.114-1.669.806-2.958 2.077-3.869 1.157-.815 2.693-1.223 4.608-1.223 1.915 0 3.413.417 4.494 1.252 1.157.91 1.735 2.332 1.735 4.266V26h-4.01v-1.792c-1.005 1.403-2.503 2.105-4.495 2.105Zm1.223-2.873c.929 0 1.697-.237 2.303-.711.607-.474.91-1.119.91-1.934v-1.252h-1.592c-1.252 0-2.2.161-2.845.484-.625.322-.938.863-.938 1.621 0 1.195.72 1.792 2.162 1.792Zm10.671-17.778h4.608v16.726h8.277V26h-12.885V5.662Zm21.632 20.65c-2.313 0-4.162-.672-5.547-2.019-1.479-1.365-2.218-3.214-2.218-5.546v-.228c0-2.313.739-4.19 2.218-5.632 1.442-1.403 3.252-2.105 5.433-2.105 2.067 0 3.755.598 5.063 1.792 1.46 1.328 2.191 3.252 2.191 5.775v1.137h-10.724c.057 1.252.398 2.219 1.024 2.902.645.663 1.536.995 2.674.995 1.782 0 2.816-.692 3.1-2.076h3.897c-.246 1.612-.986 2.854-2.219 3.726-1.213.853-2.844 1.28-4.892 1.28Zm3.129-9.357c-.133-2.219-1.214-3.328-3.243-3.328-.929 0-1.697.294-2.304.881-.588.57-.957 1.385-1.109 2.447h6.656Zm6.19-5.831h4.125v2.36c.929-1.801 2.541-2.702 4.835-2.702 1.498 0 2.703.455 3.613 1.366.929.986 1.394 2.446 1.394 4.38V26h-4.125v-8.875c0-1.024-.209-1.773-.626-2.247-.417-.493-1.081-.74-1.991-.74-.929 0-1.678.285-2.247.854-.569.55-.853 1.356-.853 2.418V26h-4.125V11.124Zm22.385 15.189c-2.01 0-3.575-.427-4.694-1.28-1.118-.853-1.716-2.086-1.792-3.698h3.84c.114.72.361 1.252.74 1.593.379.341 1.005.512 1.877.512 1.555 0 2.333-.54 2.333-1.621 0-.512-.228-.892-.683-1.138-.455-.266-1.233-.474-2.332-.626-2.01-.322-3.414-.806-4.21-1.45-.853-.664-1.28-1.726-1.28-3.186s.607-2.627 1.82-3.499c1.043-.758 2.399-1.138 4.068-1.138 1.725 0 3.119.342 4.181 1.024 1.119.759 1.773 1.953 1.963 3.584h-3.783c-.114-.626-.351-1.08-.711-1.365-.361-.284-.901-.427-1.622-.427-.663 0-1.185.143-1.564.427a1.35 1.35 0 0 0-.541 1.11c0 .473.2.824.598 1.052.417.227 1.175.417 2.275.569 1.953.265 3.385.71 4.295 1.337.986.739 1.48 1.848 1.48 3.328 0 1.592-.55 2.806-1.65 3.64-1.081.835-2.617 1.252-4.608 1.252Z" />
|
||||
<defs>
|
||||
<filter id="b" width="37.575" height="39.945" x="-16.413" y="-16.35" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur result="effect1_foregroundBlur_219_14417" stdDeviation="5.333" />
|
||||
</filter>
|
||||
<filter id="c" width="37.91" height="39.162" x="-14.19" y="8.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur result="effect1_foregroundBlur_219_14417" stdDeviation="5.333" />
|
||||
</filter>
|
||||
<clipPath id="a">
|
||||
<rect width="32" height="32" fill="#fff" rx="16" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
7
apps/dokploy/public/templates/hoarder.svg
Normal file
7
apps/dokploy/public/templates/hoarder.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 355 354" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-232,-118)">
|
||||
<path d="M565.33,118.79L253.02,118.79C241.46,118.79 232.09,128.16 232.09,139.72L232.09,450.44C232.09,462 241.46,471.37 253.02,471.37L565.33,471.37C576.89,471.37 586.26,462 586.26,450.44L586.26,139.72C586.26,128.16 576.89,118.79 565.33,118.79ZM386.85,419.57C386.85,422.58 384.4,425.03 381.39,425.03L285.11,425.03C282.1,425.03 279.65,422.58 279.65,419.57L279.65,169.43C279.65,166.42 282.1,163.96 285.11,163.96L379.87,163.96C382.88,163.96 385.33,166.41 385.33,169.43L385.33,264.64C385.33,264.64 384.81,305.61 386.85,337.76L386.85,419.58L386.85,419.57ZM537.19,419.57C537.19,423.92 532.36,426.52 528.75,424.14L484.85,395.44C482.95,394.18 480.5,394.25 478.64,395.59L440.16,423.4C438.56,424.59 436.67,424.66 435.07,424.07C433.66,423.07 432.73,421.43 432.73,419.57L432.73,225.34C437.94,224.34 443.73,223.78 450.43,223.78C483.29,223.78 537.2,242.37 537.2,294.78L537.2,419.58L537.19,419.57Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -25,6 +25,10 @@ import {
|
||||
addNewService,
|
||||
checkServiceAccess,
|
||||
} from "@dokploy/server/services/user";
|
||||
import {
|
||||
getProviderHeaders,
|
||||
type Model,
|
||||
} from "@dokploy/server/utils/ai/select-ai-provider";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -41,6 +45,58 @@ export const aiRouter = createTRPCRouter({
|
||||
}
|
||||
return aiSetting;
|
||||
}),
|
||||
|
||||
getModels: protectedProcedure
|
||||
.input(z.object({ apiUrl: z.string().min(1), apiKey: z.string().min(1) }))
|
||||
.query(async ({ input }) => {
|
||||
try {
|
||||
const headers = getProviderHeaders(input.apiUrl, input.apiKey);
|
||||
const response = await fetch(`${input.apiUrl}/models`, { headers });
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Failed to fetch models: ${errorText}`);
|
||||
}
|
||||
|
||||
const res = await response.json();
|
||||
|
||||
if (Array.isArray(res)) {
|
||||
return res.map((model) => ({
|
||||
id: model.id || model.name,
|
||||
object: "model",
|
||||
created: Date.now(),
|
||||
owned_by: "provider",
|
||||
}));
|
||||
}
|
||||
|
||||
if (res.models) {
|
||||
return res.models.map((model: any) => ({
|
||||
id: model.id || model.name,
|
||||
object: "model",
|
||||
created: Date.now(),
|
||||
owned_by: "provider",
|
||||
})) as Model[];
|
||||
}
|
||||
|
||||
if (res.data) {
|
||||
return res.data as Model[];
|
||||
}
|
||||
|
||||
const possibleModels =
|
||||
(Object.values(res).find(Array.isArray) as any[]) || [];
|
||||
return possibleModels.map((model) => ({
|
||||
id: model.id || model.name,
|
||||
object: "model",
|
||||
created: Date.now(),
|
||||
owned_by: "provider",
|
||||
})) as Model[];
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: error instanceof Error ? error?.message : `Error: ${error}`,
|
||||
});
|
||||
}
|
||||
}),
|
||||
create: adminProcedure.input(apiCreateAi).mutation(async ({ ctx, input }) => {
|
||||
return await saveAiSettings(ctx.session.activeOrganizationId, input);
|
||||
}),
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
updateDestinationById,
|
||||
} from "@dokploy/server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { eq, desc } from "drizzle-orm";
|
||||
|
||||
export const destinationRouter = createTRPCRouter({
|
||||
create: adminProcedure
|
||||
@@ -98,6 +98,7 @@ export const destinationRouter = createTRPCRouter({
|
||||
all: protectedProcedure.query(async ({ ctx }) => {
|
||||
return await db.query.destinations.findMany({
|
||||
where: eq(destinations.organizationId, ctx.session.activeOrganizationId),
|
||||
orderBy: [desc(destinations.createdAt)],
|
||||
});
|
||||
}),
|
||||
remove: adminProcedure
|
||||
|
||||
@@ -206,6 +206,10 @@ export const serverRouter = createTRPCRouter({
|
||||
enabled: boolean;
|
||||
version: string;
|
||||
};
|
||||
railpack: {
|
||||
enabled: boolean;
|
||||
version: string;
|
||||
};
|
||||
isDokployNetworkInstalled: boolean;
|
||||
isSwarmInstalled: boolean;
|
||||
isMainDirectoryInstalled: boolean;
|
||||
|
||||
96
apps/dokploy/templates/datalens/docker-compose.yml
Normal file
96
apps/dokploy/templates/datalens/docker-compose.yml
Normal file
@@ -0,0 +1,96 @@
|
||||
services:
|
||||
pg-compeng:
|
||||
image: postgres:16-alpine
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_PASSWORD: "postgres"
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
|
||||
control-api:
|
||||
image: ghcr.io/datalens-tech/datalens-control-api:0.2192.0
|
||||
restart: always
|
||||
environment:
|
||||
BI_API_UWSGI_WORKERS_COUNT: 4
|
||||
CONNECTOR_AVAILABILITY_VISIBLE: "clickhouse,postgres,chyt,ydb,mysql,greenplum,mssql,appmetrica_api,metrika_api"
|
||||
RQE_FORCE_OFF: 1
|
||||
DL_CRY_ACTUAL_KEY_ID: key_1
|
||||
DL_CRY_KEY_VAL_ID_key_1: "h1ZpilcYLYRdWp7Nk8X1M1kBPiUi8rdjz9oBfHyUKIk="
|
||||
RQE_SECRET_KEY: ""
|
||||
US_HOST: "http://us:8083"
|
||||
US_MASTER_TOKEN: "fake-us-master-token"
|
||||
depends_on:
|
||||
- us
|
||||
|
||||
data-api:
|
||||
container_name: datalens-data-api
|
||||
image: ghcr.io/datalens-tech/datalens-data-api:0.2192.0
|
||||
restart: always
|
||||
environment:
|
||||
GUNICORN_WORKERS_COUNT: 5
|
||||
RQE_FORCE_OFF: 1
|
||||
CACHES_ON: 0
|
||||
MUTATIONS_CACHES_ON: 0
|
||||
RQE_SECRET_KEY: ""
|
||||
DL_CRY_ACTUAL_KEY_ID: key_1
|
||||
DL_CRY_KEY_VAL_ID_key_1: "h1ZpilcYLYRdWp7Nk8X1M1kBPiUi8rdjz9oBfHyUKIk="
|
||||
BI_COMPENG_PG_ON: 1
|
||||
BI_COMPENG_PG_URL: "postgresql://postgres:postgres@pg-compeng:5432/postgres"
|
||||
US_HOST: "http://us:8083"
|
||||
US_MASTER_TOKEN: "fake-us-master-token"
|
||||
depends_on:
|
||||
- us
|
||||
- pg-compeng
|
||||
|
||||
pg-us:
|
||||
container_name: datalens-pg-us
|
||||
image: postgres:16-alpine
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_DB: us-db-ci_purgeable
|
||||
POSTGRES_USER: us
|
||||
POSTGRES_PASSWORD: us
|
||||
volumes:
|
||||
- ${VOLUME_US:-./metadata}:/var/lib/postgresql/data
|
||||
|
||||
us:
|
||||
image: ghcr.io/datalens-tech/datalens-us:0.310.0
|
||||
restart: always
|
||||
depends_on:
|
||||
- pg-us
|
||||
environment:
|
||||
APP_INSTALLATION: "opensource"
|
||||
APP_ENV: "prod"
|
||||
MASTER_TOKEN: "fake-us-master-token"
|
||||
POSTGRES_DSN_LIST: ${METADATA_POSTGRES_DSN_LIST:-postgres://us:us@pg-us:5432/us-db-ci_purgeable}
|
||||
SKIP_INSTALL_DB_EXTENSIONS: ${METADATA_SKIP_INSTALL_DB_EXTENSIONS:-0}
|
||||
USE_DEMO_DATA: ${USE_DEMO_DATA:-0}
|
||||
HC: ${HC:-0}
|
||||
NODE_EXTRA_CA_CERTS: /certs/root.crt
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
volumes:
|
||||
- ./certs:/certs
|
||||
|
||||
datalens:
|
||||
image: ghcr.io/datalens-tech/datalens-ui:0.2601.0
|
||||
restart: always
|
||||
ports:
|
||||
- ${UI_PORT:-8080}:8080
|
||||
depends_on:
|
||||
- us
|
||||
- control-api
|
||||
- data-api
|
||||
environment:
|
||||
APP_MODE: "full"
|
||||
APP_ENV: "production"
|
||||
APP_INSTALLATION: "opensource"
|
||||
AUTH_POLICY: "disabled"
|
||||
US_ENDPOINT: "http://us:8083"
|
||||
BI_API_ENDPOINT: "http://control-api:8080"
|
||||
BI_DATA_ENDPOINT: "http://data-api:8080"
|
||||
US_MASTER_TOKEN: "fake-us-master-token"
|
||||
NODE_EXTRA_CA_CERTS: "/usr/local/share/ca-certificates/cert.pem"
|
||||
HC: ${HC:-0}
|
||||
YANDEX_MAP_ENABLED: ${YANDEX_MAP_ENABLED:-0}
|
||||
YANDEX_MAP_TOKEN: ${YANDEX_MAP_TOKEN:-0}
|
||||
23
apps/dokploy/templates/datalens/index.ts
Normal file
23
apps/dokploy/templates/datalens/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 8080,
|
||||
serviceName: "datalens",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = ["HC=1"];
|
||||
|
||||
return {
|
||||
envs,
|
||||
domains,
|
||||
};
|
||||
}
|
||||
45
apps/dokploy/templates/hoarder/docker-compose.yml
Normal file
45
apps/dokploy/templates/hoarder/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
services:
|
||||
web:
|
||||
image: ghcr.io/hoarder-app/hoarder:0.22.0
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- hoarder-data:/data
|
||||
ports:
|
||||
- 3000
|
||||
environment:
|
||||
- DISABLE_SIGNUPS
|
||||
- NEXTAUTH_URL
|
||||
- NEXTAUTH_SECRET
|
||||
- MEILI_ADDR=http://meilisearch:7700
|
||||
- BROWSER_WEB_URL=http://chrome:9222
|
||||
- DATA_DIR=/data
|
||||
chrome:
|
||||
image: gcr.io/zenika-hub/alpine-chrome:124
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --no-sandbox
|
||||
- --disable-gpu
|
||||
- --disable-dev-shm-usage
|
||||
- --remote-debugging-address=0.0.0.0
|
||||
- --remote-debugging-port=9222
|
||||
- --hide-scrollbars
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.6
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MEILI_MASTER_KEY
|
||||
- MEILI_NO_ANALYTICS="true"
|
||||
volumes:
|
||||
- meilisearch-data:/meili_data
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- curl
|
||||
- '-f'
|
||||
- 'http://127.0.0.1:7700/health'
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
volumes:
|
||||
meilisearch-data:
|
||||
hoarder-data:
|
||||
34
apps/dokploy/templates/hoarder/index.ts
Normal file
34
apps/dokploy/templates/hoarder/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateBase64,
|
||||
generatePassword,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainDomain = generateRandomDomain(schema);
|
||||
const postgresPassword = generatePassword();
|
||||
const nextSecret = generateBase64(32);
|
||||
const meiliMasterKey = generateBase64(32);
|
||||
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: mainDomain,
|
||||
port: 3000,
|
||||
serviceName: "web",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
`NEXTAUTH_SECRET=${nextSecret}`,
|
||||
`MEILI_MASTER_KEY=${meiliMasterKey}`,
|
||||
`NEXTAUTH_URL=http://${mainDomain}`,
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
# This is an UNOFFICIAL production docker image build for Superset:
|
||||
# - https://github.com/amancevice/docker-superset
|
||||
|
||||
|
||||
|
||||
# ## SETUP INSTRUCTIONS
|
||||
#
|
||||
# After deploying this image, you will need to run one of the two
|
||||
@@ -10,7 +10,7 @@
|
||||
# $ superset-init # Initialise database only
|
||||
#
|
||||
# You will be prompted to enter the credentials for the admin user.
|
||||
|
||||
|
||||
|
||||
# ## NETWORK INSTRUCTIONS
|
||||
#
|
||||
@@ -66,7 +66,7 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
superset_redis:
|
||||
superset_redis:
|
||||
image: redis
|
||||
restart: always
|
||||
volumes:
|
||||
|
||||
@@ -108,6 +108,20 @@ export const templates: TemplateData[] = [
|
||||
tags: ["monitoring"],
|
||||
load: () => import("./grafana/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "datalens",
|
||||
name: "DataLens",
|
||||
version: "1.23.0",
|
||||
description: "A modern, scalable business intelligence and data visualization system.",
|
||||
logo: "datalens.svg",
|
||||
links: {
|
||||
github: "https://github.com/datalens-tech/datalens",
|
||||
website: "https://datalens.tech/",
|
||||
docs: "https://datalens.tech/docs/",
|
||||
},
|
||||
tags: ["analytics", "self-hosted", "bi", "monitoring"],
|
||||
load: () => import("./datalens/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "directus",
|
||||
name: "Directus",
|
||||
@@ -707,6 +721,21 @@ export const templates: TemplateData[] = [
|
||||
tags: ["self-hosted", "open-source", "manager"],
|
||||
load: () => import("./hi-events/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "hoarder",
|
||||
name: "Hoarder",
|
||||
version: "0.22.0",
|
||||
description:
|
||||
'Hoarder is an open source "Bookmark Everything" app that uses AI for automatically tagging the content you throw at it.',
|
||||
logo: "hoarder.svg",
|
||||
links: {
|
||||
github: "https://github.com/hoarder/hoarder",
|
||||
website: "https://hoarder.app/",
|
||||
docs: "https://docs.hoarder.app/",
|
||||
},
|
||||
tags: ["self-hosted", "bookmarks", "link-sharing"],
|
||||
load: () => import("./hoarder/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "windows",
|
||||
name: "Windows (dockerized)",
|
||||
|
||||
@@ -17,15 +17,17 @@ services:
|
||||
retries: 5
|
||||
|
||||
zipline:
|
||||
image: ghcr.io/diced/zipline:3.7.9
|
||||
image: ghcr.io/diced/zipline:4
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- CORE_RETURN_HTTPS=${ZIPLINE_RETURN_HTTPS}
|
||||
- CORE_SECRET=${ZIPLINE_SECRET}
|
||||
- CORE_HOST=0.0.0.0
|
||||
- CORE_HOSTNAME=0.0.0.0
|
||||
- CORE_PORT=${ZIPLINE_PORT}
|
||||
- CORE_DATABASE_URL=postgres://postgres:postgres@postgres/postgres
|
||||
- DATABASE_URL=postgres://postgres:postgres@postgres/postgres
|
||||
- CORE_LOGGER=${ZIPLINE_LOGGER}
|
||||
- DATASOURCE_TYPE=local
|
||||
- DATASOURCE_LOCAL_DIRECTORY=./uploads
|
||||
volumes:
|
||||
- "../files/uploads:/zipline/uploads"
|
||||
- "../files/public:/zipline/public"
|
||||
|
||||
@@ -42,6 +42,7 @@ export const buildType = pgEnum("buildType", [
|
||||
"paketo_buildpacks",
|
||||
"nixpacks",
|
||||
"static",
|
||||
"railpack",
|
||||
]);
|
||||
|
||||
export interface HealthCheckSwarm {
|
||||
@@ -383,6 +384,7 @@ const createSchema = createInsertSchema(applications, {
|
||||
"paketo_buildpacks",
|
||||
"nixpacks",
|
||||
"static",
|
||||
"railpack",
|
||||
]),
|
||||
herokuVersion: z.string().optional(),
|
||||
publishDirectory: z.string().optional(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
@@ -21,6 +21,7 @@ export const destinations = pgTable("destination", {
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
createdAt: timestamp("createdAt").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const destinationsRelations = relations(
|
||||
|
||||
@@ -14,6 +14,9 @@ const { handler, api } = betterAuth({
|
||||
provider: "pg",
|
||||
schema: schema,
|
||||
}),
|
||||
...(!IS_CLOUD && {
|
||||
baseURL: "http://localhost:3000",
|
||||
}),
|
||||
logger: {
|
||||
disabled: process.env.NODE_ENV === "production",
|
||||
},
|
||||
|
||||
@@ -170,6 +170,9 @@ ${installNixpacks()}
|
||||
|
||||
echo -e "12. Installing Buildpacks"
|
||||
${installBuildpacks()}
|
||||
|
||||
echo -e "13. Installing Railpack"
|
||||
${installRailpack()}
|
||||
`;
|
||||
|
||||
return bashCommand;
|
||||
@@ -573,6 +576,16 @@ const installNixpacks = () => `
|
||||
fi
|
||||
`;
|
||||
|
||||
const installRailpack = () => `
|
||||
if command_exists railpack; then
|
||||
echo "Railpack already installed ✅"
|
||||
else
|
||||
export RAILPACK_VERSION=0.0.37
|
||||
bash -c "$(curl -fsSL https://railpack.com/install.sh)"
|
||||
echo "Railpack version $RAILPACK_VERSION installed ✅"
|
||||
fi
|
||||
`;
|
||||
|
||||
const installBuildpacks = () => `
|
||||
SUFFIX=""
|
||||
if [ "$SYS_ARCH" = "aarch64" ] || [ "$SYS_ARCH" = "arm64" ]; then
|
||||
|
||||
@@ -38,6 +38,18 @@ export const validateNixpacks = () => `
|
||||
fi
|
||||
`;
|
||||
|
||||
export const validateRailpack = () => `
|
||||
if command_exists railpack; then
|
||||
version=$(railpack --version | awk '{print $3}')
|
||||
if [ -n "$version" ]; then
|
||||
echo "$version true"
|
||||
else
|
||||
echo "0.0.0 false"
|
||||
fi
|
||||
else
|
||||
echo "0.0.0 false"
|
||||
fi
|
||||
`;
|
||||
export const validateBuildpacks = () => `
|
||||
if command_exists pack; then
|
||||
version=$(pack --version | awk '{print $1}')
|
||||
@@ -86,7 +98,7 @@ export const serverValidate = async (serverId: string) => {
|
||||
rcloneVersionEnabled=$(${validateRClone()})
|
||||
nixpacksVersionEnabled=$(${validateNixpacks()})
|
||||
buildpacksVersionEnabled=$(${validateBuildpacks()})
|
||||
|
||||
railpackVersionEnabled=$(${validateRailpack()})
|
||||
dockerVersion=$(echo $dockerVersionEnabled | awk '{print $1}')
|
||||
dockerEnabled=$(echo $dockerVersionEnabled | awk '{print $2}')
|
||||
|
||||
@@ -96,6 +108,9 @@ export const serverValidate = async (serverId: string) => {
|
||||
nixpacksVersion=$(echo $nixpacksVersionEnabled | awk '{print $1}')
|
||||
nixpacksEnabled=$(echo $nixpacksVersionEnabled | awk '{print $2}')
|
||||
|
||||
railpackVersion=$(echo $railpackVersionEnabled | awk '{print $1}')
|
||||
railpackEnabled=$(echo $railpackVersionEnabled | awk '{print $2}')
|
||||
|
||||
buildpacksVersion=$(echo $buildpacksVersionEnabled | awk '{print $1}')
|
||||
buildpacksEnabled=$(echo $buildpacksVersionEnabled | awk '{print $2}')
|
||||
|
||||
@@ -103,7 +118,7 @@ export const serverValidate = async (serverId: string) => {
|
||||
isSwarmInstalled=$(${validateSwarm()})
|
||||
isMainDirectoryInstalled=$(${validateMainDirectory()})
|
||||
|
||||
echo "{\\"docker\\": {\\"version\\": \\"$dockerVersion\\", \\"enabled\\": $dockerEnabled}, \\"rclone\\": {\\"version\\": \\"$rcloneVersion\\", \\"enabled\\": $rcloneEnabled}, \\"nixpacks\\": {\\"version\\": \\"$nixpacksVersion\\", \\"enabled\\": $nixpacksEnabled}, \\"buildpacks\\": {\\"version\\": \\"$buildpacksVersion\\", \\"enabled\\": $buildpacksEnabled}, \\"isDokployNetworkInstalled\\": $isDokployNetworkInstalled, \\"isSwarmInstalled\\": $isSwarmInstalled, \\"isMainDirectoryInstalled\\": $isMainDirectoryInstalled}"
|
||||
echo "{\\"docker\\": {\\"version\\": \\"$dockerVersion\\", \\"enabled\\": $dockerEnabled}, \\"rclone\\": {\\"version\\": \\"$rcloneVersion\\", \\"enabled\\": $rcloneEnabled}, \\"nixpacks\\": {\\"version\\": \\"$nixpacksVersion\\", \\"enabled\\": $nixpacksEnabled}, \\"buildpacks\\": {\\"version\\": \\"$buildpacksVersion\\", \\"enabled\\": $buildpacksEnabled}, \\"railpack\\": {\\"version\\": \\"$railpackVersion\\", \\"enabled\\": $railpackEnabled}, \\"isDokployNetworkInstalled\\": $isDokployNetworkInstalled, \\"isSwarmInstalled\\": $isSwarmInstalled, \\"isMainDirectoryInstalled\\": $isMainDirectoryInstalled}"
|
||||
`;
|
||||
client.exec(bashCommand, (err, stream) => {
|
||||
if (err) {
|
||||
|
||||
@@ -17,7 +17,7 @@ function getProviderName(apiUrl: string) {
|
||||
if (apiUrl.includes("localhost:11434") || apiUrl.includes("ollama"))
|
||||
return "ollama";
|
||||
if (apiUrl.includes("api.deepinfra.com")) return "deepinfra";
|
||||
throw new Error(`Unsupported AI provider for URL: ${apiUrl}`);
|
||||
return "custom";
|
||||
}
|
||||
|
||||
export function selectAIProvider(config: { apiUrl: string; apiKey: string }) {
|
||||
@@ -67,7 +67,46 @@ export function selectAIProvider(config: { apiUrl: string; apiKey: string }) {
|
||||
baseURL: config.apiUrl,
|
||||
apiKey: config.apiKey,
|
||||
});
|
||||
case "custom":
|
||||
return createOpenAICompatible({
|
||||
name: "custom",
|
||||
baseURL: config.apiUrl,
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.apiKey}`,
|
||||
},
|
||||
});
|
||||
default:
|
||||
throw new Error(`Unsupported AI provider: ${providerName}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const getProviderHeaders = (
|
||||
apiUrl: string,
|
||||
apiKey: string,
|
||||
): Record<string, string> => {
|
||||
// Anthropic
|
||||
if (apiUrl.includes("anthropic")) {
|
||||
return {
|
||||
"x-api-key": apiKey,
|
||||
"anthropic-version": "2023-06-01",
|
||||
};
|
||||
}
|
||||
|
||||
// Mistral
|
||||
if (apiUrl.includes("mistral")) {
|
||||
return {
|
||||
Authorization: apiKey,
|
||||
};
|
||||
}
|
||||
|
||||
// Default (OpenAI style)
|
||||
return {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
};
|
||||
};
|
||||
export interface Model {
|
||||
id: string;
|
||||
object: string;
|
||||
created: number;
|
||||
owned_by: string;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { buildCustomDocker, getDockerCommand } from "./docker-file";
|
||||
import { buildHeroku, getHerokuCommand } from "./heroku";
|
||||
import { buildNixpacks, getNixpacksCommand } from "./nixpacks";
|
||||
import { buildPaketo, getPaketoCommand } from "./paketo";
|
||||
import { buildRailpack, getRailpackCommand } from "./railpack";
|
||||
import { buildStatic, getStaticCommand } from "./static";
|
||||
|
||||
// NIXPACKS codeDirectory = where is the path of the code directory
|
||||
@@ -55,6 +56,8 @@ export const buildApplication = async (
|
||||
await buildCustomDocker(application, writeStream);
|
||||
} else if (buildType === "static") {
|
||||
await buildStatic(application, writeStream);
|
||||
} else if (buildType === "railpack") {
|
||||
await buildRailpack(application, writeStream);
|
||||
}
|
||||
|
||||
if (application.registryId) {
|
||||
@@ -96,6 +99,9 @@ export const getBuildCommand = (
|
||||
case "dockerfile":
|
||||
command = getDockerCommand(application, logPath);
|
||||
break;
|
||||
case "railpack":
|
||||
command = getRailpackCommand(application, logPath);
|
||||
break;
|
||||
}
|
||||
if (registry) {
|
||||
command += uploadImageRemoteCommand(application, logPath);
|
||||
|
||||
87
packages/server/src/utils/builders/railpack.ts
Normal file
87
packages/server/src/utils/builders/railpack.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { WriteStream } from "node:fs";
|
||||
import type { ApplicationNested } from ".";
|
||||
import { prepareEnvironmentVariables } from "../docker/utils";
|
||||
import { getBuildAppDirectory } from "../filesystem/directory";
|
||||
import { spawnAsync } from "../process/spawnAsync";
|
||||
import { execAsync } from "../process/execAsync";
|
||||
|
||||
export const buildRailpack = async (
|
||||
application: ApplicationNested,
|
||||
writeStream: WriteStream,
|
||||
) => {
|
||||
const { env, appName } = application;
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
);
|
||||
|
||||
try {
|
||||
// Ensure buildkit container is running, create if it doesn't exist
|
||||
await execAsync(
|
||||
"docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit",
|
||||
);
|
||||
|
||||
// Build the application using railpack
|
||||
const args = ["build", buildAppDirectory, "--name", appName];
|
||||
|
||||
// Add environment variables
|
||||
for (const env of envVariables) {
|
||||
args.push("--env", env);
|
||||
}
|
||||
|
||||
await spawnAsync(
|
||||
"railpack",
|
||||
args,
|
||||
(data) => {
|
||||
if (writeStream.writable) {
|
||||
writeStream.write(data);
|
||||
}
|
||||
},
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
BUILDKIT_HOST: "docker-container://buildkit",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const getRailpackCommand = (
|
||||
application: ApplicationNested,
|
||||
logPath: string,
|
||||
) => {
|
||||
const { env, appName } = application;
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
);
|
||||
|
||||
// Build the application using railpack
|
||||
const args = ["build", buildAppDirectory, "--name", appName];
|
||||
|
||||
// Add environment variables
|
||||
for (const env of envVariables) {
|
||||
args.push("--env", env);
|
||||
}
|
||||
|
||||
const command = `railpack ${args.join(" ")}`;
|
||||
const bashCommand = `
|
||||
echo "Building with Railpack..." >> "${logPath}";
|
||||
docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit;
|
||||
export BUILDKIT_HOST=docker-container://buildkit;
|
||||
${command} >> ${logPath} 2>> ${logPath} || {
|
||||
echo "❌ Railpack build failed" >> ${logPath};
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ Railpack build completed." >> ${logPath};
|
||||
`;
|
||||
|
||||
return bashCommand;
|
||||
};
|
||||
@@ -275,7 +275,7 @@ export const getBitbucketBranches = async (
|
||||
}
|
||||
const bitbucketProvider = await findBitbucketById(input.bitbucketId);
|
||||
const { owner, repo } = input;
|
||||
const url = `https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/refs/branches`;
|
||||
const url = `https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/refs/branches?pagelen=100`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
|
||||
@@ -266,7 +266,7 @@ export const getGitlabRepositories = async (gitlabId?: string) => {
|
||||
if (groupName) {
|
||||
return full_path.toLowerCase().includes(groupName) && kind === "group";
|
||||
}
|
||||
return kind === "member";
|
||||
return kind === "user";
|
||||
});
|
||||
const mappedRepositories = filteredRepos.map((repo: any) => {
|
||||
return {
|
||||
@@ -433,7 +433,7 @@ export const testGitlabConnection = async (
|
||||
if (groupName) {
|
||||
return full_path.toLowerCase().includes(groupName) && kind === "group";
|
||||
}
|
||||
return kind === "member";
|
||||
return kind === "user";
|
||||
});
|
||||
|
||||
return filteredRepos.length;
|
||||
|
||||
Reference in New Issue
Block a user