Compare commits

..

46 Commits

Author SHA1 Message Date
Mauricio Siu a6de744f91 fix(2fa): improve error handling and display for two-factor authentication setup
- Handle potential errors when enabling two-factor authentication
- Provide more detailed error messages for password verification
- Update client-side error handling to display specific error messages
- Add base URL configuration for non-cloud environments
2025-03-08 12:37:50 -06:00
Mauricio Siu b64ddf1119 Merge pull request #1392 from ali-issa/feature/project-view-tab-reorg
feat: reorganize project view tabs into logical workflow groups #1261
2025-03-08 12:36:46 -06:00
Mauricio Siu 2f074ac734 Merge pull request #1405 from frostming/patch-1
feat: fallback to openai compatible provider if url host doesn't match
2025-03-07 00:56:33 -06:00
Mauricio Siu 96e3721b4b chore(ai): remove debug logging in model fetching 2025-03-07 00:56:12 -06:00
Mauricio Siu b8e5cae88f feat(ai): improve model fetching and error handling
- Add server-side model fetching endpoint with flexible provider support
- Refactor client-side AI settings component to use new API query
- Implement dynamic header generation for different AI providers
- Enhance error handling and toast notifications
- Remove local model fetching logic in favor of server-side implementation
2025-03-07 00:55:11 -06:00
Mauricio Siu fa20444a14 Merge pull request #1414 from gentslava/fix/template-superset
fix(template): superset
2025-03-07 00:12:43 -06:00
Mauricio Siu 668ccabec8 Merge pull request #1390 from hexaaagon/feat/zipline
feat(zipline): update zipline version
2025-03-07 00:12:19 -06:00
Mauricio Siu aa07a0c574 Merge pull request #1417 from vinumzz/vinumzz-remove-shadow-monitoring
refactor: remove unnecessary extra shadow from monitoring page
2025-03-07 00:10:22 -06:00
Mauricio Siu 0b64b43376 Merge pull request #1415 from gentslava/feature/template-datalens
feat(template): DataLens
2025-03-07 00:10:03 -06:00
Mauricio Siu 5c65dc9a21 Update apps/dokploy/templates/datalens/docker-compose.yml 2025-03-07 00:06:20 -06:00
Mauricio Siu 58262606d4 Update apps/dokploy/templates/datalens/docker-compose.yml 2025-03-07 00:06:16 -06:00
Mauricio Siu f73959db41 Update apps/dokploy/templates/datalens/docker-compose.yml 2025-03-07 00:06:10 -06:00
Mauricio Siu e6c664e65f Update apps/dokploy/templates/datalens/docker-compose.yml 2025-03-07 00:06:06 -06:00
Mauricio Siu 36cc157566 Merge pull request #1355 from drudge/templates/hoarder
feat: add Hoarder template
2025-03-07 00:03:42 -06:00
Mauricio Siu 7e070623cc Merge pull request #1411 from gentslava/canary
fix: breadcrumbs UX
2025-03-06 23:58:42 -06:00
Mauricio Siu b2c0a685f8 fix(destinations): validate server selection for cloud destinations 2025-03-06 23:57:39 -06:00
Mauricio Siu c14528886d Merge pull request #1424 from Dokploy/1382-automated-postgres-backup-always-empty
feat(destinations): add createdAt timestamp and display creation date
2025-03-06 23:54:03 -06:00
Mauricio Siu 29eb490e2d feat(destinations): add createdAt timestamp and display creation date 2025-03-06 23:46:21 -06:00
Mauricio Siu 6166963b00 fix(gitlab): remove debug console logs from connection testing 2025-03-06 22:27:30 -06:00
Mauricio Siu f544efed35 Merge pull request #1422 from Dokploy/1418-fetching-gitlab-repositories-0-found
fix(gitlab): update repository filtering and connection testing
2025-03-06 22:17:34 -06:00
Mauricio Siu 598d095241 fix(gitlab): update repository filtering and connection testing
- Change repository filtering to use 'user' kind instead of 'member'
- Add console logging for debugging GitLab provider and repository connection
- Ensure consistent filtering logic in both getGitlabRepositories and testGitlabConnection
2025-03-06 22:17:20 -06:00
Mauricio Siu 457a8e05fd chore(issue-template): update bug report label emoji 2025-03-06 22:05:31 -06:00
Mauricio Siu 3ca057c44a chore(issue-template): update bug report label and add cloud version option 2025-03-06 22:04:46 -06:00
Nicholas Penree ad3a0198e9 feat: add Hoarder template 2025-03-06 08:23:49 -05:00
Peter Vinum ab5f62604c refactor: remove unnecessary extra shadow from monitoring page 2025-03-06 14:08:23 +01:00
Vyacheslav Shcherbinin bf9e886b9a Disable demo 2025-03-06 14:14:01 +07:00
Vyacheslav Shcherbinin f5cd0fbdd8 Restart policy 2025-03-06 11:32:13 +07:00
Vyacheslav Scherbinin 8859cc97b4 fix: superset docker-compose 2025-03-06 10:46:10 +07:00
Vyacheslav Scherbinin 3bdd5e4dd0 Template DataLens 2025-03-06 10:34:13 +07:00
Vyacheslav Scherbinin b0c710aa92 Tab instead space 2025-03-05 21:25:11 +07:00
Vyacheslav Scherbinin c83d0a95b7 Remove ending separator 2025-03-05 20:53:38 +07:00
Vyacheslav Scherbinin 71ca5babfd Remove duplicate breadcrumb 2025-03-05 20:51:09 +07:00
Vyacheslav Scherbinin f342613503 Text format 2025-03-05 20:27:16 +07:00
Frost Ming efd176451f fix: default case correct 2025-03-05 16:19:28 +08:00
Frost Ming a7fd64e019 fix: use a named case 2025-03-05 16:17:50 +08:00
Frost Ming 21c8b98f9c feat: fallback to openai compatible provider if url host doesn't match 2025-03-05 16:12:46 +08:00
Mauricio Siu 6ff06576d0 fix(dockerfile): update Railpack installation script to use bash 2025-03-05 00:36:20 -06:00
Mauricio Siu 24cc08a1ac Merge pull request #1402 from Dokploy/feat/add-railpack
feat(application): add Railpack as a new build type
2025-03-05 00:24:37 -06:00
Mauricio Siu e039826d50 chore(version): bump project version to v0.19.1 2025-03-05 00:21:36 -06:00
Mauricio Siu a947b4915a Merge pull request #1398 from shaunjanssens/feature/bitbucket-branches-length
feat: Bitbucket branch length
2025-03-05 00:19:25 -06:00
Mauricio Siu fc1d9ad202 Merge pull request #1399 from nktnet1/profile-moved-to-top
feat(ui): move profile to top of account dropdown
2025-03-05 00:18:43 -06:00
Mauricio Siu 5489e3b0a5 feat(application): add Railpack as a new build type
- Introduce Railpack as a new build method for applications
- Update database schema to include 'railpack' in buildType enum
- Add Railpack installation and validation scripts for servers
- Implement Railpack build and command generation utilities
- Update UI to include Railpack as a build option with a 'New' badge
2025-03-05 00:18:10 -06:00
Khiet Tam Nguyen e43b907a8d feat(ui): move profile to first item in account dropdown 2025-03-04 22:42:04 +11:00
Shaun Janssens 62fae661a1 feat: bitbucket branch length 2025-03-04 10:50:42 +01:00
Ali Issa 6846e0e5a3 feat: reorganize project view tabs into logical workflow groups #1261
Restructure the project view tabs to follow a more intuitive user journey:
- Group tabs into "Initial Setup", "Deployment", and "Monitoring" sections
- Maintain "Advanced" as a standalone option
- Order tabs to match typical project workflow (configuration → deployment → monitoring)

This reorganization reduces cognitive load by grouping related functions,
minimizes tab switching during common tasks, and provides a clearer
mental model of the platform's workflow for new users.
2025-03-03 06:45:36 -05:00
Hexaa 49d4cea06f feat(zipline): update zipline version 2025-03-03 12:17:42 +07:00
49 changed files with 10929 additions and 137 deletions
+2 -1
View File
@@ -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
+7
View File
@@ -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.
+4
View File
@@ -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>
-4
View File
@@ -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>
+8 -8
View File
@@ -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>
@@ -0,0 +1 @@
ALTER TYPE "public"."buildType" ADD VALUE 'railpack';
@@ -0,0 +1 @@
ALTER TABLE "destination" ADD COLUMN "createdAt" timestamp DEFAULT now() NOT NULL;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+14
View File
@@ -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 -1
View File
@@ -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>
@@ -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

@@ -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

+56
View File
@@ -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;
@@ -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
View 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,
};
}
@@ -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
View 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:
+29
View File
@@ -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(),
+2 -1
View File
@@ -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(
+3
View File
@@ -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",
},
+13
View File
@@ -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
+17 -2
View File
@@ -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);
@@ -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;