Compare commits

..

34 Commits

Author SHA1 Message Date
Mauricio Siu
467acc4d4d Merge pull request #586 from Dokploy/canary
v0.10.3
2024-10-24 01:21:17 -06:00
Mauricio Siu
fc2778db35 Merge pull request #585 from Dokploy/505-mongodb-backup-is-empty-in-output-pipeline
fix(dokploy): add missing --archive to mongodump
2024-10-24 01:14:10 -06:00
Mauricio Siu
85d6ff9012 chore(version): bump version 2024-10-24 01:11:43 -06:00
Mauricio Siu
522f8baec7 fix(dokploy): add missing --archive to mongodump 2024-10-24 01:07:02 -06:00
Mauricio Siu
22eb965919 Merge pull request #584 from dmbr0/patch-2
Fix typo in README.md
2024-10-23 20:08:37 -06:00
Alex Whitney
c0746b95b3 Fix typo in README.md 2024-10-23 20:18:59 -04:00
Mauricio Siu
01e5cf0852 Merge pull request #580 from Dokploy/canary
v0.10.2
2024-10-22 21:17:38 -06:00
Mauricio Siu
8faa6ae1cf chore(version): bump version 2024-10-22 20:29:57 -06:00
Mauricio Siu
76ed1107c2 refactor(dokploy): add -r flag to read the enviroments vars 2024-10-22 20:19:49 -06:00
Mauricio Siu
cff5049096 Merge pull request #579 from Dokploy/578-unable-to-reset-my-password
fix(dokploy): use the exact path of functions #578
2024-10-22 20:02:43 -06:00
Mauricio Siu
cb5ca100a6 fix(dokploy): use the exact path of functions #578 2024-10-22 20:00:29 -06:00
Mauricio Siu
03d1e974dd Update LICENSE.MD 2024-10-22 16:53:12 -06:00
Mauricio Siu
b609d72d1c Merge pull request #576 from Dokploy/541-install-failed-due-to-docker-swarm-initialize-failed
fix(installation): exit of script when docker swarm init fails
2024-10-21 21:58:22 -06:00
Mauricio Siu
d7071fba60 fix(installation): exit of script when docker swarm init fails 2024-10-21 21:50:26 -06:00
Mauricio Siu
76585991ec Merge pull request #559 from eremannisto/fix/improve-faq-questions-and-answers
fix: Improve `FAQ` questions and answers
2024-10-21 21:31:00 -06:00
Mauricio Siu
da6efcf733 Merge pull request #406 from benbristow/fix/directus
fix: directus healthchecks (fix race condition starting up), volumes for uploads & extensions, add secret/db password generation, bump version to 11.0.2
2024-10-21 21:28:52 -06:00
Mauricio Siu
64b0770cfb Merge pull request #575 from Dokploy/574-clone-github-gitlab-and-bitbucket-submodules
feat(dokploy): add recurse submodules to providers #331
2024-10-21 21:25:18 -06:00
Mauricio Siu
1ec83a3236 feat(dokploy): add recurse submodules to providers #331 2024-10-21 21:14:40 -06:00
Mauricio Siu
df9fad088f Merge pull request #567 from Dokploy/canary
v0.10.1
2024-10-18 23:12:12 -06:00
Mauricio Siu
7d5a660f4d chore: bump version 2024-10-18 21:56:39 -06:00
Mauricio Siu
f7f0cbf318 Merge pull request #566 from arioberek/canary
fix: update reset password link URL
2024-10-18 09:34:51 -06:00
Arielton Oberek
841c0731aa fix: remove language prefix from reset password URL 2024-10-18 10:41:07 -03:00
Arielton Oberek
137cd25267 fix: reset password button URL 2024-10-18 10:35:08 -03:00
Mauricio Siu
4dcd16c41e Merge pull request #564 from Dokploy/fix/git-ssh
fix(git): remove old references to ssh files to use the tmp file
2024-10-18 00:28:51 -06:00
Mauricio Siu
60497fe59d refactor: add husky 2024-10-18 00:10:01 -06:00
Mauricio Siu
8536945a60 styles: lint 2024-10-17 23:58:51 -06:00
Mauricio Siu
e0a8d8258c fix(git): remove old references to ssh files to use the tmp file 2024-10-17 23:55:27 -06:00
Mauricio Siu
fe19cdb5e4 fix(setup): import directly from specific path 2024-10-16 15:57:51 -06:00
Mauricio Siu
c4654a9619 chore(contributing): update contributing 2024-10-16 13:23:25 -06:00
Ere Männistö
0a123a652b Improve FAQ questions and answers
- Improve questions and answers
- Fix typos
2024-10-15 19:54:41 +03:00
Mauricio Siu
5d437c29b2 refactor: remove header and navbar 2024-10-13 22:58:01 -06:00
Mauricio Siu
53f345ab1d feat: add privacy & terms 2024-10-13 22:54:02 -06:00
Ben Bristow
f8721d3e04 fix: remove 'networks' section 2024-09-02 21:20:27 +01:00
Ben Bristow
a6c7c3b031 fix: directus healthchecks (fix race condition starting up), volumes for uploads & extensions, add secret/db password generation, bump version to 11.0.2 2024-09-02 21:11:03 +01:00
34 changed files with 889 additions and 96 deletions

1
.husky/commit-msg Normal file
View File

@@ -0,0 +1 @@
npx commitlint --edit "$1"

6
.husky/install.mjs Normal file
View File

@@ -0,0 +1,6 @@
// Skip Husky install in production and CI
if (process.env.NODE_ENV === "production" || process.env.CI === "true") {
process.exit(0);
}
const husky = (await import("husky")).default;
console.log(husky());

2
.husky/pre-commit Normal file
View File

@@ -0,0 +1,2 @@
pnpm run check
git add .

View File

@@ -71,6 +71,12 @@ Run the command that will spin up all the required services and files.
pnpm run dokploy:setup
```
Build the server package (If you make any changes after in the packages/server folder, you need to rebuild and run this command)
```bash
pnpm run server:build
```
Now run the development server.
```bash

View File

@@ -17,10 +17,10 @@ See the License for the specific language governing permissions and limitations
## Additional Terms for Specific Features
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:
The following additional terms apply to the multi-node support, Docker Compose file and Multi Server features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License:
- **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.
- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support, Docker Compose file support and Multi Server, will always be free to use in the self-hosted version.
- **Restriction on Resale**: The multi-node support, Docker Compose file support and Multi Server 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, Docker Compose file support and Multi Server 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

@@ -30,7 +30,7 @@ Dokploy include multiples features to make your life easier.
- **Traefik Integration**: Automatically integrates with Traefik for routing and load balancing.
- **Real-time Monitoring**: Monitor CPU, memory, storage, and network usage, for every resource.
- **Docker Management**: Easily deploy and manage Docker containers.
- **CLI/API**: Manage your applications and databases using the command line or trought the API.
- **CLI/API**: Manage your applications and databases using the command line or through the API.
- **Notifications**: Get notified when your deployments are successful or failed (Slack, Discord, Telegram, Email, etc.)
- **Multi Server**: Deploy and manager your applications remotely to external servers.
- **Self-Hosted**: Self-host Dokploy on your VPS.

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.10.0",
"version": "v0.10.3",
"private": true,
"license": "Apache-2.0",
"type": "module",
@@ -10,7 +10,7 @@
"build-server": "tsx esbuild.config.ts",
"build-next": "next build",
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
"reset-password": "node dist/reset-password.mjs",
"reset-password": "node -r dotenv/config dist/reset-password.mjs",
"dev": "tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ",
"studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts",
"migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts",

View File

@@ -178,7 +178,7 @@ export default function Home() {
<div className="mt-4 text-sm flex flex-row justify-center gap-2">
<Link
className="hover:underline text-muted-foreground"
href="https://docs.dokploy.com/get-started/reset-password"
href="https://docs.dokploy.com/docs/core/get-started/reset-password"
target="_blank"
>
Lost your password?

View File

@@ -2,14 +2,16 @@ import {
createDefaultMiddlewares,
createDefaultServerTraefikConfig,
createDefaultTraefikConfig,
initializeNetwork,
initializePostgres,
initializeRedis,
initializeSwarm,
initializeTraefik,
setupDirectories,
} from "@dokploy/server";
} from "@dokploy/server/dist/setup/traefik-setup";
import { setupDirectories } from "@dokploy/server/dist/setup/config-paths";
import { initializePostgres } from "@dokploy/server/dist/setup/postgres-setup";
import { initializeRedis } from "@dokploy/server/dist/setup/redis-setup";
import {
initializeNetwork,
initializeSwarm,
} from "@dokploy/server/dist/setup/setup";
(async () => {
try {
setupDirectories();

View File

@@ -1,47 +1,66 @@
version: "3.8"
services:
database:
image: postgis/postgis:13-master
volumes:
- directus:/var/lib/postgresql/data
- directus_database:/var/lib/postgresql/data
networks:
- dokploy-network
environment:
POSTGRES_USER: "directus"
POSTGRES_PASSWORD: "directus"
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
POSTGRES_DB: "directus"
healthcheck:
test: ["CMD", "pg_isready", "--host=localhost", "--username=directus"]
interval: 10s
timeout: 5s
retries: 5
start_interval: 5s
start_period: 30s
cache:
image: redis:6
healthcheck:
test: ["CMD-SHELL", "[ $$(redis-cli ping) = 'PONG' ]"]
interval: 10s
timeout: 5s
retries: 5
start_interval: 5s
start_period: 30s
networks:
- dokploy-network
directus:
image: directus/directus:10.12.1
image: directus/directus:11.0.2
ports:
- 8055
volumes:
- ../files/uploads:/directus/uploads
- ../files/extensions:/directus/extensions
- directus_uploads:/directus/uploads
- directus_extensions:/directus/extensions
depends_on:
- cache
- database
database:
condition: service_healthy
cache:
condition: service_healthy
environment:
SECRET: "replace-with-secure-random-value"
SECRET: ${DIRECTUS_SECRET}
DB_CLIENT: "pg"
DB_HOST: "database"
DB_PORT: "5432"
DB_DATABASE: "directus"
DB_USER: "directus"
DB_PASSWORD: "directus"
DB_PASSWORD: ${DATABASE_PASSWORD}
CACHE_ENABLED: "true"
CACHE_AUTO_PURGE: "true"
CACHE_STORE: "redis"
REDIS: "redis://cache:6379"
# After first successful login, remove the admin email/password env. variables below
# as these will now be stored in the database.
ADMIN_EMAIL: "admin@example.com"
ADMIN_PASSWORD: "d1r3ctu5"
volumes:
directus:
directus_uploads:
directus_extensions:
directus_database:

View File

@@ -2,10 +2,15 @@ import {
type DomainSchema,
type Schema,
type Template,
generateBase64,
generatePassword,
generateRandomDomain,
} from "../utils";
export function generate(schema: Schema): Template {
const directusSecret = generateBase64(64);
const databasePassword = generatePassword();
const domains: DomainSchema[] = [
{
host: generateRandomDomain(schema),
@@ -14,7 +19,13 @@ export function generate(schema: Schema): Template {
},
];
const envs = [
`DATABASE_PASSWORD=${databasePassword}`,
`DIRECTUS_SECRET=${directusSecret}`,
];
return {
domains,
envs,
};
}

View File

@@ -80,7 +80,7 @@ export const templates: TemplateData[] = [
{
id: "directus",
name: "Directus",
version: "10.12.1",
version: "11.0.2",
description:
"Directus is an open source headless CMS that provides an API-first solution for building custom backends.",
logo: "directus.jpg",

View File

@@ -6,6 +6,8 @@ import GoogleAnalytics from "@/components/analitycs/google";
import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -89,7 +91,9 @@ export default async function RootLayout({
<GoogleAnalytics />
<body className="flex h-full flex-col">
<NextIntlClientProvider messages={messages}>
<Header />
{children}
<Footer />
</NextIntlClientProvider>
</body>
</html>

View File

@@ -1,16 +1,12 @@
import { CallToAction } from "@/components/CallToAction";
import { Faqs } from "@/components/Faqs";
import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header";
import { Hero } from "@/components/Hero";
import { PrimaryFeatures } from "@/components/PrimaryFeatures";
import { SecondaryFeatures } from "@/components/SecondaryFeatures";
import { Testimonials } from "../../components/Testimonials";
export default function Home() {
return (
<div>
<Header />
<main>
<Hero />
<PrimaryFeatures />
@@ -18,7 +14,6 @@ export default function Home() {
<CallToAction />
{/* <Testimonials /> */}
<Faqs />
<Footer />
</main>
</div>
);

View File

@@ -0,0 +1,9 @@
import { Pricing } from "@/components/pricing";
export default function Home() {
return (
<div className="w-full">
<Pricing />
</div>
);
}

View File

@@ -0,0 +1,111 @@
export default function Home() {
return (
<div className="flex flex-col gap-4 w-full max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-center mb-6">Privacy</h1>
<section className="flex flex-col gap-2">
<p>
At Dokploy, we are committed to protecting your privacy. This Privacy
Policy explains how we collect, use, and safeguard your personal
information when you use our website and services.
</p>
<p>
By using Dokploy, you agree to the collection and use of information
in accordance with this Privacy Policy. If you do not agree with these
practices, please do not use our services.
</p>
<h2 className="text-2xl font-semibold mb-4">
1. Information We Collect
</h2>
<p className="">
We only collect limited, non-personal data through Umami Analytics, a
privacy-focused analytics tool. No personal identifying information
(PII) is collected. The data we collect includes:
</p>
<ul className="list-disc list-inside mb-4">
<li>Website usage statistics (e.g., page views, session duration)</li>
<li>Anonymized IP addresses</li>
<li>Referring websites</li>
<li>Browser and device type</li>
</ul>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">
2. How We Use the Information
</h2>
<p className="mb-4">
The information we collect is used solely for improving the
functionality and user experience of our platform. Specifically, we
use it to:
</p>
<ul className="list-disc list-inside mb-4">
<li>Monitor traffic and website performance</li>
<li>Optimize the user experience</li>
<li>Understand how users interact with our platform</li>
</ul>
<p>
Additionally, we use a single cookie to manage user sessions, which is
necessary for the proper functioning of the platform.
</p>
</section>
<section className="flex flex-col gap-2">
<h2 className="text-2xl font-semibold mb-4">3. Data Security</h2>
<p className="">
We take reasonable precautions to protect your data. Since we do not
collect personal information, the risk of data misuse is minimized.
Umami Analytics is privacy-friendly and does not rely on cookies or
store PII.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">4. Third-Party Services</h2>
<p>
We do not share your data with any third-party services other than
Umami Analytics. We do not sell, trade, or transfer your data to
outside parties.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">5. Cookies</h2>
<p className="mb-4">
Dokploy does not use cookies to track user activity. Umami Analytics
is cookie-free and does not require any tracking cookies for its
functionality.
</p>
</section>
<section className="flex flex-col gap-2">
<h2 className="text-2xl font-semibold mb-4">
6. Changes to This Privacy Policy
</h2>
<p className="">
We may update this Privacy Policy from time to time. Any changes will
be posted on this page, and it is your responsibility to review this
policy periodically.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">12. Contact Information</h2>
<p className="mb-4">
If you have any questions or concerns regarding these Privacy Policy,
please contact us at:
</p>
<p className="mb-4">
Email:
<a
href="mailto:support@dokploy.com"
className="text-blue-500 hover:underline"
>
support@dokploy.com
</a>
</p>
</section>
</div>
);
}

View File

@@ -0,0 +1,205 @@
export default function Home() {
return (
<div className="flex flex-col gap-4 w-full max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-center mb-6">
Terms and Conditions
</h1>
<section className="flex flex-col gap-2">
<p>
Welcome to Dokploy! These Terms and Conditions outline the rules and
regulations for the use of Dokploys website and services.
</p>
<p>
By accessing or using our services, you agree to be bound by the
following terms. If you do not agree with these terms, please do not
use our website or services.
</p>
<h2 className="text-2xl font-semibold mb-4">1. Definitions</h2>
<p className="">
Website: Refers to the website of Dokploy (
<a
href="https://dokploy.com"
className="text-blue-500 hover:underline"
>
https://dokploy.com
</a>
) and its subdomains.
</p>
<p>
Services: The platform and related services offered by Dokploy for
deploying and managing applications using Docker and other related
tools.
</p>
<p>User: Any individual or organization using Dokploy.</p>
<p>
Subscription: The paid plan for using additional features, resources,
or server capacity.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">2. Service Description</h2>
<p className="mb-4">
Dokploy is a platform that allows users to deploy and manage web
applications on their own servers using custom builders and Docker
technology. Dokploy offers both free and paid services, including
subscriptions for adding additional servers, features, or increased
capacity.
</p>
</section>
<section className="flex flex-col gap-2">
<h2 className="text-2xl font-semibold mb-4">
3. User Responsibilities
</h2>
<p className="">
Users are responsible for maintaining the security of their accounts,
servers, and applications deployed through Dokploy.
</p>
<p className="">
Users must not use the platform for illegal activities, including but
not limited to distributing malware, violating intellectual property
rights, or engaging in cyberattacks.
</p>
<p className="">
Users must comply with all local, state, and international laws in
connection with their use of Dokploy.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">
4. Subscription and Payment
</h2>
<ul className="list-disc list-inside mb-4">
<li>
By purchasing a subscription, users agree to the pricing and payment
terms detailed on the website or via Paddle (our payment processor).
</li>
<li>
Subscriptions renew automatically unless canceled by the user before
the renewal date.
</li>
</ul>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">5. Refund Policy</h2>
<p className="mb-4">
Due to the nature of our digital services, Dokploy operates on a
no-refund policy for any paid subscriptions, except where required by
law. We offer a self-hosted version of Dokploy with the same core
functionalities, which users can deploy and use without any cost. We
recommend users try the self-hosted version to evaluate the platform
before committing to a paid subscription.
</p>
</section>
<section className="flex flex-col gap-2">
<h2 className="text-2xl font-semibold mb-4">
6. Limitations of Liability
</h2>
<p className="">
Dokploy is provided "as is" without any warranties, express or
implied, including but not limited to the availability, reliability,
or accuracy of the service.
</p>
<p className="">
Users are fully responsible for any modifications made to their remote
servers or the environment where Dokploy is deployed. Any changes to
the server configuration, system settings, security policies, or other
environments that deviate from the recommended use of Dokploy may
result in compatibility issues, performance degradation, or security
vulnerabilities. Additionally, Dokploy may not function properly on
unsupported operating systems or environments. We do not guarantee the
platform will operate correctly or reliably under modified server
conditions or on unsupported systems, and we will not be held liable
for any disruptions, malfunctions, or damages resulting from such
changes or unsupported configurations.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">
7. Service Modifications and Downtime
</h2>
<p className="mb-4">
While we strive to provide uninterrupted service, there may be periods
of downtime due to scheduled maintenance or upgrades to our
infrastructure, such as server maintenance or system improvements. We
will provide notice to users ahead of any planned maintenance.
</p>
</section>
<section className="flex flex-col gap-2">
<h2 className="text-2xl font-semibold mb-4">
8. Intellectual Property
</h2>
<p className="">
Dokploy retains all intellectual property rights to the platform,
including code, design, and content.
</p>
<p className="">
Users are granted a limited, non-exclusive, and non-transferable
license to use Dokploy in accordance with these terms.
</p>
<p className="">
Users may not modify, reverse-engineer, or distribute any part of the
platform without express permission.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">9. Termination</h2>
<p className="mb-4">
Dokploy reserves the right to suspend or terminate access to the
platform for users who violate these terms or engage in harmful
behavior.
</p>
<p className="mb-4">
Users may terminate their account at any time by contacting support.
Upon termination, access to the platform will be revoked, and any
stored data may be permanently deleted.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">10. Changes to Terms</h2>
<p className="mb-4">
Dokploy reserves the right to update these Terms & Conditions at any
time. Changes will be effective immediately upon posting on the
website. It is the user's responsibility to review these terms
periodically.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">11. Governing Law</h2>
<p className="mb-4">
These Terms & Conditions are governed by applicable laws based on the
user's location. Any disputes arising under these terms will be
resolved in accordance with the legal jurisdiction relevant to the
users location, unless otherwise required by applicable law.
</p>
</section>
<section className="">
<h2 className="text-2xl font-semibold mb-4">12. Contact Information</h2>
<p className="mb-4">
If you have any questions or concerns regarding these Terms, you can
reach us at:
</p>
<p className="mb-4">
Email:
<a
href="mailto:support@dokploy.com"
className="text-blue-500 hover:underline"
>
support@dokploy.com
</a>
</p>
</section>
</div>
);
}

View File

@@ -7,9 +7,6 @@ export function SlimLayout() {
const t = useTranslations("404");
return (
<>
<div>
<Header />
</div>
<main className="flex flex-auto items-center justify-center text-center">
<div>
<h1 className="mb-4 text-6xl font-semibold text-primary">404</h1>
@@ -22,9 +19,6 @@ export function SlimLayout() {
</p>
</div>
</main>
<div>
<Footer />
</div>
</>
);
}

View File

@@ -0,0 +1,310 @@
"use client";
import clsx from "clsx";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { Container } from "./Container";
import { trackGAEvent } from "./analitycs";
import { Button } from "./ui/button";
import { Switch } from "./ui/switch";
function SwirlyDoodle(props: React.ComponentPropsWithoutRef<"svg">) {
return (
<svg
aria-hidden="true"
viewBox="0 0 281 40"
preserveAspectRatio="none"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M240.172 22.994c-8.007 1.246-15.477 2.23-31.26 4.114-18.506 2.21-26.323 2.977-34.487 3.386-2.971.149-3.727.324-6.566 1.523-15.124 6.388-43.775 9.404-69.425 7.31-26.207-2.14-50.986-7.103-78-15.624C10.912 20.7.988 16.143.734 14.657c-.066-.381.043-.344 1.324.456 10.423 6.506 49.649 16.322 77.8 19.468 23.708 2.65 38.249 2.95 55.821 1.156 9.407-.962 24.451-3.773 25.101-4.692.074-.104.053-.155-.058-.135-1.062.195-13.863-.271-18.848-.687-16.681-1.389-28.722-4.345-38.142-9.364-15.294-8.15-7.298-19.232 14.802-20.514 16.095-.934 32.793 1.517 47.423 6.96 13.524 5.033 17.942 12.326 11.463 18.922l-.859.874.697-.006c2.681-.026 15.304-1.302 29.208-2.953 25.845-3.07 35.659-4.519 54.027-7.978 9.863-1.858 11.021-2.048 13.055-2.145a61.901 61.901 0 0 0 4.506-.417c1.891-.259 2.151-.267 1.543-.047-.402.145-2.33.913-4.285 1.707-4.635 1.882-5.202 2.07-8.736 2.903-3.414.805-19.773 3.797-26.404 4.829Zm40.321-9.93c.1-.066.231-.085.29-.041.059.043-.024.096-.183.119-.177.024-.219-.007-.107-.079ZM172.299 26.22c9.364-6.058 5.161-12.039-12.304-17.51-11.656-3.653-23.145-5.47-35.243-5.576-22.552-.198-33.577 7.462-21.321 14.814 12.012 7.205 32.994 10.557 61.531 9.831 4.563-.116 5.372-.288 7.337-1.559Z"
/>
</svg>
);
}
function CheckIcon({
className,
...props
}: React.ComponentPropsWithoutRef<"svg">) {
return (
<svg
aria-hidden="true"
className={clsx(
"h-6 w-6 flex-none fill-current stroke-current",
className,
)}
{...props}
>
<path
d="M9.307 12.248a.75.75 0 1 0-1.114 1.004l1.114-1.004ZM11 15.25l-.557.502a.75.75 0 0 0 1.15-.043L11 15.25Zm4.844-5.041a.75.75 0 0 0-1.188-.918l1.188.918Zm-7.651 3.043 2.25 2.5 1.114-1.004-2.25-2.5-1.114 1.004Zm3.4 2.457 4.25-5.5-1.187-.918-4.25 5.5 1.188.918Z"
strokeWidth={0}
/>
<circle
cx={12}
cy={12}
r={8.25}
fill="none"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
function Plan({
name,
price,
description,
href,
features,
featured = false,
}: {
name: string;
price: string;
description: string;
href: string;
features: Array<string>;
featured?: boolean;
}) {
const router = useRouter();
return (
<section
className={clsx(
"flex flex-col rounded-3xl px-6 sm:px-8",
featured ? "order-first bg-black border py-8 lg:order-none" : "lg:py-8",
)}
>
<h3 className="mt-5 font-display text-lg text-white">{name}</h3>
<p
className={clsx(
"mt-2 text-base",
featured ? "text-white" : "text-slate-400",
)}
>
{description}
</p>
<p className="order-first font-display text-5xl font-light tracking-tight text-white">
{price}
</p>
<ul
role="list"
className={clsx(
"order-last mt-10 flex flex-col gap-y-3 text-sm",
featured ? "text-white" : "text-slate-200",
)}
>
{features.map((feature) => (
<li key={feature} className="flex">
<CheckIcon className={featured ? "text-white" : "text-slate-400"} />
<span className="ml-4">{feature}</span>
</li>
))}
</ul>
<Button
onClick={() => {
router.push(href);
trackGAEvent({
action: "Buy Plan Clicked",
category: "Pricing",
label: `${name} - ${price}`,
});
}}
className="rounded-full mt-8"
>
Get started
</Button>
{/* <Button
href={href}
variant={featured ? "solid" : "outline"}
color="white"
className="mt-8"
aria-label={`Get started with the ${name} plan for ${price}`}
>
Get started
</Button> */}
</section>
);
}
export function Pricing() {
const [monthly, setMonthly] = useState(false);
return (
<section
id="pricing"
aria-label="Pricing"
className="bg-black border-t border-border/30 py-20 sm:py-32"
>
<Container>
<div className="text-center">
<h2 className="font-display text-3xl tracking-tight text-white sm:text-4xl">
<span className="relative whitespace-nowrap">
<SwirlyDoodle className="absolute left-0 top-1/2 h-[1em] w-full fill-muted-foreground" />
<span className="relative"> Simple & Affordable,</span>
</span>{" "}
Pricing.
</h2>
<p className="mt-4 text-lg text-muted-foreground">
Deploy Smarter, Scale Faster Without Breaking the Bank
</p>
</div>
<div className="mt-10 flex flex-row gap-x-4 justify-center">
<Switch checked={monthly} onCheckedChange={(e) => setMonthly(e)} />
{!monthly ? "Monthly" : "Yearly"}
</div>
<div className=" mt-10 mx-auto">
<div className="mt-16 grid md:grid-cols-2 gap-y-10 mx-auto w-full lg:-mx-8 xl:mx-0 xl:gap-x-8">
<Plan
name="Free"
price="$0"
description="Perfect for developers who prefer to manage their own servers."
href="https://docs.dokploy.com/en/docs/core/get-started/installation#docker"
features={[
"Unlimited deployments",
"Self-hosted on your own infrastructure",
"Full access to all deployment features",
"Docker Swarm and Docker Compose support",
"Community support",
"Custom domains and SSL integration",
"No feature limitations on the core platform",
]}
/>
<Plan
featured
name="General"
price={!monthly ? "$6.99" : "$5.49"}
description="Ideal for indie hackers, freelancers, agencies, and businesses looking for a managed solution."
href="/register"
features={[
"1 free server included (user-provided)",
"All self-hosted features without hosting the UI",
"Dokploy infrastructure managed by us",
"$3.99 per additional server (user-provided)",
]}
/>
{/* <Plan
name="Enterprise"
price="$39"
description="For even the biggest enterprise companies."
href="/register"
features={[
"Send unlimited quotes and invoices",
"Connect up to 15 bank accounts",
"Track up to 200 expenses per month",
"Automated payroll support",
"Export up to 25 reports, including TPS",
]}
/> */}
</div>
</div>
</Container>
<Faqs />
</section>
);
}
const faqs = [
[
{
question: "How does Dokploy's free plan work?",
answer:
"The free plan allows you to self-host Dokploy on your own infrastructure with unlimited deployments and full access to all features.",
},
{
question: "Do I need to provide my own server for the managed plan?",
answer:
"Yes, in the managed plan, you provide your own server, and we manage the Dokploy UI infrastructure for you.",
},
{
question: "What happens if I need more than one server?",
answer:
"Each additional server costs $3.99/month and can be easily added to your account.",
},
],
[
{
question: "Can I use my custom domain with Dokploy?",
answer:
"Yes, custom domain support is available on all plans, including the free version.",
},
{
question: "Is there a limit on the number of deployments?",
answer:
"No, there is no limit on the number of deployments in any of the plans.",
},
{
question: "Do I have to manually configure Traefik?",
answer:
"Dokploy offers dynamic Traefik configuration out-of-the-box, so no manual setup is needed.",
},
],
[
{
question: "How do automated backups work?",
answer:
"Automated backups are included in the managed plan and are limited to database backups only.",
},
{
question: "What kind of support do you offer?",
answer:
"We offer community support for the free plan and priority support for paid plans.",
},
{
question: "Is Dokploy open-source?",
answer:
"Yes, Dokploy is fully open-source. You can contribute or modify it as needed for your projects.",
},
],
];
export function Faqs() {
return (
<section
id="faqs"
aria-labelledby="faq-title"
className="relative overflow-hidden bg-black py-20 sm:py-32"
>
<Container className="relative">
<div className="mx-auto max-w-2xl lg:mx-0">
<h2
id="faq-title"
className="font-display text-3xl tracking-tight text-primary sm:text-4xl"
>
{"Frequently asked questions"}
</h2>
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
If you cant find what youre looking for, please submit an issue
to:{" "}
<Link href={"mailto:support@dokploy.com"} className="text-primary">
support@dokploy.com
</Link>
</p>
</div>
<ul className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-8 lg:max-w-none lg:grid-cols-3">
{faqs.map((column, columnIndex) => (
<li key={columnIndex}>
<ul className="flex flex-col gap-y-8">
{column.map((faq, faqIndex) => (
<li key={faqIndex}>
<h3 className="font-display text-lg leading-7 text-primary">
{faq.question}
</h3>
<p className="mt-4 text-sm text-muted-foreground">
{faq.answer}
</p>
</li>
))}
</ul>
</li>
))}
</ul>
</Container>
</section>
);
}

View File

@@ -0,0 +1,29 @@
"use client";
import * as SwitchPrimitives from "@radix-ui/react-switch";
import * as React from "react";
import { cn } from "@/lib/utils";
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className,
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
)}
/>
</SwitchPrimitives.Root>
));
Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch };

View File

@@ -56,23 +56,23 @@
},
"faq": {
"title": "Frequently asked questions",
"des": "If you cant find what youre looking for, please submit an issue through our GitHub repository or ask questions on our Discord.",
"des": "If you can't find what you're looking for, please submit an issue through our GitHub repository or ask questions on our Discord.",
"q1": "What is dokploy?",
"a1": "Dokploy is a stable, easy-to-use deployment solution designed to simplify the application management process. Think of Dokploy as a free alternative self-hostable solution to platforms like Heroku, Vercel, and Netlify.",
"q2": "Why Choose Dokploy?",
"a2": "Simplicity, Flexibility, and Fast",
"q3": "Is free?",
"a3": "Yes, dokploy is totally free. You can use it for personal projects, small teams, or even for large-scale applications.",
"a2": "Dokploy offers simplicity, flexibility, and speed in application deployment and management.",
"q3": "Is Dokploy free?",
"a3": "Yes, Dokploy is totally free. You can use it for personal projects, small teams, or even for large-scale applications.",
"q4": "Is it open source?",
"a4": "Yes, dokploy is open source and free to use.",
"q5": "What types of languages can i deploy with dokploy?",
"a5": "Dokploy do not restrict programming languages. you are free to choose your preferred language and framework.",
"a4": "Yes, Dokploy is open source and free to use.",
"q5": "What types of languages can I deploy with Dokploy?",
"a5": "Dokploy does not restrict programming languages. You are free to choose your preferred language and framework.",
"q6": "How do I request a feature or report a bug?",
"a6": "Currently we are working on fixing bug fixes, but we will be releasing new features soon. You can also request features or report bugs.",
"a6": "To request a feature or report a bug, please create an issue on our GitHub repository or ask in our Discord channel. We are currently focused on addressing existing bugs and plan to release new features soon.",
"q7": "Do you track the usage of Dokploy?",
"a7": "No, we don't track any usage data.",
"q8": "Are there any user forums or communities where I can interact with other users?",
"a8": "Yes, we have active github discussions where you can share ideas, ask for help, and connect with other users.",
"a8": "Yes, we have active GitHub discussions where you can share ideas, ask for help, and connect with other users.",
"q9": "What types of applications can I deploy with Dokploy?",
"a9": "Dokploy supports a variety of applications, including those built with Docker, as well as applications from any Git repository, offering custom builds with Nixpacks, Dockerfiles, or Buildpacks like Heroku and Paketo.",
"q10": "How does Dokploy handle database management?",

View File

@@ -16,6 +16,7 @@
"@headlessui/tailwindcss": "^0.2.0",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@types/node": "20.4.6",
"autoprefixer": "^10.4.12",
"class-variance-authority": "^0.7.0",

View File

@@ -61,6 +61,11 @@ install_dokploy() {
docker swarm init --advertise-addr $advertise_addr
if [ $? -ne 0 ]; then
echo "Error: Failed to initialize Docker Swarm" >&2
exit 1
fi
echo "Swarm initialized"
docker network rm -f dokploy-network 2>/dev/null

View File

@@ -45,6 +45,11 @@ install_dokploy() {
docker swarm init --advertise-addr $advertise_addr
if [ $? -ne 0 ]; then
echo "Error: Failed to initialize Docker Swarm" >&2
exit 1
fi
echo "Swarm initialized"
docker network rm -f dokploy-network 2>/dev/null

View File

@@ -59,6 +59,11 @@ install_dokploy() {
advertise_addr=$(get_ip)
docker swarm init --advertise-addr $advertise_addr
if [ $? -ne 0 ]; then
echo "Error: Failed to initialize Docker Swarm" >&2
exit 1
fi
echo "Swarm initialized"

View File

@@ -23,7 +23,7 @@
"format-and-lint": "biome check .",
"check": "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true",
"format-and-lint:fix": "biome check . --write",
"prepare": "node ./.config/.husky/install.mjs"
"prepare": "node .husky/install.mjs"
},
"devDependencies": {
"dotenv": "16.4.5",
@@ -31,7 +31,7 @@
"tsx": "4.16.2",
"lint-staged": "^15.2.7",
"@biomejs/biome": "1.8.3",
"husky": "^9.0.11",
"husky": "^9.1.6",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@types/node": "^18.17.0"

View File

@@ -1,23 +1,23 @@
// import { sql } from "drizzle-orm";
// // Credits to Louistiti from Drizzle Discord: https://discord.com/channels/1043890932593987624/1130802621750448160/1143083373535973406
// import { drizzle } from "drizzle-orm/postgres-js";
// import postgres from "postgres";
import { sql } from "drizzle-orm";
// Credits to Louistiti from Drizzle Discord: https://discord.com/channels/1043890932593987624/1130802621750448160/1143083373535973406
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
// const connectionString = process.env.DATABASE_URL || "";
const connectionString = process.env.DATABASE_URL || "";
// const pg = postgres(connectionString, { max: 1 });
// const db = drizzle(pg);
const pg = postgres(connectionString, { max: 1 });
const db = drizzle(pg);
// const clearDb = async (): Promise<void> => {
// try {
// const tablesQuery = sql<string>`DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP schema drizzle CASCADE;`;
// const tables = await db.execute(tablesQuery);
// console.log(tables);
// await pg.end();
// } catch (error) {
// console.error("Error to clean database", error);
// } finally {
// }
// };
const clearDb = async (): Promise<void> => {
try {
const tablesQuery = sql<string>`DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP schema drizzle CASCADE;`;
const tables = await db.execute(tablesQuery);
console.log(tables);
await pg.end();
} catch (error) {
console.error("Error to clean database", error);
} finally {
}
};
// clearDb();
clearDb();

View File

@@ -1,6 +1,7 @@
import { IS_CLOUD, paths } from "@/server/constants";
import { findAdmin, updateAdmin } from "@/server/services/admin";
import { updateAdmin } from "@/server/services/admin";
import { type RotatingFileStream, createStream } from "rotating-file-stream";
import { db } from "../../db";
import { execAsync } from "../process/execAsync";
class LogRotationManager {
@@ -29,13 +30,17 @@ class LogRotationManager {
}
private async getStateFromDB(): Promise<boolean> {
const setting = await findAdmin();
const setting = await db.query.admins.findFirst({});
return setting?.enableLogRotation ?? false;
}
private async setStateInDB(active: boolean): Promise<void> {
const admin = await findAdmin();
await updateAdmin(admin.authId, {
const admin = await db.query.admins.findFirst({});
if (!admin) {
return;
}
await updateAdmin(admin?.authId, {
enableLogRotation: active,
});
}

View File

@@ -29,7 +29,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
mongo.serverId,
appName,
);
const mongoDumpCommand = `docker exec ${containerId} sh -c "mongodump -d '${database}' -u '${databaseUser}' -p '${databasePassword}' --authenticationDatabase=admin --gzip"`;
const mongoDumpCommand = `docker exec ${containerId} sh -c "mongodump -d '${database}' -u '${databaseUser}' -p '${databasePassword}' --archive --authenticationDatabase=admin --gzip"`;
await execAsyncRemote(
mongo.serverId,
@@ -37,7 +37,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
);
} else {
const { Id: containerId } = await getServiceContainer(appName);
const mongoDumpCommand = `docker exec ${containerId} sh -c "mongodump -d '${database}' -u '${databaseUser}' -p '${databasePassword}' --authenticationDatabase=admin --gzip"`;
const mongoDumpCommand = `docker exec ${containerId} sh -c "mongodump -d '${database}' -u '${databaseUser}' -p '${databasePassword}' --archive --authenticationDatabase=admin --gzip"`;
await execAsync(`${mongoDumpCommand} | ${rcloneCommand}`);
}

View File

@@ -61,6 +61,7 @@ export const cloneBitbucketRepository = async (
bitbucketBranch!,
"--depth",
"1",
"--recurse-submodules",
cloneUrl,
outputPath,
"--progress",
@@ -111,6 +112,7 @@ export const cloneRawBitbucketRepository = async (entity: Compose) => {
bitbucketBranch!,
"--depth",
"1",
"--recurse-submodules",
cloneUrl,
outputPath,
"--progress",
@@ -153,7 +155,7 @@ export const cloneRawBitbucketRepositoryRemote = async (compose: Compose) => {
try {
const command = `
rm -rf ${outputPath};
git clone --branch ${bitbucketBranch} --depth 1 ${cloneUrl} ${outputPath}
git clone --branch ${bitbucketBranch} --depth 1 --recurse-submodules ${cloneUrl} ${outputPath}
`;
await execAsyncRemote(serverId, command);
} catch (error) {
@@ -206,7 +208,7 @@ export const getBitbucketCloneCommand = async (
const cloneCommand = `
rm -rf ${outputPath};
mkdir -p ${outputPath};
if ! git clone --branch ${bitbucketBranch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
if ! git clone --branch ${bitbucketBranch} --depth 1 --recurse-submodules --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
echo "❌ [ERROR] Fail to clone the repository ${repoclone}" >> ${logPath};
exit 1;
fi

View File

@@ -2,7 +2,7 @@ import { createWriteStream } from "node:fs";
import path, { join } from "node:path";
import { paths } from "@/server/constants";
import type { Compose } from "@/server/services/compose";
import { updateSSHKeyById } from "@/server/services/ssh-key";
import { findSSHKeyById, updateSSHKeyById } from "@/server/services/ssh-key";
import { TRPCError } from "@trpc/server";
import { recreateDirectory } from "../filesystem/directory";
import { execAsync, execAsyncRemote } from "../process/execAsync";
@@ -29,13 +29,29 @@ export const cloneGitRepository = async (
}
const writeStream = createWriteStream(logPath, { flags: "a" });
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
const temporalKeyPath = path.join("/tmp", "id_rsa");
if (customGitSSHKeyId) {
const sshKey = await findSSHKeyById(customGitSSHKeyId);
await execAsync(`
echo "${sshKey.privateKey}" > ${temporalKeyPath}
chmod 600 ${temporalKeyPath}
`);
}
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code");
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
try {
if (!isHttpOrHttps(customGitUrl)) {
if (!customGitSSHKeyId) {
throw new TRPCError({
code: "BAD_REQUEST",
message:
"Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key",
});
}
await addHostToKnownHosts(customGitUrl);
}
await recreateDirectory(outputPath);
@@ -74,7 +90,7 @@ export const cloneGitRepository = async (
env: {
...process.env,
...(customGitSSHKeyId && {
GIT_SSH_COMMAND: `ssh -i ${keyPath} -o UserKnownHostsFile=${knownHostsPath}`,
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`,
}),
},
},
@@ -122,7 +138,6 @@ export const getCustomGitCloneCommand = async (
});
}
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code");
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
@@ -136,6 +151,13 @@ export const getCustomGitCloneCommand = async (
try {
const command = [];
if (!isHttpOrHttps(customGitUrl)) {
if (!customGitSSHKeyId) {
command.push(
`echo "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key ❌" >> ${logPath};
exit 1;
`,
);
}
command.push(addHostToKnownHostsCommand(customGitUrl));
}
command.push(`rm -rf ${outputPath};`);
@@ -144,13 +166,19 @@ export const getCustomGitCloneCommand = async (
`echo "Cloning Custom Git ${customGitUrl}" to ${outputPath}: ✅ >> ${logPath};`,
);
if (customGitSSHKeyId) {
const sshKey = await findSSHKeyById(customGitSSHKeyId);
const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`;
command.push(
`GIT_SSH_COMMAND="ssh -i ${keyPath} -o UserKnownHostsFile=${knownHostsPath}"`,
`
echo "${sshKey.privateKey}" > /tmp/id_rsa
chmod 600 /tmp/id_rsa
export GIT_SSH_COMMAND="${gitSshCommand}"
`,
);
}
command.push(
`if ! git clone --branch ${customGitBranch} --depth 1 --progress ${customGitUrl} ${outputPath} >> ${logPath} 2>&1; then
`if ! git clone --branch ${customGitBranch} --depth 1 --recurse-submodules --progress ${customGitUrl} ${outputPath} >> ${logPath} 2>&1; then
echo "❌ [ERROR] Fail to clone the repository ${customGitUrl}" >> ${logPath};
exit 1;
fi
@@ -184,7 +212,7 @@ const addHostToKnownHosts = async (repositoryURL: string) => {
};
const addHostToKnownHostsCommand = (repositoryURL: string) => {
const { SSH_PATH } = paths();
const { SSH_PATH } = paths(true);
const { domain, port } = sanitizeRepoPathSSH(repositoryURL);
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
@@ -242,13 +270,31 @@ export const cloneGitRawRepository = async (entity: {
}
const { SSH_PATH, COMPOSE_PATH } = paths();
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
const temporalKeyPath = path.join("/tmp", "id_rsa");
const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code");
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
if (customGitSSHKeyId) {
const sshKey = await findSSHKeyById(customGitSSHKeyId);
await execAsync(`
echo "${sshKey.privateKey}" > ${temporalKeyPath}
chmod 600 ${temporalKeyPath}
`);
}
try {
await addHostToKnownHosts(customGitUrl);
if (!isHttpOrHttps(customGitUrl)) {
if (!customGitSSHKeyId) {
throw new TRPCError({
code: "BAD_REQUEST",
message:
"Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key",
});
}
await addHostToKnownHosts(customGitUrl);
}
await recreateDirectory(outputPath);
if (customGitSSHKeyId) {
@@ -266,6 +312,7 @@ export const cloneGitRawRepository = async (entity: {
customGitBranch,
"--depth",
"1",
"--recurse-submodules",
customGitUrl,
outputPath,
"--progress",
@@ -275,7 +322,7 @@ export const cloneGitRawRepository = async (entity: {
env: {
...process.env,
...(customGitSSHKeyId && {
GIT_SSH_COMMAND: `ssh -i ${keyPath} -o UserKnownHostsFile=${knownHostsPath}`,
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`,
}),
},
},
@@ -308,7 +355,6 @@ export const cloneRawGitRepositoryRemote = async (compose: Compose) => {
}
const { SSH_PATH, COMPOSE_PATH } = paths(true);
const keyPath = path.join(SSH_PATH, `${customGitSSHKeyId}_rsa`);
const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code");
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
@@ -322,18 +368,31 @@ export const cloneRawGitRepositoryRemote = async (compose: Compose) => {
try {
const command = [];
if (!isHttpOrHttps(customGitUrl)) {
if (!customGitSSHKeyId) {
command.push(
`echo "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key ❌" ;
exit 1;
`,
);
}
command.push(addHostToKnownHostsCommand(customGitUrl));
}
command.push(`rm -rf ${outputPath};`);
command.push(`mkdir -p ${outputPath};`);
if (customGitSSHKeyId) {
const sshKey = await findSSHKeyById(customGitSSHKeyId);
const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`;
command.push(
`GIT_SSH_COMMAND="ssh -i ${keyPath} -o UserKnownHostsFile=${knownHostsPath}"`,
`
echo "${sshKey.privateKey}" > /tmp/id_rsa
chmod 600 /tmp/id_rsa
export GIT_SSH_COMMAND="${gitSshCommand}"
`,
);
}
command.push(
`if ! git clone --branch ${customGitBranch} --depth 1 --progress ${customGitUrl} ${outputPath} ; then
`if ! git clone --branch ${customGitBranch} --depth 1 --recurse-submodules --progress ${customGitUrl} ${outputPath} ; then
echo "[ERROR] Fail to clone the repository ";
exit 1;
fi

View File

@@ -125,6 +125,7 @@ export const cloneGithubRepository = async (
branch!,
"--depth",
"1",
"--recurse-submodules",
cloneUrl,
outputPath,
"--progress",
@@ -204,7 +205,7 @@ export const getGithubCloneCommand = async (
const cloneCommand = `
rm -rf ${outputPath};
mkdir -p ${outputPath};
if ! git clone --branch ${branch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
if ! git clone --branch ${branch} --depth 1 --recurse-submodules --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
echo "❌ [ERROR] Fallo al clonar el repositorio ${repoclone}" >> ${logPath};
exit 1;
fi
@@ -239,6 +240,7 @@ export const cloneRawGithubRepository = async (entity: Compose) => {
branch!,
"--depth",
"1",
"--recurse-submodules",
cloneUrl,
outputPath,
"--progress",

View File

@@ -137,6 +137,7 @@ export const cloneGitlabRepository = async (
gitlabBranch!,
"--depth",
"1",
"--recurse-submodules",
cloneUrl,
outputPath,
"--progress",
@@ -225,7 +226,7 @@ export const getGitlabCloneCommand = async (
const cloneCommand = `
rm -rf ${outputPath};
mkdir -p ${outputPath};
if ! git clone --branch ${gitlabBranch} --depth 1 --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
if ! git clone --branch ${gitlabBranch} --depth 1 --recurse-submodules --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
echo "❌ [ERROR] Fail to clone the repository ${repoclone}" >> ${logPath};
exit 1;
fi
@@ -361,6 +362,7 @@ export const cloneRawGitlabRepository = async (entity: Compose) => {
gitlabBranch!,
"--depth",
"1",
"--recurse-submodules",
cloneUrl,
outputPath,
"--progress",
@@ -395,7 +397,7 @@ export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => {
try {
const command = `
rm -rf ${outputPath};
git clone --branch ${branch} --depth 1 ${cloneUrl} ${outputPath}
git clone --branch ${branch} --depth 1 --recurse-submodules ${cloneUrl} ${outputPath}
`;
await execAsyncRemote(serverId, command);
} catch (error) {

13
pnpm-lock.yaml generated
View File

@@ -31,8 +31,8 @@ importers:
specifier: 0.20.2
version: 0.20.2
husky:
specifier: ^9.0.11
version: 9.1.3
specifier: ^9.1.6
version: 9.1.6
lint-staged:
specifier: ^15.2.7
version: 15.2.7
@@ -520,6 +520,9 @@ importers:
'@radix-ui/react-slot':
specifier: ^1.0.2
version: 1.1.0(@types/react@18.3.5)(react@18.2.0)
'@radix-ui/react-switch':
specifier: ^1.0.3
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@types/node':
specifier: 20.4.6
version: 20.4.6
@@ -5501,8 +5504,8 @@ packages:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
husky@9.1.3:
resolution: {integrity: sha512-ET3TQmQgdIu0pt+jKkpo5oGyg/4MQZpG6xcam5J5JyNJV+CBT23OBpCF15bKHKycRyMH9k6ONy8g2HdGIsSkMQ==}
husky@9.1.6:
resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==}
engines: {node: '>=18'}
hasBin: true
@@ -13813,7 +13816,7 @@ snapshots:
human-signals@5.0.0: {}
husky@9.1.3: {}
husky@9.1.6: {}
hyperdyperid@1.2.0: {}