Merge branch 'canary' into feat/multi-node-support

This commit is contained in:
Mauricio Siu
2024-05-26 03:09:39 -06:00
17 changed files with 104 additions and 52 deletions

View File

@@ -1,5 +1,7 @@
# License
## Core License (Apache License 2.0)
Copyright 2024 Mauricio Siu.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,11 +15,12 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
## Appendix
## Additional Terms for Specific Features
In the event of a conflict, the provisions in this appendix shall take precedence over those in the Apache License.
The following additional terms apply to the multi-node support and Docker Compose file support features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License:
- **Modification Distribution:** Any modifications to the software must be distributed freely.
- **Future Paid Features:** Any future paid features of Dokploy cannot be sold or offered as a service by any party other than the copyright holder without prior written consent.
- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support and Docker Compose file support, will always be free to use in the self-hosted version.
- **Restriction on Resale**: The multi-node support and Docker Compose file support features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent.
- **Modification Distribution**: Any modifications to the multi-node support and Docker Compose file support features must be distributed freely and cannot be sold or offered as a service.
For further inquiries or permissions, please contact us directly.

View File

@@ -41,6 +41,8 @@ Getestete Systems:
- Ubuntu 20.04
- Debian 11
- Fedora 40
- Centos 9
## 📄 Dokumentation

View File

@@ -42,6 +42,8 @@ curl -sSL https://dokploy.com/install.sh | sh
- Ubuntu 20.04
- Debian 11
- Fedora 40
- Centos 9
## 📄 Документация
Для подробной документации посетите docs.dokploy.com/docs.
Для подробной документации посетите [docs.dokploy.com/docs](https://docs.dokploy.com).

View File

@@ -45,6 +45,8 @@ curl -sSL https://dokploy.com/install.sh | sh
- Ubuntu 20.04
- Debian 11
- Fedora 40
- Centos 9
## 📄 文档

View File

@@ -44,6 +44,8 @@ Tested Systems:
- Ubuntu 20.04
- Debian 11
- Fedora 40
- Centos 9
## 📄 Documentation

View File

@@ -89,9 +89,9 @@ export const ShowProjects = () => {
<span className="flex flex-col gap-1.5">
<div className="flex items-center gap-2">
<BookIcon className="size-4 text-muted-foreground" />
<span className="text-base font-medium leading-none">
<Link className="text-base font-medium leading-none" href={`/dashboard/project/${project.projectId}`}>
{project.name}
</span>
</Link>
</div>
<span className="text-sm font-medium text-muted-foreground">

View File

@@ -25,7 +25,7 @@ import {
} from "@/components/ui/card";
const appearanceFormSchema = z.object({
theme: z.enum(["light", "dark"], {
theme: z.enum(["light", "dark", "system"], {
required_error: "Please select a theme.",
}),
});
@@ -34,7 +34,7 @@ type AppearanceFormValues = z.infer<typeof appearanceFormSchema>;
// This can come from your database or API.
const defaultValues: Partial<AppearanceFormValues> = {
theme: "light",
theme: "system",
};
export function AppearanceForm() {
@@ -46,7 +46,7 @@ export function AppearanceForm() {
useEffect(() => {
form.reset({
theme: theme === "light" ? "light" : "dark",
theme: (theme ?? "system") as AppearanceFormValues["theme"],
});
}, [form, theme]);
function onSubmit(data: AppearanceFormValues) {
@@ -81,28 +81,15 @@ export function AppearanceForm() {
onValueChange={field.onChange}
defaultValue={field.value}
value={field.value}
className="grid max-w-md grid-cols-1 sm:grid-cols-2 gap-8 pt-2"
className="grid max-w-md md:max-w-lg grid-cols-1 sm:grid-cols-3 gap-8 pt-2"
>
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem value="light" className="sr-only" />
</FormControl>
<div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
<div className="space-y-2 rounded-sm bg-[#ecedef] p-2">
<div className="space-y-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
</div>
<div className="items-center rounded-md border-2 border-muted p-1 hover:bg-accent transition-colors cursor-pointer">
<img src="/images/theme-light.svg" alt="light" />
</div>
<span className="block w-full p-2 text-center font-normal">
Light
@@ -114,27 +101,30 @@ export function AppearanceForm() {
<FormControl>
<RadioGroupItem value="dark" className="sr-only" />
</FormControl>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
<div className="space-y-2 rounded-sm bg-slate-950 p-2">
<div className="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-2 w-[80px] rounded-lg bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
</div>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer">
<img src="/images/theme-dark.svg" alt="dark" />
</div>
<span className="block w-full p-2 text-center font-normal">
Dark
</span>
</FormLabel>
</FormItem>
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem
value="system"
className="sr-only"
/>
</FormControl>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer">
<img src="/images/theme-system.svg" alt="system" />
</div>
<span className="block w-full p-2 text-center font-normal">
System
</span>
</FormLabel>
</FormItem>
</RadioGroup>
</FormItem>
);

View File

@@ -147,11 +147,11 @@ export const ProfileForm = () => {
}}
defaultValue={field.value}
value={field.value}
className="flex flex-row flex-wrap gap-2 max-xl:justify-cente"
className="flex flex-row flex-wrap gap-2 max-xl:justify-center"
>
{randomImages.map((image) => (
<FormItem key={image}>
<FormLabel className="[&:has([data-state=checked])>img]:border-primary [&:has([data-state=checked])>img]:border-1 [&:has([data-state=checked])>img]:p-px">
<FormLabel className="[&:has([data-state=checked])>img]:border-primary [&:has([data-state=checked])>img]:border-1 [&:has([data-state=checked])>img]:p-px cursor-pointer">
<FormControl>
<RadioGroupItem
value={image}
@@ -163,7 +163,7 @@ export const ProfileForm = () => {
key={image}
src={image}
alt="avatar"
className="h-12 w-12 rounded-full border transition-transform"
className="h-12 w-12 rounded-full border hover:p-px hover:border-primary transition-transform"
/>
</FormLabel>
</FormItem>

View File

@@ -109,7 +109,6 @@ export const Nav = ({ links }: NavProps) => {
<nav className="grid gap-1 px-2 group-[[data-collapsed=true]]:justify-center group-[[data-collapsed=true]]:px-2">
{links.map((link, index) => {
const isActive = router.pathname === link.href;
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
return (
<Link
key={index}
@@ -117,7 +116,7 @@ export const Nav = ({ links }: NavProps) => {
className={cn(
buttonVariants({ variant: "ghost", size: "sm" }),
isActive &&
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white",
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white bg-muted",
"justify-start",
)}
>

View File

@@ -88,8 +88,7 @@ const Tree = React.forwardRef<HTMLDivElement, TreeProps>(
const { ref: refRoot, width, height } = useResizeObserver();
return (
<div ref={refRoot} className={cn("overflow-hidden", className)}>
{/* style={{ width, height }} */}
<div ref={refRoot} className={cn("overflow-y-auto", className)}>
<ScrollArea>
<div className="relative p-2">
<TreeItem

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.0.3",
"version": "v0.0.4",
"private": true,
"license": "AGPL-3.0-only",
"type": "module",

View File

@@ -35,7 +35,25 @@ export default async function handler(
const deploymentTitle = extractCommitMessage(req.headers, req.body);
const sourceType = application.sourceType;
if (sourceType === "github") {
if (sourceType === "docker") {
const applicationDockerTag = extractImageTag(application.dockerImage);
const webhookDockerTag = extractImageTagFromRequest(
req.headers,
req.body,
);
if (
applicationDockerTag &&
webhookDockerTag &&
webhookDockerTag !== applicationDockerTag
) {
res.status(301).json({
message: `Application Image Tag (${applicationDockerTag}) doesn't match request event payload Image Tag (${webhookDockerTag}).`,
});
return;
}
}
else if (sourceType === "github") {
const branchName = extractBranchName(req.headers, req.body);
if (!branchName || branchName !== application.branch) {
res.status(301).json({ message: "Branch Not Match" });
@@ -79,6 +97,36 @@ export default async function handler(
res.status(400).json({ message: "Error To Deploy Application", error });
}
}
/**
* Return the last part of the image name, which is the tag
* Example: "my-image" => null
* Example: "my-image:latest" => "latest"
* Example: "my-image:1.0.0" => "1.0.0"
* Example: "myregistryhost:5000/fedora/httpd:version1.0" => "version1.0"
* @link https://docs.docker.com/reference/cli/docker/image/tag/
*/
function extractImageTag(dockerImage: string | null) {
if (!dockerImage || typeof dockerImage !== "string") {
return null;
}
const tag = dockerImage.split(":").pop();
return tag === dockerImage ? "latest" : tag;
}
/**
* @link https://docs.docker.com/docker-hub/webhooks/#example-webhook-payload
*/
function extractImageTagFromRequest(headers: any, body: any): string | null {
if (headers["user-agent"]?.includes("Go-http-client")) {
if (body.push_data && body.repository) {
return body.push_data.tag;
}
}
return null;
}
function extractCommitMessage(headers: any, body: any) {
// GitHub
if (headers["x-github-event"]) {

View File

@@ -212,7 +212,7 @@ const Project = (
}}
className="group relative cursor-pointer bg-transparent transition-colors hover:bg-card h-fit"
>
<div className="absolute -right-1 -top-1">
<div className="absolute -right-1 -top-2">
<StatusTooltip status={service.status} />
</div>

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 588 406" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="588" height="406" rx="16" fill="#050912"/><g filter="url(#a)"><rect x="24" y="22" width="540" height="122" rx="20" fill="#171E2D"/></g><g filter="url(#b)"><rect x="24" y="167" width="540" height="96" rx="20" fill="#171E2D"/></g><g filter="url(#c)"><rect x="24" y="288" width="540" height="96" rx="20" fill="#171E2D"/></g><rect x="56" y="47" width="240" height="24" rx="12" fill="#8291A9"/><rect x="56" y="94" width="300" height="24" rx="12" fill="#8291A9"/><rect x="129" y="203" width="300" height="24" rx="12" fill="#8291A9"/><rect x="129" y="324" width="300" height="24" rx="12" fill="#8291A9"/><circle cx="80" cy="215" r="24" fill="#8291A9"/><circle cx="80" cy="336" r="24" fill="#8291A9"/><defs><filter id="a" x="19" y="20" width="550" height="132" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset dy="3"/><feGaussianBlur stdDeviation="2.5"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_10_60"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_10_60" result="shape"/></filter><filter id="b" x="19" y="165" width="550" height="106" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset dy="3"/><feGaussianBlur stdDeviation="2.5"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_10_60"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_10_60" result="shape"/></filter><filter id="c" x="19" y="286" width="550" height="106" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset dy="3"/><feGaussianBlur stdDeviation="2.5"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_10_60"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_10_60" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 588 406" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="588" height="406" rx="16" fill="#E7E8EB"/><g filter="url(#a)"><rect x="24" y="22" width="540" height="122" rx="20" fill="#fff"/></g><g filter="url(#b)"><rect x="24" y="167" width="540" height="96" rx="20" fill="#fff"/></g><g filter="url(#c)"><rect x="24" y="288" width="540" height="96" rx="20" fill="#fff"/></g><rect x="56" y="47" width="240" height="24" rx="12" fill="#E7E8EB"/><rect x="56" y="94" width="300" height="24" rx="12" fill="#E7E8EB"/><rect x="129" y="203" width="300" height="24" rx="12" fill="#E7E8EB"/><rect x="129" y="324" width="300" height="24" rx="12" fill="#E7E8EB"/><circle cx="80" cy="215" r="24" fill="#E7E8EB"/><circle cx="80" cy="336" r="24" fill="#E7E8EB"/><defs><filter id="a" x="19" y="20" width="550" height="132" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset dy="3"/><feGaussianBlur stdDeviation="2.5"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_10_59"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_10_59" result="shape"/></filter><filter id="b" x="19" y="165" width="550" height="106" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset dy="3"/><feGaussianBlur stdDeviation="2.5"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_10_59"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_10_59" result="shape"/></filter><filter id="c" x="19" y="286" width="550" height="106" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feOffset dy="3"/><feGaussianBlur stdDeviation="2.5"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_10_59"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_10_59" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -33,7 +33,9 @@ export const initializeRedis = async () => {
Ports: [
{
TargetPort: 6379,
PublishedPort: 6379,
...(process.env.NODE_ENV === "development"
? { PublishedPort: 6379 }
: {}),
Protocol: "tcp",
},
],