OpnForm icon indicating copy to clipboard operation
OpnForm copied to clipboard

Add EasyPanel Template for OpnForm with 1-Click Deploy Button

Open JhumanJ opened this issue 1 year ago β€’ 18 comments

Add EasyPanel Template for OpnForm with 1-Click Deploy Button

We’d love to have an EasyPanel template for OpnForm, our open-source form builder. The goal is to enable users to deploy OpnForm with a single click through EasyPanel.

Background:

OpnForm already has a working Docker setup, which you can find in our deployment documentation. The Docker configuration includes everything needed for running the application. The primary task for this issue is to adapt the Docker setup for EasyPanel and create a 1-click deploy button.

Deliverables:

  1. EasyPanel Template: A template that adapts the existing Docker setup to EasyPanel.

  2. .env File Generator Script: A script (or modifications to the current one) that generates .env files based on the EasyPanel deployment setup. Currently, we use scripts/setup-env.sh, but we likely need to adapt or create an equivalent script for EasyPanel.

  3. 1-Click Deploy Button:

    • Create a 1-click deploy button that integrates with EasyPanel and simplifies the deployment process for OpnForm users. Add mention to this new deployment option (and button) in the docs.
  4. Merge Request to EasyPanel Repository:

    • Submit the template to the EasyPanel repository following their contribution guidelines.

Resources:

Skills Required:

  • Familiarity with Docker and Docker Compose.
  • Experience with EasyPanel templates and deployment.
  • Ability to script and automate environment variable setup.

If you have any questions or need clarification on this issue, feel free to comment below! 😊

Thank you for contributing to OpnForm!

JhumanJ avatar Nov 21 '24 14:11 JhumanJ

/bounty 100

JhumanJ avatar Dec 05 '24 09:12 JhumanJ

πŸ’Ž $100 bounty β€’ OpnForm

Steps to solve:

  1. Start working: Comment /attempt #627 with your implementation plan
  2. Submit work: Create a pull request including /claim #627 in the PR body to claim the bounty
  3. Receive payment: 100% of the bounty is received 2-5 days post-reward. Make sure you are eligible for payouts

Thank you for contributing to JhumanJ/OpnForm!

Add a bounty β€’ Share on socials

Attempt Started (GMT+0) Solution
πŸ”΄ @ibishal Dec 5, 2024, 9:31:22 AM WIP

algora-pbc[bot] avatar Dec 05 '24 09:12 algora-pbc[bot]

/attempt #627 can i get assigned

Algora profile Completed bounties Tech Active attempts Options
@ibishal 2 bounties from 2 projects
TypeScript, Scala,
Rust
Cancel attempt

ibishal avatar Dec 05 '24 09:12 ibishal

Hey @ibishal are you comfortable doing this? If so then yes - please read the full issue!

JhumanJ avatar Dec 05 '24 10:12 JhumanJ

Hey @ibishal are you comfortable doing this? If so then yes - please read the full issue!

will complete this by EOD !

ibishal avatar Dec 05 '24 10:12 ibishal

Sounds good! Please note that you need to submit the template to the EasyPanel repository following their contribution guidelines, thanks!

JhumanJ avatar Dec 05 '24 10:12 JhumanJ

Opened a pr at easypanel repo(https://github.com/easypanel-io/templates/pull/634)

ibishal avatar Dec 05 '24 15:12 ibishal

Hey @ibishal thanks for this? Does it work for you? I tried on my easypanel instance to use the schema generated but it failed: back-end seem stuck on "Waiting for DB to be ready". Although front-end seem to work

Also can we automatically assign a URL to the project on creation?

JhumanJ avatar Dec 05 '24 17:12 JhumanJ

Actually i dont have any VPS so i could not test on easypanel instance but i have test similar step on docker, it works

try with this index.ts

import {
  Output,
  randomPassword,
  randomString,
  Services,
} from "~templates-utils";
import { Input } from "./meta";

export function generate(input: Input): Output {
  const services: Services = [];

  const appKey = Buffer.from(randomString(32)).toString("base64");
  const databasePassword = randomPassword();
  const jwtSecret = randomString(40);
  const sharedSecret = randomString(40);

  const apiEnvironment = [
    `APP_NAME=OpnForm`,
    `APP_ENV=production`,
    `APP_KEY=base64:${appKey}`,
    `APP_DEBUG=false`,
    `APP_URL=https://$(PRIMARY_DOMAIN)`,
    `DB_HOST=$(PROJECT_NAME)_${input.databaseServiceName}`,
    `REDIS_HOST=$(PROJECT_NAME)_${input.redisServiceName}`,
    `DB_DATABASE=$(PROJECT_NAME)`,
    `DB_USERNAME=postgres`,
    `DB_PASSWORD=${databasePassword}`,
    `DB_CONNECTION=pgsql`,
    `FILESYSTEM_DISK=local`,
    `LOCAL_FILESYSTEM_VISIBILITY=public`,
    `JWT_SECRET=${jwtSecret}`,
    `FRONT_API_SECRET=${sharedSecret}`,
  ].join("\n");

  services.push({
    type: "postgres",
    data: {
      serviceName: input.databaseServiceName,
      password: databasePassword,
      command: "",
    },
  });

  services.push({
    type: "redis",
    data: {
      serviceName: input.redisServiceName,
    },
  });

  services.push({
    type: "app",
    data: {
      serviceName: input.apiServiceName,
      env: apiEnvironment,
      source: {
        type: "image",
        image: input.apiServiceImage,
      },
      mounts: [
        {
          type: "volume",
          name: "opnform-storage",
          mountPath: "/usr/share/nginx/html/storage",
        },
      ],
    },
  });

  services.push({
    type: "app",
    data: {
      serviceName: input.workerServiceName,
      env: apiEnvironment + "\nIS_API_WORKER=true",
      source: {
        type: "image",
        image: input.apiServiceImage,
      },
      mounts: [
        {
          type: "volume",
          name: "opnform-storage",
          mountPath: "/usr/share/nginx/html/storage",
        },
      ],
    },
  });

  services.push({
    type: "app",
    data: {
      serviceName: input.uiServiceName,
      env: [
        `NUXT_PUBLIC_APP_URL=/`,
        `NUXT_PUBLIC_API_BASE=/api`,
        `NUXT_API_SECRET=${sharedSecret}`,
      ].join("\n"),
      source: {
        type: "image",
        image: input.uiServiceImage,
      },
    },
  });

  services.push({
    type: "app",
    data: {
      serviceName: input.ingressServiceName,
      source: {
        type: "image",
        image: "nginx:1",
      },
      domains: [
        {
          host: "$(EASYPANEL_DOMAIN)",
          port: 80,
        },
      ],
    },
  });

  return { services };
}

ibishal avatar Dec 05 '24 18:12 ibishal

How should I try this? I used the schema generated in this comment https://github.com/easypanel-io/templates/pull/634#issuecomment-2520553093

https://deploy-preview-634--easypanel-templates.netlify.app/opnform

JhumanJ avatar Dec 06 '24 09:12 JhumanJ

can you try with this schema

{
  "services": [
    {
      "type": "app",
      "data": {
        "serviceName": "opnform-api",
        "env": "APP_NAME=OpnForm\nAPP_ENV=production\nAPP_KEY=base64:YzRlMzU3MTdhMDVmNWQ3ZTE5M2Q5YWQyYjBlNDZmN2Q=\nAPP_DEBUG=false\nAPP_URL=https://$(PRIMARY_DOMAIN)\nDB_HOST=$(PROJECT_NAME)_opnform-db\nREDIS_HOST=$(PROJECT_NAME)_opnform-redis\nDB_DATABASE=$(PROJECT_NAME)\nDB_USERNAME=postgres\nDB_PASSWORD=ef5dbb0b00e656a677ec\nDB_CONNECTION=pgsql\nFILESYSTEM_DISK=local\nLOCAL_FILESYSTEM_VISIBILITY=public\nJWT_SECRET=a0b4810e002e9c65725f56cb15dcab14b13b544a\nFRONT_API_SECRET=bef4d24d2124b4c01f5f405c31c4da72406b7252",
        "source": {
          "type": "image",
          "image": "jhumanj/opnform-api:1.4.5"
        },
        "mounts": [
          {
            "type": "volume",
            "name": "opnform-storage",
            "mountPath": "/usr/share/nginx/html/storage"
          }
        ]
      }
    },
    {
      "type": "app",
      "data": {
        "serviceName": "opnform-ui",
        "env": "NUXT_PUBLIC_APP_URL=https://$(PRIMARY_DOMAIN)\nNUXT_PUBLIC_API_BASE=https://$(PRIMARY_DOMAIN)/api\nNUXT_API_SECRET=bef4d24d2124b4c01f5f405c31c4da72406b7252\nNUXT_PUBLIC_ENV=dev",
        "source": {
          "type": "image",
          "image": "jhumanj/opnform-client:1.4.5"
        }
      }
    },
    {
      "type": "app",
      "data": {
        "serviceName": "opnform-ingress",
        "env": "UI_HOST=$(PROJECT_NAME)_opnform-ui\nAPI_HOST=$(PROJECT_NAME)_opnform-api",
        "source": {
          "type": "image",
          "image": "nginx:1.25"
        },
        "domains": [
          {
            "host": "$(EASYPANEL_DOMAIN)",
            "port": 80
          }
        ],
        "deploy": {
          "command": "envsubst '$UI_HOST $API_HOST' < /etc/nginx/conf.d/vhost.template > /etc/nginx/conf.d/default.conf && nginx -g \"daemon off;\""
        },
        "mounts": [
          {
            "type": "file",
            "content": "\nserver {\n    listen 80;\n    server_name _;\n\n    access_log /dev/stdout;\n    error_log  /dev/stderr error;\n\n    location / {\n        proxy_pass http://${UI_HOST}:3000;\n        proxy_http_version 1.1;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-Host $host;\n        proxy_set_header X-Forwarded-Port $server_port;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"Upgrade\";\n    }\n\n    location ~/(api|open|local\\/temp|forms\\/assets)/ {\n        try_files $uri $uri/ /index.php?$query_string;\n    }\n\n    location ~ \\.php$ {\n        fastcgi_split_path_info ^(.+\\.php)(/.+)$;\n        fastcgi_pass ${API_HOST}:9000;\n        fastcgi_index index.php;\n        include fastcgi_params;\n        fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php;\n        fastcgi_param PATH_INFO $fastcgi_path_info;\n    }\n}",
            "mountPath": "/etc/nginx/conf.d/vhost.template"
          }
        ]
      }
    },
    {
      "type": "app",
      "data": {
        "serviceName": "opnform-worker",
        "env": "APP_NAME=OpnForm\nAPP_ENV=production\nAPP_KEY=base64:YzRlMzU3MTdhMDVmNWQ3ZTE5M2Q5YWQyYjBlNDZmN2Q=\nAPP_DEBUG=false\nAPP_URL=https://$(PRIMARY_DOMAIN)\nDB_HOST=$(PROJECT_NAME)_opnform-db\nREDIS_HOST=$(PROJECT_NAME)_opnform-redis\nDB_DATABASE=$(PROJECT_NAME)\nDB_USERNAME=postgres\nDB_PASSWORD=ef5dbb0b00e656a677ec\nDB_CONNECTION=pgsql\nFILESYSTEM_DISK=local\nLOCAL_FILESYSTEM_VISIBILITY=public\nJWT_SECRET=a0b4810e002e9c65725f56cb15dcab14b13b544a\nFRONT_API_SECRET=bef4d24d2124b4c01f5f405c31c4da72406b7252\nIS_API_WORKER=true",
        "source": {
          "type": "image",
          "image": "jhumanj/opnform-api:1.4.5"
        },
        "mounts": [
          {
            "type": "volume",
            "name": "opnform-storage",
            "mountPath": "/usr/share/nginx/html/storage"
          }
        ]
      }
    },
    {
      "type": "redis",
      "data": {
        "serviceName": "opnform-redis"
      }
    },
    {
      "type": "postgres",
      "data": {
        "serviceName": "opnform-db",
        "password": "ef5dbb0b00e656a677ec"
      }
    }
  ]
}

cc @JhumanJ

ibishal avatar Dec 09 '24 11:12 ibishal

Still not working unfortunately

JhumanJ avatar Dec 12 '24 09:12 JhumanJ

@ibishal: Reminder that in 7 days the bounty will become up for grabs, so please submit a pull request before then πŸ™

algora-pbc[bot] avatar Dec 12 '24 09:12 algora-pbc[bot]

I tried this template and it is solid, but there are errors:

  1. in the opnform-api service, the error "Waiting for DB to be ready" is received, this is because the DB is not initialized, and not because the variable APP_URL=https://$(PRIMARY_DOMAIN) is not good, this $(PRIMARY_DOMAIN is refers to the primary domain of the service, not the entire application, so when you enter the container it says that APP_URL=https:// To begin with, you need to manually replace and put the domain from the opnform-ingress service, https://example-domain.com, after this correction the app will start
  2. The UI is up, but there is also an error ERR_NAME_NOT_RESOLVED https://api/templates?limit=10 in the console, which indicates that the ENV values ​​in the opnform-ui service are not good. it should be instead of NUXT_PUBLIC_APP_URL=https://$(PRIMARY_DOMAIN) NUXT_PUBLIC_API_BASE=https://$(PRIMARY_DOMAIN)/api put the domain from the opnform-ingress service instead of $(PRIMARY_DOMAIN), e.g. https://example-domain.com , now the address is obtained in the console as it should be, but now I get an error of the https://example-domain.com/api/register 502 (Bad Gateway)
  3. now I assume that the /etc/nginx/conf.d/vhost.template file of the opnform-ingress service is not good, but I have not been able to get it to work, When I try to curl from the opnform-ingress container service curl http://API_HOST:9000 I get : curl: (56) Recv failure: Connection reset by peer

Wiola09 avatar Dec 12 '24 16:12 Wiola09

I have progress, now at least I got to API container, but in it I get "GET /index.php" 404 So, NGINX forwards requests. But when I include APP_DEBUG=true in the API container

and I type in the browser https://<example-domain.com>/api/fonts/ I get an error: "The route api/fonts could not be found."

As if in the API container I have an extra prefix api/ on my route?

Wiola09 avatar Dec 17 '24 06:12 Wiola09

The bounty is up for grabs! Everyone is welcome to /attempt #627 πŸ™Œ

algora-pbc[bot] avatar Dec 19 '24 09:12 algora-pbc[bot]

I have progress, now at least I got to API container, but in it I get "GET /index.php" 404 So, NGINX forwards requests. But when I include APP_DEBUG=true in the API container

and I type in the browser https://<example-domain.com>/api/fonts/ I get an error: "The route api/fonts could not be found."

As if in the API container I have an extra prefix api/ on my route?

so are you doing the above ?

ibishal avatar Dec 20 '24 05:12 ibishal

Yes, I partially described the process here. I did several things, including php artisan migrate --force in the API container to initialize the DB. I'll be able to reproduce it again, but right now I'm running into a problem that I'm getting requests in the API container from nginx from the opnform-ingress container service, but apparently I'm getting them on the wrong route. Here I need advice from someone who has a better understanding of app architecture, what's going on...

Wiola09 avatar Dec 20 '24 06:12 Wiola09

https://github.com/easypanel-io/templates/pull/956

JhumanJ avatar May 26 '25 16:05 JhumanJ