Polski
Zacznij
← Wszystkie szablony

Wdróż aplikację Windmill na Pethost jednym kliknięciem

Skrypty, flow i aplikacje w Pythonie, TypeScripcie, Go, SQL lub Bashu, uruchamiane przez workery według harmonogramu, webhookiem albo przyciskiem

Co się dzieje

  1. Logujesz się do Pethost albo zakładasz konto, kluczem dostępu, przez GitHub albo Google.
  2. Nie masz jeszcze maszyny? Wybierasz plan, a my ją dla ciebie przygotowujemy. Link czeka w twojej przeglądarce, aż będzie gotowa.
  3. Okno „Nowy projekt” otwiera się na tej aplikacji. Wypełniasz to, o co prosi, i klikasz „Utwórz projekt”: E-mail, Hasło administratora.
  4. Za minutę czy dwie aplikacja działa pod własnym adresem z HTTPS. Jej sekrety są tworzone losowo, a dane trafiają do nocnego backupu.
  5. To projekt jak każdy inny: jego compose.yaml, .env, logi i terminal są w panelu, a twój agent może go zmienić, cofnąć do wcześniejszej wersji albo przywrócić.

Pierwsze kroki

Zaloguj się adresem e-mail i hasłem administratora, które podano: Windmill tworzy tego superadmina przy pierwszym starcie. Utwórz workspace, a potem skrypt albo flow; jego pakiety instalują się przy pierwszym uruchomieniu.

Co uruchamia

Szablon to ten compose.yaml i zmienne, które zapisuje do .env projektu. Pethost uruchamia plik dokładnie tak, jak jest napisany.

services:
  windmill_server:
    image: ghcr.io/windmill-labs/windmill:1.835.0
    restart: unless-stopped
    environment:
      DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db/windmill?sslmode=disable
      MODE: server
      BASE_URL: ${URL}
      ADMIN_EMAIL: ${ADMIN_EMAIL}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
    # A new Windmill signs anyone in as [email protected] / changeme. So, before it answers on its
    # own port, it is started on one nobody reaches, the email and password asked are made its
    # superadmin, and the stock account is deleted.
    command:
      - sh
      - -c
      - |
        PORT=8001 windmill &
        setup=$$!
        node -e '
          const base = "http://127.0.0.1:8001/api";
          const json = { "Content-Type": "application/json" };
          const login = (email, password) => fetch(base + "/auth/login", { method: "POST", headers: json, body: JSON.stringify({ email, password }) });
          const replaceStock = async (token) => {
            const auth = { ...json, Authorization: "Bearer " + token };
            const account = { email: process.env.ADMIN_EMAIL, password: process.env.ADMIN_PASSWORD, super_admin: true, name: "Admin" };
            await fetch(base + "/users/create", { method: "POST", headers: auth, body: JSON.stringify(account) });
            const mine = await login(account.email, account.password);
            if (!mine.ok) throw new Error("the new superadmin cannot sign in: " + (await mine.text()));
            const gone = await fetch(base + "/users/delete/[email protected]", { method: "DELETE", headers: { Authorization: "Bearer " + (await mine.text()) } });
            if (!gone.ok) throw new Error("the stock account stays: " + (await gone.text()));
          };
          const run = async () => {
            for (let i = 0; i < 300; i++) {
              try {
                const stock = await login("[email protected]", "changeme");
                if (stock.status === 400 || stock.status === 401) return;
                if (stock.ok) return await replaceStock(await stock.text());
              } catch (error) {
                if (String(error).includes("Error:")) console.error(String(error));
              }
              await new Promise((done) => setTimeout(done, 1000));
            }
            process.exit(1);
          };
          run().catch((error) => { console.error(String(error)); process.exit(1); });
        ' || exit 1
        kill $$setup
        wait $$setup
        exec windmill
    volumes:
      - worker_logs:/tmp/windmill/logs
    depends_on:
      db:
        condition: service_healthy

  windmill_worker:
    image: ghcr.io/windmill-labs/windmill:1.835.0
    restart: unless-stopped
    environment:
      DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db/windmill?sslmode=disable
      MODE: worker
      WORKER_GROUP: default
    volumes:
      - worker_dependency_cache:/tmp/windmill/cache
      - worker_logs:/tmp/windmill/logs
    depends_on:
      db:
        condition: service_healthy

  # Runs the light "native" jobs (HTTP and SQL steps) in process.
  windmill_worker_native:
    image: ghcr.io/windmill-labs/windmill:1.835.0
    restart: unless-stopped
    environment:
      DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db/windmill?sslmode=disable
      MODE: worker
      WORKER_GROUP: native
      NATIVE_MODE: "true"
      SLEEP_QUEUE: "200"
    volumes:
      - worker_logs:/tmp/windmill/logs
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:18
    restart: unless-stopped
    shm_size: 1g
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: windmill
    volumes:
      - db_data:/var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 10

volumes:
  db_data:
  worker_dependency_cache:
  worker_logs: