diff --git a/resources/views/livewire/boarding/index.blade.php b/resources/views/livewire/boarding/index.blade.php index d55438989..1db47f0fb 100644 --- a/resources/views/livewire/boarding/index.blade.php +++ b/resources/views/livewire/boarding/index.blade.php @@ -3,293 +3,143 @@ Onboarding | Last Hour Cloud
-
+
@if ($currentState === 'welcome') -

Welcome to your Last Hour Cloud

-

Let's help you to set up the basics and show you around.

+

Welcome to Last Hour Cloud

+
Let's help you get set up.
- Let's Start + Get + Started
- @endif -
-

Welcome to your Last Hour Cloud

-

Let's help you to set up the basics and show you around.

-
- Let's Start - -
- @endif -
-
- @if ($currentState === 'explanation') - + @elseif ($currentState === 'explanation') + - Last Hour Cloud is an all-in-one application to automate tasks on your servers, deploy applications with Git - integrations, deploy databases and services, monitor these resources with notifications and alerts + Last Hour Cloud is an all-in-one application to automate tasks on your servers, deploy application with + Git + integrations, deploy databases and services, monitor these resources with notifications and + alerts without vendor lock-in - and much much more. + and more.

- +
-

You do not to manage your servers too much. This does - it for you.

-

All configurations are stored on your server, so - everything works without this (except integrations and automations).

-

You will get notified on your favourite platform (Discord, - Telegram, Email, etc.) when something goes wrong, or if an action is needed from your side.

+

+ You don't need to manage your servers anymore. + Last Hour Cloud does + it for you. +

+

+ All configurations are stored on your servers, so + everything works without a connection to Last Hour Cloud (except integrations and automations). +

+

+ You can get notified on your favourite platforms + (Discord, + Telegram, Email, etc.) when something goes wrong, or an action is needed from your side. +

- Next + Next
- @endif - @if ($currentState === 'select-server-type') + @elseif ($currentState === 'select-server-type') - Ready to deploy apps on your ? + Do you want to deploy your resources to your + + or to a + ? - Remote Server + Localhost - @if (!$serverReachable) - Localhost is not reachable with the following public key. -

- Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user - 'root' or skip the guided tour and add a new private key manually to Last Hour Cloud and to the - server. -
- Check the upstream documentation for further help. - - Check again - - @endif -
- -

Servers are the main building blocks, as they will host your applications, databases, - services, called resources. Any CPU intensive process will use the server's CPU where you - are deploying your resources.

-

Localhost is the server where Last Hour Cloud is running on. It is not recommended to use one server - for everything.

-

Remote Server is a server reachable through SSH. It can be hosted at home, or from any cloud - provider.

-
-
- @endif -
-
- @if ($currentState === 'private-key') - - - Do you have your own SSH Private Key? - - - Yes - - No (create one for me) - - @if (count($privateKeys) > 0) -
- - @foreach ($privateKeys as $privateKey) - - @endforeach - - Use this SSH Key -
- @endif -
- -

SSH Keys are used to connect to a remote server through a secure shell, called SSH.

-

You can use your own ssh private key, or you can allow Last Hour Cloud to create one for you.

-

In both ways, you need to add the public version of your ssh private key to the remote - server's - ~/.ssh/authorized_keys file. -

-
-
- @endif -
-
- @if ($currentState === 'select-existing-server') - - - There are already servers available for your Team. Do you want to use one of them? - - -
-
- No (create one - for - me) - -
-
-
- - @foreach ($servers as $server) - - @endforeach - - Use this Server -
-
-
- @if (!$serverReachable) - This server is not reachable with the following public key. -

- Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user - 'root' or skip the boarding process and add a new private key manually to Last Hour Cloud and to the - server. - - Check - again - - @endif -
- -

Private Keys are used to connect to a remote server through a secure shell, called SSH.

-

You can use your own private key, or you can let Last Hour Cloud create one for you.

-

In both ways, you need to add the public version of your private key to the remote server's - ~/.ssh/authorized_keys file. -

-
-
- @endif -
-
- @if ($currentState === 'create-private-key') - - - Please let me know your key details. - - -
- - - - @if ($privateKeyType === 'create') - - ACTION REQUIRED: Copy the 'Public Key' to your server's - ~/.ssh/authorized_keys - file. - @endif - Save - -
- -

Private Keys are used to connect to a remote server through a secure shell, called SSH.

-

You can use your own private key, or you can let Last Hour Cloud create one for you.

-

In both ways, you need to add the public version of your private key to the remote server's - ~/.ssh/authorized_keys file. -

-
-
- @endif -
-
- @if ($currentState === 'create-server') - - - Please let us know your server details. - - -
-
- - -
-
- - - -
-
- -
- Continue -
-
- -

Username should be for now. We are working on using - non-root users.

-
-
- @endif -
-
- @if ($currentState === 'validate-server') - - - we need to validate your server (connection, Docker Engine, etc) and configure to see if something is - missing. Are you okay with this? - - - - Validate & configure - - - - - Send it! - - - - @endif - @if ($currentState === 'select-server-type') - - - Ready to deploy apps on your ? - - - Remote Server + Remote Server + @if (!$serverReachable) - Localhost is not reachable with the following public key. -

- Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user - 'root' or skip the guided tour and add a new private key manually to Last Hour Cloud and to the - server. -
- Check the upstream documentation for further help. - - Check again - +
+

Server is not reachable

+

Please check the connection details below and correct them if they are + incorrect.

+ +
+ +
+ +

+ Non-root user is experimental: + See upstream docs +

+
+
+ +
+

If the connection details are correct, please ensure:

+
    +
  • The correct public key is in your ~/.ssh/authorized_keys + file for the specified user
  • +
  • Or skip the boarding process and manually add a new private key to Last Hour Cloud and + the server
  • +
+
+ +

+ For more help, check this upstream documentation. +

+ + + + + Check Again + +
@endif

Servers are the main building blocks, as they will host your applications, databases, services, called resources. Any CPU intensive process will use the server's CPU where you are deploying your resources.

-

Localhost is the server where Last Hour Cloud is running on. It is not recommended to use one server - for everything.

-

Remote Server is a server reachable through SSH. It can be hosted at home, or from any cloud - provider.

+

+ is the server where Last Hour Cloud is running on. It is not + recommended to use one server + for everything. +

+

+ is a server reachable through SSH. It can be hosted + at home, or from any cloud + provider. +

- @endif -
-
- @if ($currentState === 'private-key') + @elseif ($currentState === 'private-key') Do you have your own SSH Private Key? - Yes + Yes - No (create one for me) + No (create one for me) @if (count($privateKeys) > 0) -
+ @foreach ($privateKeys as $privateKey)
-
- @if ($currentState === 'select-existing-server') + @elseif ($currentState === 'select-existing-server') There are already servers available for your Team. Do you want to use one of them? @@ -321,9 +168,8 @@
- No (create one - for - me) + No + (create one for me)
@@ -340,41 +186,75 @@
@if (!$serverReachable) - This server is not reachable with the following public key. -

- Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user - 'root' or skip the boarding process and add a new private key manually to Last Hour Cloud and to the - server. - - Check - again - +
+

Server is not reachable

+

Please check the connection details below and correct them if they are + incorrect.

+ +
+ +
+ +

+ Non-root user is experimental: + upstream docs +

+
+
+ +
+

If the connection details are correct, please ensure:

+
    +
  • The correct public key is in your ~/.ssh/authorized_keys + file for the specified user
  • +
  • Or skip the boarding process and manually add a new private key to Last Hour Cloud and + the server
  • +
+
+ +

+ For more help, check this upstream documentation. +

+ + + + + Check again + +
@endif

Private Keys are used to connect to a remote server through a secure shell, called SSH.

-

You can use your own private key, or you can let Last Hour Cloud create one for you.

+

You can use your own private key, or you can let Last Hour Cloud to create one for you.

In both ways, you need to add the public version of your private key to the remote server's ~/.ssh/authorized_keys file.

- @endif -
-
- @if ($currentState === 'create-private-key') + @elseif ($currentState === 'create-private-key') Please let me know your key details. - - - - + + + + @if ($privateKeyType === 'create') - ACTION REQUIRED: Copy the 'Public Key' to your server's + ACTION REQUIRED: Copy the 'Public Key' to your + server's ~/.ssh/authorized_keys file. @endif @@ -383,50 +263,62 @@

Private Keys are used to connect to a remote server through a secure shell, called SSH.

-

You can use your own private key, or you can let Last Hour Cloud create one for you.

+

You can use your own private key, or you can let Last Hour Cloud to create one for you.

In both ways, you need to add the public version of your private key to the remote server's ~/.ssh/authorized_keys file.

- @endif -
-
- @if ($currentState === 'create-server') - + @elseif ($currentState === 'create-server') + - Please let us know your server details. + Please let me know your server details. - -
- - + +
+ +
-
- - - +
+
-
- +
+ +
+ +
+ +
Non-root user is + experimental: upstream docs. +
+
+
+
+
+
Continue - -

Username should be for now. We are working on using - non-root users.

-
- @endif -
-
- @if ($currentState === 'validate-server') + @elseif ($currentState === 'validate-server') - we need to validate your server (connection, Docker Engine, etc) and configure to see if something is - missing. Are you okay with this? + I need to validate your server (connection, Docker Engine, etc) and configure if something is + missing for me. Are you okay with this? @@ -434,65 +326,39 @@ - - Send it! + + Let's do it!

This will install the latest Docker Engine on your server, configure a few things to be able - to run optimal.

Minimum Docker Engine version is: 22

To manually install Docker - Engine, check this + to run optimal.

Minimum Docker Engine version is: 22

To manually install + Docker + Engine, check
this documentation.

- @endif -
- {{--
- @if ($currentState === 'select-proxy') - - - If you would like to attach any kind of domain to your resources, you need a proxy. - - - - Decide later - - - Traefik - v2 - - - Nginx - - - Caddy - - - -

This will install the latest Docker Engine on your server and configure a few items to be able - to run optimally.

-
-
- @endif -
--}} -
- @if ($currentState === 'create-project') + @elseif ($currentState === 'create-project') @if (count($projects) > 0) - You already have some projects. Do you want to use one of them or should we create a new one for + You already have some projects. Do you want to use one of them or should I create a new one + for you? @else - We will create an initial project for you. You can change it later on. + Let's create an initial project for you. You can change all the details later on. @endif - Create New Project + Create new + project!
@if (count($projects) > 0)
- + @foreach ($projects as $project)
-

Projects put together several resources into one virtual group. There are no - limitations on the number of projects you can have here.

-

Each project should have at least one environment. This helps you to create a production & +

Projects contain several resources combined into one virtual group. There are no + limitations on the number of projects you can add.

+

Each project should have at least one environment, this allows you to create a production & staging version of the same application, but grouped separately.

- @endif -
-
- @if ($currentState === 'create-resource') + @elseif ($currentState === 'create-resource') - Next we will redirect you to the resource page, where you can create your first resource. + Let's go to the new resource page, where you can create your first resource. -
Let's do +
Let's do it!
-

A resource is an application, a database or a service (like WordPress).

+

A resource could be an application, a database or a service (like WordPress).

@endif
-
- Skip Guided Tour - Restart Guided Tour +
+
+
Skip + onboarding
+
Restart + onboarding
+
+ + +
+ Feedback +
+
+ +
-
\ No newline at end of file +
\ No newline at end of file diff --git a/scripts/cloud_upgrade.sh b/scripts/cloud_upgrade.sh index 8bab73b98..6654c0ac2 100644 --- a/scripts/cloud_upgrade.sh +++ b/scripts/cloud_upgrade.sh @@ -2,7 +2,7 @@ set -e export IMAGE=$1 docker system prune -af docker compose pull -read -p "Press Enter to update Coolify to $IMAGE..." last_version diff --git a/scripts/dev_install.sh b/scripts/dev_install.sh deleted file mode 100644 index d6cc254c2..000000000 --- a/scripts/dev_install.sh +++ /dev/null @@ -1,283 +0,0 @@ -#!/bin/bash - -set -e # Exit immediately if a command exits with a non-zero status -## $1 could be empty, so we need to disable this check -#set -u # Treat unset variables as an error and exit -set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status - -VERSION="1.2.2" -DOCKER_VERSION="24.0" - -CDN="https://cdn.lasthourhosting.org/lasthourcloudprod" -OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"') - -if [ "$OS_TYPE" = "arch" ]; then - OS_VERSION="rolling" -else - OS_VERSION=$(grep -w "VERSION_ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"') -fi - -LATEST_VERSION=$(wget -q -O - $CDN/versions.json | grep -i version | sed -n '2p' | xargs | awk '{print $2}' | tr -d ',') -DATE=$(date +"%Y%m%d-%H%M%S") - -if [ $EUID != 0 ]; then - echo "Please run as root" - exit -fi - -case "$OS_TYPE" in -arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux) ;; -*) - echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now." - exit - ;; -esac - -# Overwrite LATEST_VERSION if user pass a version number -if [ "$1" != "" ]; then - LATEST_VERSION=$1 - LATEST_VERSION="${LATEST_VERSION,,}" - LATEST_VERSION="${LATEST_VERSION#v}" -fi - -echo -e "-------------" -echo -e "Welcome to Last Hour Cloud v4 development installer!" -echo -e "This script will install everything for you." -echo -e "(Source code: https://githaven.org/Shiloh/lasthourcloud/src/branch/main/scripts/dev_install.sh)\n" -echo -e "-------------" - -echo "OS: $OS_TYPE $OS_VERSION" -echo "Last Hour Cloud version: $LATEST_VERSION" - -echo -e "-------------" -echo "Installing required packages..." - -case "$OS_TYPE" in -arch) - pacman -Sy >/dev/null 2>&1 || true - if ! pacman -Q curl wget git jq >/dev/null 2>&1; then - pacman -S --noconfirm curl wget git jq >/dev/null 2>&1 || true - fi - ;; -ubuntu | debian | raspbian) - apt update -y >/dev/null 2>&1 - apt install -y curl wget git jq >/dev/null 2>&1 - ;; -centos | fedora | rhel | ol | rocky | almalinux) - dnf install -y curl wget git jq >/dev/null 2>&1 - ;; -sles | opensuse-leap | opensuse-tumbleweed) - zypper refresh >/dev/null 2>&1 - zypper install -y curl wget git jq >/dev/null 2>&1 - ;; -*) - echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now." - exit - ;; -esac - -# Detect OpenSSH server -SSH_DETECTED=false -if [ -x "$(command -v systemctl)" ]; then - if systemctl status sshd >/dev/null 2>&1; then - echo "OpenSSH server is installed." - SSH_DETECTED=true - fi - if systemctl status ssh >/dev/null 2>&1; then - echo "OpenSSH server is installed." - SSH_DETECTED=true - fi -elif [ -x "$(command -v service)" ]; then - if service sshd status >/dev/null 2>&1; then - echo "OpenSSH server is installed." - SSH_DETECTED=true - fi - if service ssh status >/dev/null 2>&1; then - echo "OpenSSH server is installed." - SSH_DETECTED=true - fi -fi -if [ "$SSH_DETECTED" = "false" ]; then - echo "###############################################################################" - echo "WARNING: Could not detect if OpenSSH server is installed and running - this does not mean that it is not installed, just that we could not detect it." - echo -e "Please make sure it is set, otherwise Last Hour Cloud cannot connect to the host system. \n" - echo "###############################################################################" -fi - -# Detect SSH PermitRootLogin -SSH_PERMIT_ROOT_LOGIN=false -SSH_PERMIT_ROOT_LOGIN_CONFIG=$(grep "^PermitRootLogin" /etc/ssh/sshd_config | awk '{print $2}') || SSH_PERMIT_ROOT_LOGIN_CONFIG="N/A (commented out or not found at all)" -if [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "prohibit-password" ] || [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "yes" ] || [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "without-password" ]; then - echo "PermitRootLogin is enabled." - SSH_PERMIT_ROOT_LOGIN=true -fi - -if [ "$SSH_PERMIT_ROOT_LOGIN" != "true" ]; then - echo "###############################################################################" - echo "WARNING: PermitRootLogin is not enabled in /etc/ssh/sshd_config." - echo -e "It is set to $SSH_PERMIT_ROOT_LOGIN_CONFIG. Should be prohibit-password, yes or without-password.\n" - echo -e "Please make sure it is set, otherwise Last Hour Cloud cannot connect to the host system. \n" - echo "(Currently we only support root user to login via SSH, this will be changed in the future.)" - echo "###############################################################################" -fi - -if ! [ -x "$(command -v docker)" ]; then - if [ "$OS_TYPE" == 'almalinux' ]; then - dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo - dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin - if ! [ -x "$(command -v docker)" ]; then - echo "Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." - exit 1 - fi - systemctl start docker - systemctl enable docker - else - set +e - if ! [ -x "$(command -v docker)" ]; then - echo "Docker is not installed. Installing Docker." - if [ "$OS_TYPE" = "arch" ]; then - pacman -Sy docker docker-compose --noconfirm - systemctl enable docker.service - if [ -x "$(command -v docker)" ]; then - echo "Docker installed successfully." - else - echo "Failed to install Docker with pacman. Try to install it manually." - echo "Please visit https://wiki.archlinux.org/title/docker for more information." - exit - fi - else - curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh - if [ -x "$(command -v docker)" ]; then - echo "Docker installed successfully." - else - echo "Docker installation failed with Rancher script. Trying with official script." - curl https://get.docker.com | sh -s -- --version ${DOCKER_VERSION} - if [ -x "$(command -v docker)" ]; then - echo "Docker installed successfully." - else - echo "Docker installation failed with official script." - echo "Maybe your OS is not supported?" - echo "Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." - exit 1 - fi - fi - fi - fi - set -e - fi -fi - -echo -e "-------------" -echo -e "Check Docker Configuration..." -mkdir -p /etc/docker -# shellcheck disable=SC2015 -test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json /etc/docker/daemon.json.original-"$DATE" || cat >/etc/docker/daemon.json </etc/docker/daemon.json.coolify <"$TEMP_FILE"; then - echo "Error merging JSON files" - exit 1 -fi -mv "$TEMP_FILE" /etc/docker/daemon.json - -if [ -s /etc/docker/daemon.json.original-"$DATE" ]; then - DIFF=$(diff <(jq --sort-keys . /etc/docker/daemon.json) <(jq --sort-keys . /etc/docker/daemon.json.original-"$DATE")) - if [ "$DIFF" != "" ]; then - echo "Docker configuration updated, restart docker daemon..." - systemctl restart docker - else - echo "Docker configuration is up to date." - fi -else - echo "Docker configuration updated, restart docker daemon..." - systemctl restart docker -fi - -echo -e "-------------" - -mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy,webhooks-during-maintenance} -mkdir -p /data/coolify/ssh/{keys,mux} -mkdir -p /data/coolify/proxy/dynamic - -# echo "Downloading required files from CDN..." -# curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml -# curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml -# curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production -# curl -fsSL $CDN/scripts/upgrade.sh -o /data/coolify/source/upgrade.sh - -echo "Copying required files from Last Hour Cloud git repo..." -cp /home/lasthour/lasthourcloud/docker-compose.yml /data/coolify/source/docker-compose.yml -cp /home/lasthour/lasthourcloud/docker-compose.dev.yml /data/coolify/source/docker-compose.dev.yml -cp /home/lasthour/lasthourcloud/.env.production /data/coolify/source/.env.production -cp /home/lasthour/lasthourcloud/scripts/dev_upgrade.sh /data/coolify/source/dev_upgrade.sh - -chown -R 9999:root /data/coolify -chmod -R 700 /data/coolify - -# Copy .env.example if .env does not exist -if [ ! -f /data/coolify/source/.env ]; then - cp /data/coolify/source/.env.production /data/coolify/source/.env - sed -i "s|APP_ID=.*|APP_ID=$(openssl rand -hex 16)|g" /data/coolify/source/.env - sed -i "s|APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|g" /data/coolify/source/.env - sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env - sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env - sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env - sed -i "s|PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|g" /data/coolify/source/.env - sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env -fi - -# Merge .env and .env.production. New values will be added to .env -sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env - -if [ "$AUTOUPDATE" = "false" ]; then - if ! grep -q "AUTOUPDATE=" /data/coolify/source/.env; then - echo "AUTOUPDATE=false" >>/data/coolify/source/.env - else - sed -i "s|AUTOUPDATE=.*|AUTOUPDATE=false|g" /data/coolify/source/.env - fi -fi - -# Generate an ssh key (ed25519) at /data/coolify/ssh/keys/id.root@host.docker.internal -if [ ! -f /data/coolify/ssh/keys/id.root@host.docker.internal ]; then - ssh-keygen -t ed25519 -a 100 -f /data/coolify/ssh/keys/id.root@host.docker.internal -q -N "" -C root@coolify - chown 9999 /data/coolify/ssh/keys/id.root@host.docker.internal -fi - -addSshKey() { - cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >>~/.ssh/authorized_keys - chmod 600 ~/.ssh/authorized_keys -} - -if [ ! -f ~/.ssh/authorized_keys ]; then - mkdir -p ~/.ssh - chmod 700 ~/.ssh - touch ~/.ssh/authorized_keys - addSshKey -fi - -if ! grep -qw "root@coolify" ~/.ssh/authorized_keys; then - addSshKey -fi -echo "Generated SSH access" - -echo "Begin dev_upgrade.sh" -bash /data/coolify/source/dev_upgrade.sh "${LATEST_VERSION:-latest}" - -echo -e "\nCongratulations! Your Last Hour Cloud instance is ready to use.\n" -echo "Please visit http://$(curl -4s https://ifconfig.io):8000 to get started." \ No newline at end of file diff --git a/scripts/dev_upgrade.sh b/scripts/dev_upgrade.sh deleted file mode 100644 index 5ba45dbe8..000000000 --- a/scripts/dev_upgrade.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -## Do not modify this file. You will lose the ability to autoupdate! - -VERSION="1.0.4" -CDN="https://cdn.lasthourhosting.org/lasthourcloudprod" - -# curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml -# curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml -# curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production - -echo "Copying required files from Last Hour Cloud git repo..." -cp /home/lasthour/lasthourcloud/docker-compose.yml /data/coolify/source/docker-compose.yml -cp /home/lasthour/lasthourcloud/docker-compose.dev.yml /data/coolify/source/docker-compose.dev.yml -cp /home/lasthour/lasthourcloud/.env.production /data/coolify/source/.env.production - -# Merge .env and .env.production. New values will be added to .env -sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env - -# Check if PUSHER_APP_ID or PUSHER_APP_KEY or PUSHER_APP_SECRET is empty in /data/coolify/source/.env -if grep -q "PUSHER_APP_ID=$" /data/coolify/source/.env; then - sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env -fi - -if grep -q "PUSHER_APP_KEY=$" /data/coolify/source/.env; then - sed -i "s|PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|g" /data/coolify/source/.env -fi - -if grep -q "PUSHER_APP_SECRET=$" /data/coolify/source/.env; then - sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env -fi - -# Make sure coolify network exists -docker network create --attachable coolify 2>/dev/null -# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null - -if [ -f /data/coolify/source/docker-compose.custom.yml ]; then - echo "docker-compose.custom.yml detected." - docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.dev.yml -f /data/coolify/source/docker-compose.custom.yml up -d --pull always --remove-orphans --force-recreate" -else - docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.dev.yml up -d --pull always --remove-orphans --force-recreate" -fi diff --git a/scripts/install.sh b/scripts/install.sh index 356d54242..304184900 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,15 +1,36 @@ #!/bin/bash +## Do not modify this file. You will lose the ability to install and auto-update! set -e # Exit immediately if a command exits with a non-zero status ## $1 could be empty, so we need to disable this check #set -u # Treat unset variables as an error and exit set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status +CDN="https://cdn.lasthourhosting.org/lasthourcloudprod" +DATE=$(date +"%Y%m%d-%H%M%S") -VERSION="1.3.3" +VERSION="1.5" DOCKER_VERSION="26.0" -CDN="https://cdn.lasthourhosting.org/lasthourcloudprod" +mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy,webhooks-during-maintenance,metrics,logs} +mkdir -p /data/coolify/ssh/{keys,mux} +mkdir -p /data/coolify/proxy/dynamic + +chown -R 9999:root /data/coolify +chmod -R 700 /data/coolify + +INSTALLATION_LOG_WITH_DATE="/data/coolify/source/installation-${DATE}.log" + +exec > >(tee -a $INSTALLATION_LOG_WITH_DATE) 2>&1 + +getAJoke() { + JOKES=$(curl -s --max-time 2 https://v2.jokeapi.dev/joke/Programming?format=txt&type=single&amount=1 || true) + if [ "$JOKES" != "" ]; then + echo -e " - Until then, here's a joke for you:\n" + echo -e "$JOKES\n" + fi +} OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"') +ENV_FILE="/data/coolify/source/.env" # Check if the OS is manjaro, if so, change it to arch if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then @@ -43,7 +64,17 @@ if [ "$OS_TYPE" = 'amzn' ]; then fi LATEST_VERSION=$(curl --silent $CDN/versions.json | grep -i version | xargs | awk '{print $2}' | tr -d ',') -DATE=$(date +"%Y%m%d-%H%M%S") +LATEST_HELPER_VERSION=$(curl --silent $CDN/versions.json | grep -i version | xargs | awk '{print $6}' | tr -d ',') +LATEST_REALTIME_VERSION=$(curl --silent $CDN/versions.json | grep -i version | xargs | awk '{print $8}' | tr -d ',') + +if [ -z "$LATEST_HELPER_VERSION" ]; then + LATEST_HELPER_VERSION=latest +fi + +if [ -z "$LATEST_REALTIME_VERSION" ]; then + LATEST_REALTIME_VERSION=latest +fi + if [ $EUID != 0 ]; then echo "Please run as root" @@ -51,9 +82,9 @@ if [ $EUID != 0 ]; then fi case "$OS_TYPE" in -arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn) ;; +arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn | alpine) ;; *) - echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now." + echo "This script only supports Debian, Redhat, Arch Linux, Alpine Linux, or SLES based operating systems for now." exit ;; esac @@ -65,33 +96,41 @@ if [ "$1" != "" ]; then LATEST_VERSION="${LATEST_VERSION#v}" fi -echo -e "-------------" -echo -e "Welcome to Last Hour Cloud v4 installer!" -echo -e "This script will install everything for you." -<<<<<<< HEAD -<<<<<<< HEAD -echo -e "(Source code: https://github.com/coollabsio/coolify/blob/main/scripts/install.sh )\n" -======= -echo -e "(Source code: https://https://githaven.org/Shiloh/lasthourcloud/blob/main/scripts/install.sh)\n" ->>>>>>> 35700ec24 (main: begin major rewrite for lasthour) -======= -echo -e "(Source code: https://githaven.org/Shiloh/lasthourcloud/src/branch/main/scripts/install.sh)\n" ->>>>>>> 4e89beaf1 (main: fix CDN url) -echo -e "-------------" +echo -e "\033[0;35m" +cat << "EOF" -echo "OS: $OS_TYPE $OS_VERSION" -echo "Last Hour Cloud version: $LATEST_VERSION" - -echo -e "-------------" -echo "Installing required packages..." + ██ ███████ ███████ ██ ██ ███████ ██ ███████ ██ ██ ██ ███ ██ ██████ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ + ██ █████ ███████ ██ ██ ███████ ██ ███████ █████ ██ ██ ██ ██ ██ ███ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + █████ ███████ ███████ ██████ ███████ ██ ███████ ██ ██ ██ ██ ████ ██████ + +EOF +echo -e "\033[0m" +echo -e "Welcome to Last Hour Cloud Installer!" +echo -e "This script will install everything for you. Sit back and relax." +echo -e "Source code: https://githaven.org/Shiloh/lasthourcloud/src/branch/prod/scripts/install.sh\n" +echo -e "---------------------------------------------" +echo "| Operating System | $OS_TYPE $OS_VERSION" +echo "| Docker | $DOCKER_VERSION" +echo "| Last Hour Cloud | $LATEST_VERSION" +echo "| Helper | $LATEST_HELPER_VERSION" +echo "| Realtime | $LATEST_REALTIME_VERSION" +echo -e "---------------------------------------------\n" +echo -e "1. Installing required packages (curl, wget, git, jq). " case "$OS_TYPE" in arch) pacman -Sy --noconfirm --needed curl wget git jq >/dev/null || true ;; +alpine) + sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories + apk update >/dev/null + apk add curl wget git jq >/dev/null + ;; ubuntu | debian | raspbian) - apt update -y >/dev/null - apt install -y curl wget git jq >/dev/null + apt-get update -y >/dev/null + apt-get install -y curl wget git jq >/dev/null ;; centos | fedora | rhel | ol | rocky | almalinux | amzn) if [ "$OS_TYPE" = "amzn" ]; then @@ -100,7 +139,10 @@ centos | fedora | rhel | ol | rocky | almalinux | amzn) if ! command -v dnf >/dev/null; then yum install -y dnf >/dev/null fi - dnf install -y curl wget git jq >/dev/null + if ! command -v curl >/dev/null; then + dnf install -y curl >/dev/null + fi + dnf install -y wget git jq >/dev/null fi ;; sles | opensuse-leap | opensuse-tumbleweed) @@ -113,24 +155,26 @@ sles | opensuse-leap | opensuse-tumbleweed) ;; esac + + +echo -e "2. Check OpenSSH server configuration. " + # Detect OpenSSH server SSH_DETECTED=false if [ -x "$(command -v systemctl)" ]; then if systemctl status sshd >/dev/null 2>&1; then - echo "OpenSSH server is installed." + echo " - OpenSSH server is installed." SSH_DETECTED=true - fi - if systemctl status ssh >/dev/null 2>&1; then - echo "OpenSSH server is installed." + elif systemctl status ssh >/dev/null 2>&1; then + echo " - OpenSSH server is installed." SSH_DETECTED=true fi elif [ -x "$(command -v service)" ]; then if service sshd status >/dev/null 2>&1; then - echo "OpenSSH server is installed." + echo " - OpenSSH server is installed." SSH_DETECTED=true - fi - if service ssh status >/dev/null 2>&1; then - echo "OpenSSH server is installed." + elif service ssh status >/dev/null 2>&1; then + echo " - OpenSSH server is installed." SSH_DETECTED=true fi fi @@ -142,105 +186,91 @@ if [ "$SSH_DETECTED" = "false" ]; then fi # Detect SSH PermitRootLogin -SSH_PERMIT_ROOT_LOGIN=false -SSH_PERMIT_ROOT_LOGIN_CONFIG=$(grep "^PermitRootLogin" /etc/ssh/sshd_config | awk '{print $2}') || SSH_PERMIT_ROOT_LOGIN_CONFIG="N/A (commented out or not found at all)" -if [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "prohibit-password" ] || [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "yes" ] || [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "without-password" ]; then - echo "PermitRootLogin is enabled." - SSH_PERMIT_ROOT_LOGIN=true -fi - -if [ "$SSH_PERMIT_ROOT_LOGIN" != "true" ]; then - echo "###############################################################################" - echo "WARNING: PermitRootLogin is not enabled in /etc/ssh/sshd_config." - echo -e "It is set to $SSH_PERMIT_ROOT_LOGIN_CONFIG. Should be prohibit-password, yes or without-password.\n" -<<<<<<< HEAD - echo -e "Please make sure it is set, otherwise Coolify cannot connect to the host system. \n" -======= - echo -e "Please make sure it is set, otherwise Last Hour Cloud cannot connect to the host system. \n" - echo "(Currently we only support root user to login via SSH, this will be changed in the future.)" ->>>>>>> 35700ec24 (main: begin major rewrite for lasthour) - echo "###############################################################################" +SSH_PERMIT_ROOT_LOGIN=$(sshd -T | grep -i "permitrootlogin" | awk '{print $2}') || true +if [ "$SSH_PERMIT_ROOT_LOGIN" = "yes" ] || [ "$SSH_PERMIT_ROOT_LOGIN" = "without-password" ] || [ "$SSH_PERMIT_ROOT_LOGIN" = "prohibit-password" ]; then + echo " - SSH PermitRootLogin is enabled." +else + echo " - SSH PermitRootLogin is disabled." + echo " If you have problems with SSH, please read this upstream documentation: https://coolify.io/docs/knowledge-base/server/openssh" fi # Detect if docker is installed via snap if [ -x "$(command -v snap)" ]; then - if snap list | grep -q docker; then - echo "Docker is installed via snap." - echo "Please note that Coolify does not support Docker installed via snap." - echo "Please remove Docker with snap (snap remove docker) and reexecute this script." + SNAP_DOCKER_INSTALLED=$(snap list docker >/dev/null 2>&1 && echo "true" || echo "false") + if [ "$SNAP_DOCKER_INSTALLED" = "true" ]; then + echo " - Docker is installed via snap." + echo " Please note that Last Hour Cloud does not support Docker installed via snap." + echo " Please remove Docker with snap (snap remove docker) and reexecute this script." exit 1 fi fi +echo -e "3. Check Docker Installation. " if ! [ -x "$(command -v docker)" ]; then - # Almalinux - if [ "$OS_TYPE" == 'almalinux' ]; then - dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo - dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin - if ! [ -x "$(command -v docker)" ]; then - echo "Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." - exit 1 - fi - systemctl start docker - systemctl enable docker - else - set +e - if ! [ -x "$(command -v docker)" ]; then - echo "Docker is not installed. Installing Docker." - # Arch Linux - if [ "$OS_TYPE" = "arch" ]; then - pacman -Sy docker docker-compose --noconfirm - systemctl enable docker.service - if [ -x "$(command -v docker)" ]; then - echo "Docker installed successfully." - else - echo "Failed to install Docker with pacman. Try to install it manually." - echo "Please visit https://wiki.archlinux.org/title/docker for more information." - exit - fi - else - # Amazon Linux 2023 - if [ "$OS_TYPE" = "amzn" ]; then - dnf install docker -y - DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker} - mkdir -p $DOCKER_CONFIG/cli-plugins - curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o $DOCKER_CONFIG/cli-plugins/docker-compose - chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose - systemctl start docker - systemctl enable docker - if [ -x "$(command -v docker)" ]; then - echo "Docker installed successfully." - else - echo "Failed to install Docker with pacman. Try to install it manually." - echo "Please visit https://wiki.archlinux.org/title/docker for more information." - exit - fi - else - # Automated Docker installation - curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh - if [ -x "$(command -v docker)" ]; then - echo "Docker installed successfully." - else - echo "Docker installation failed with Rancher script. Trying with official script." - curl https://get.docker.com | sh -s -- --version ${DOCKER_VERSION} - if [ -x "$(command -v docker)" ]; then - echo "Docker installed successfully." - else - echo "Docker installation failed with official script." - echo "Maybe your OS is not supported?" - echo "Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." - exit 1 - fi - fi + echo " - Docker is not installed. Installing Docker. It may take a while." + getAJoke + case "$OS_TYPE" in + "almalinux") + dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo >/dev/null 2>&1 + dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1 + if ! [ -x "$(command -v docker)" ]; then + echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." + exit 1 + fi + systemctl start docker >/dev/null 2>&1 + systemctl enable docker >/dev/null 2>&1 + ;; + "alpine") + apk add docker docker-cli-compose >/dev/null 2>&1 + rc-update add docker default >/dev/null 2>&1 + service docker start >/dev/null 2>&1 + if ! [ -x "$(command -v docker)" ]; then + echo " - Failed to install Docker with apk. Try to install it manually." + echo " Please visit https://wiki.alpinelinux.org/wiki/Docker for more information." + exit 1 + fi + ;; + "arch") + pacman -Sy docker docker-compose --noconfirm >/dev/null 2>&1 + systemctl enable docker.service >/dev/null 2>&1 + if ! [ -x "$(command -v docker)" ]; then + echo " - Failed to install Docker with pacman. Try to install it manually." + echo " Please visit https://wiki.archlinux.org/title/docker for more information." + exit 1 + fi + ;; + "amzn") + dnf install docker -y >/dev/null 2>&1 + DOCKER_CONFIG=${DOCKER_CONFIG:-/usr/local/lib/docker} + mkdir -p $DOCKER_CONFIG/cli-plugins >/dev/null 2>&1 + curl -sL https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o $DOCKER_CONFIG/cli-plugins/docker-compose >/dev/null 2>&1 + chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose >/dev/null 2>&1 + systemctl start docker >/dev/null 2>&1 + systemctl enable docker >/dev/null 2>&1 + if ! [ -x "$(command -v docker)" ]; then + echo " - Failed to install Docker with dnf. Try to install it manually." + echo " Please visit https://www.cyberciti.biz/faq/how-to-install-docker-on-amazon-linux-2/ for more information." + exit 1 + fi + ;; + *) + curl -s https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh >/dev/null 2>&1 + if ! [ -x "$(command -v docker)" ]; then + curl -s https://get.docker.com | sh -s -- --version ${DOCKER_VERSION} >/dev/null 2>&1 + if ! [ -x "$(command -v docker)" ]; then + echo " - Docker installation failed." + echo " Maybe your OS is not supported?" + echo " - Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." + exit 1 fi fi - fi - set -e - fi + esac + echo " - Docker installed successfully." +else + echo " - Docker is installed." fi -echo -e "-------------" -echo -e "Check Docker Configuration..." +echo -e "4. Check Docker Configuration. " mkdir -p /etc/docker # shellcheck disable=SC2015 test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json /etc/docker/daemon.json.original-"$DATE" || cat >/etc/docker/daemon.json </dev/null 2>&1; then + echo " - Using systemctl to restart Docker." + systemctl restart docker + + if [ $? -eq 0 ]; then + echo " - Docker restarted successfully using systemctl." + else + echo " - Failed to restart Docker using systemctl." + return 1 + fi + + # Check if service command is available + elif command -v service >/dev/null 2>&1; then + echo " - Using service command to restart Docker." + service docker restart + + if [ $? -eq 0 ]; then + echo " - Docker restarted successfully using service." + else + echo " - Failed to restart Docker using service." + return 1 + fi + + # If neither systemctl nor service is available + else + echo " - Neither systemctl nor service command is available on this system." + return 1 + fi +} + if [ -s /etc/docker/daemon.json.original-"$DATE" ]; then DIFF=$(diff <(jq --sort-keys . /etc/docker/daemon.json) <(jq --sort-keys . /etc/docker/daemon.json.original-"$DATE")) if [ "$DIFF" != "" ]; then - echo "Docker configuration updated, restart docker daemon..." - systemctl restart docker + echo " - Docker configuration updated, restart docker daemon..." + restart_docker_service else - echo "Docker configuration is up to date." + echo " - Docker configuration is up to date." fi else - echo "Docker configuration updated, restart docker daemon..." - systemctl restart docker + echo " - Docker configuration updated, restart docker daemon..." + restart_docker_service fi -echo -e "-------------" - -mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy,webhooks-during-maintenance,metrics,logs} -mkdir -p /data/coolify/ssh/{keys,mux} -mkdir -p /data/coolify/proxy/dynamic - -echo "Downloading required files from CDN..." +echo -e "5. Download required files from CDN. " curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production -curl -fsSL $CDN/scripts/upgrade.sh -o /data/coolify/source/upgrade.sh +curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh -# echo "Copying required files from Last Hour Cloud git repo..." -# cp /home/lasthour/lasthourcloud/docker-compose.yml /data/coolify/source/docker-compose.yml -# cp /home/lasthour/lasthourcloud/docker-compose.prod.yml /data/coolify/source/docker-compose.prod.yml -# cp /home/lasthour/lasthourcloud/.env.production /data/coolify/source/.env.production -# cp /home/lasthour/lasthourcloud/scripts/upgrade.sh /data/coolify/source/upgrade.sh - - -chown -R 9999:root /data/coolify -chmod -R 700 /data/coolify +echo -e "6. Make backup of .env to .env-$DATE" # Copy .env.example if .env does not exist -if [ ! -f /data/coolify/source/.env ]; then - cp /data/coolify/source/.env.production /data/coolify/source/.env - sed -i "s|APP_ID=.*|APP_ID=$(openssl rand -hex 16)|g" /data/coolify/source/.env - sed -i "s|APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|g" /data/coolify/source/.env - sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env - sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env - sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env - sed -i "s|PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|g" /data/coolify/source/.env - sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env +if [ -f $ENV_FILE ]; then + cp $ENV_FILE $ENV_FILE-$DATE +else + echo " - File does not exist: $ENV_FILE" + echo " - Copying .env.production to .env-$DATE" + cp /data/coolify/source/.env.production $ENV_FILE-$DATE + # Generate a secure APP_ID and APP_KEY + sed -i "s|^APP_ID=.*|APP_ID=$(openssl rand -hex 16)|" "$ENV_FILE-$DATE" + sed -i "s|^APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|" "$ENV_FILE-$DATE" + + # Generate a secure Postgres DB username and password + # Causes issues: database "random-user" does not exist + # sed -i "s|^DB_USERNAME=.*|DB_USERNAME=$(openssl rand -hex 16)|" "$ENV_FILE-$DATE" + sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE-$DATE" + + # Generate a secure Redis password + sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE-$DATE" + + # Generate secure Pusher credentials + sed -i "s|^PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE" + sed -i "s|^PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE" + sed -i "s|^PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|" "$ENV_FILE-$DATE" fi # Merge .env and .env.production. New values will be added to .env -sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env +echo -e "7. Propagating .env with new values - if necessary." +awk -F '=' '!seen[$1]++' "$ENV_FILE-$DATE" /data/coolify/source/.env.production > $ENV_FILE if [ "$AUTOUPDATE" = "false" ]; then if ! grep -q "AUTOUPDATE=" /data/coolify/source/.env; then @@ -325,32 +387,122 @@ if [ "$AUTOUPDATE" = "false" ]; then sed -i "s|AUTOUPDATE=.*|AUTOUPDATE=false|g" /data/coolify/source/.env fi fi - -# Generate an ssh key (ed25519) at /data/coolify/ssh/keys/id.root@host.docker.internal -if [ ! -f /data/coolify/ssh/keys/id.root@host.docker.internal ]; then - ssh-keygen -t ed25519 -a 100 -f /data/coolify/ssh/keys/id.root@host.docker.internal -q -N "" -C root@coolify - chown 9999 /data/coolify/ssh/keys/id.root@host.docker.internal -fi - -addSshKey() { - cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >>~/.ssh/authorized_keys - chmod 600 ~/.ssh/authorized_keys -} - +echo -e "8. Checking for SSH key for localhost access." if [ ! -f ~/.ssh/authorized_keys ]; then mkdir -p ~/.ssh chmod 700 ~/.ssh touch ~/.ssh/authorized_keys - addSshKey + chmod 600 ~/.ssh/authorized_keys fi -if ! grep -qw "root@coolify" ~/.ssh/authorized_keys; then - addSshKey -fi -echo "Generated SSH access" +checkSshKeyInAuthorizedKeys() { + grep -qw "root@coolify" ~/.ssh/authorized_keys + return $? +} -echo "Begin upgrade.sh" -bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" +checkSshKeyInCoolifyData() { + [ -s /data/coolify/ssh/keys/id.root@host.docker.internal ] + return $? +} -echo -e "\nCongratulations! Your Last Hour Cloud instance is ready to use.\n" -echo "Please visit http://$(curl -4s https://ifconfig.io):8000 to get started." \ No newline at end of file +generateAuthorizedKeys() { + sed -i "/root@coolify/d" ~/.ssh/authorized_keys + cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys + rm -f /data/coolify/ssh/keys/id.root@host.docker.internal.pub +} +generateSshKey() { + echo " - Generating SSH key." + ssh-keygen -t ed25519 -a 100 -f /data/coolify/ssh/keys/id.root@host.docker.internal -q -N "" -C root@coolify + chown 9999 /data/coolify/ssh/keys/id.root@host.docker.internal + generateAuthorizedKeys +} + +syncSshKeys() { + DB_RUNNING=$(docker inspect coolify-db --format '{{ .State.Status }}' 2>/dev/null) + # Check if SSH key exists in Coolify data but not in authorized_keys + if checkSshKeyInCoolifyData && ! checkSshKeyInAuthorizedKeys; then + # Add the existing Coolify SSH key to authorized_keys + cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys + # Check if SSH key exists in authorized_keys but not in Coolify data + elif checkSshKeyInAuthorizedKeys && ! checkSshKeyInCoolifyData; then + # Ensure Coolify DB is running before proceeding + if [ "$DB_RUNNING" = "running" ]; then + # Retrieve DB user and SSH key from Coolify database + DB_USER=$(docker inspect coolify-db --format '{{ .Config.Env }}' | grep -oP 'POSTGRES_USER=\K[^ ]+') + DB_SSH_KEY=$(docker exec coolify-db psql -U $DB_USER -d coolify -t -c "SELECT \"private_key\" FROM \"private_keys\" WHERE id = 0 AND team_id = 0 LIMIT 1;" -A -t) + + if [ -z "$DB_SSH_KEY" ]; then + # If no key found in DB, generate a new one + echo " - SSH key not found in database. Generating new key." + generateSshKey + else + # If key found in DB, save it and update authorized_keys + echo " - SSH key found in database. Saving to file." + echo "$DB_SSH_KEY" > /data/coolify/ssh/keys/id.root@host.docker.internal + chmod 600 /data/coolify/ssh/keys/id.root@host.docker.internal + chown 9999 /data/coolify/ssh/keys/id.root@host.docker.internal + + # Generate public key from private key and update authorized_keys + ssh-keygen -y -f /data/coolify/ssh/keys/id.root@host.docker.internal -C root@coolify > /data/coolify/ssh/keys/id.root@host.docker.internal.pub + sed -i "/root@coolify/d" ~/.ssh/authorized_keys + cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys + rm -f /data/coolify/ssh/keys/id.root@host.docker.internal.pub + chmod 600 ~/.ssh/authorized_keys + fi + fi + # If SSH key doesn't exist in either location + elif ! checkSshKeyInAuthorizedKeys && ! checkSshKeyInCoolifyData; then + # Ensure Coolify DB is running before proceeding + if [ "$DB_RUNNING" = "running" ]; then + # Retrieve DB user and SSH key from Coolify database + DB_USER=$(docker inspect coolify-db --format '{{ .Config.Env }}' | grep -oP 'POSTGRES_USER=\K[^ ]+') + DB_SSH_KEY=$(docker exec coolify-db psql -U $DB_USER -d coolify -t -c "SELECT \"private_key\" FROM \"private_keys\" WHERE id = 0 AND team_id = 0 LIMIT 1;" -A -t) + if [ -z "$DB_SSH_KEY" ]; then + # If no key found in DB, generate a new one + echo " - SSH key not found in database. Generating new key." + generateSshKey + else + # If key found in DB, save it and update authorized_keys + echo " - SSH key found in database. Saving to file." + echo "$DB_SSH_KEY" > /data/coolify/ssh/keys/id.root@host.docker.internal + chmod 600 /data/coolify/ssh/keys/id.root@host.docker.internal + ssh-keygen -y -f /data/coolify/ssh/keys/id.root@host.docker.internal -C root@coolify > /data/coolify/ssh/keys/id.root@host.docker.internal.pub + sed -i "/root@coolify/d" ~/.ssh/authorized_keys + cat /data/coolify/ssh/keys/id.root@host.docker.internal.pub >> ~/.ssh/authorized_keys + fi + else + generateSshKey + fi + fi +} + +syncSshKeys || true + +chown -R 9999:root /data/coolify +chmod -R 700 /data/coolify + +echo -e "9. Installing Coolify ($LATEST_VERSION)" +echo -e " - It could take a while based on your server's performance, network speed, stars, etc." +echo -e " - Please wait." +getAJoke + +bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}" >/dev/null 2>&1 +echo " - Last Hour Cloud installed successfully." +rm -f $ENV_FILE-$DATE + +echo " - Waiting for 20 seconds for Last Hour Cloud (database migrations) to be ready." +getAJoke + +sleep 20 +echo -e "\033[0;35m + ____ _ _ _ _ _ + / ___|___ _ __ __ _ _ __ __ _| |_ _ _| | __ _| |_(_) ___ _ __ ___| | + | | / _ \| '_ \ / _\` | '__/ _\` | __| | | | |/ _\` | __| |/ _ \| '_ \/ __| | + | |__| (_) | | | | (_| | | | (_| | |_| |_| | | (_| | |_| | (_) | | | \__ \_| + \____\___/|_| |_|\__, |_| \__,_|\__|\__,_|_|\__,_|\__|_|\___/|_| |_|___(_) + |___/ +\033[0m" +echo -e "\nYour instance is ready to use." +echo -e "Please visit http://$(curl -4s https://ifconfig.io):8000 to get started.\n" +echo -e "WARNING: We recommend you to backup your /data/coolify/source/.env file to a safe location, outside of this server." +cp /data/coolify/source/.env /data/coolify/source/.env.backup \ No newline at end of file diff --git a/scripts/run b/scripts/run index a8b5af968..3c5b435fd 100755 --- a/scripts/run +++ b/scripts/run @@ -1,104 +1,56 @@ -#!/usr/bin/env bash +#!/bin/bash +# Sync docker volumes between two servers -# Inspired on https://github.com/adriancooney/Taskfile -# -# Install an alias, to be able to simply execute `run` -# echo 'alias run=./scripts/run' >> ~/.aliases -# -# Define Docker Compose command prefix... +VERSION="1.0.0" +SOURCE=$1 +DESTINATION=$2 set -e - -if [ $? == 0 ]; then - DOCKER_COMPOSE="docker compose" -else - DOCKER_COMPOSE="docker-compose" +if [ -z "$SOURCE" ]; then + echo "Source server is not specified." + exit 1 +fi +if [ -z "$DESTINATION" ]; then + echo "Destination server is not specified." + exit 1 fi -function help { - echo "$0 " - echo "Tasks:" - compgen -A function | cat -n -} +SOURCE_USER=$(echo $SOURCE | cut -d@ -f1) +SOURCE_SERVER=$(echo $SOURCE | cut -d: -f1 | cut -d@ -f2) +SOURCE_PORT=$(echo $SOURCE | cut -d: -f2 | cut -d/ -f1) +SOURCE_VOLUME_NAME=$(echo $SOURCE | cut -d/ -f2) -# function sync:v3 { -# if [ -z "$1" ]; then -# echo -e "Please provide a version.\n\nExample: run sync:v3 3.12.32" -# exit 1 -# fi -# skopeo copy --all docker://ghcr.io/coollabsio/coolify:$1 docker://coollabsio/coolify:$1 -# } -function sync:bunny { - php artisan sync:bunny --env=secrets -} +if ! [[ "$SOURCE_PORT" =~ ^[0-9]+$ ]]; then + echo "Invalid source port: $SOURCE_PORT" + exit 1 +fi -# function queue { -# bash spin exec -u webuser coolify php artisan queue:listen -# } +DESTINATION_USER=$(echo $DESTINATION | cut -d@ -f1) +DESTINATION_SERVER=$(echo $DESTINATION | cut -d: -f1 | cut -d@ -f2) +DESTINATION_PORT=$(echo $DESTINATION | cut -d: -f2 | cut -d/ -f1) +DESTINATION_VOLUME_NAME=$(echo $DESTINATION | cut -d/ -f2) -# function horizon { -# bash spin exec -u webuser coolify php artisan horizon -vvv -# } +if ! [[ "$DESTINATION_PORT" =~ ^[0-9]+$ ]]; then + echo "Invalid destination port: $DESTINATION_PORT" + exit 1 +fi -# function schedule { -# bash spin exec -u webuser coolify php artisan schedule:work -# } +echo "Generating backup file to ./$SOURCE_VOLUME_NAME.tgz" +ssh -p $SOURCE_PORT $SOURCE_USER@$SOURCE_SERVER "docker run -v $SOURCE_VOLUME_NAME:/volume --rm --log-driver none loomchild/volume-backup backup -c pigz -v" >./$SOURCE_VOLUME_NAME.tgz +echo "" +if [ -f "./$SOURCE_VOLUME_NAME.tgz" ]; then + echo "Uploading backup file to $DESTINATION_SERVER:~/$DESTINATION_VOLUME_NAME.tgz" + scp -P $DESTINATION_PORT ./$SOURCE_VOLUME_NAME.tgz $DESTINATION_USER@$DESTINATION_SERVER:~/$DESTINATION_VOLUME_NAME.tgz + echo "" + echo "Restoring backup file on remote ($DESTINATION_SERVER:/~/$DESTINATION_VOLUME_NAME.tgz)" + ssh -p $DESTINATION_PORT $DESTINATION_USER@$DESTINATION_SERVER "docker run -i -v $DESTINATION_VOLUME_NAME:/volume --log-driver none --rm loomchild/volume-backup restore -c pigz -vf < ~/$DESTINATION_VOLUME_NAME.tgz" + echo "" + echo "Deleting backup file on remote ($DESTINATION_SERVER:/~/$DESTINATION_VOLUME_NAME.tgz)" + ssh -p $DESTINATION_PORT $DESTINATION_USER@$DESTINATION_SERVER "rm ~/$DESTINATION_VOLUME_NAME.tgz" -# function schedule:run { -# bash spin exec -u webuser coolify php artisan schedule:run -# } + echo "" + echo "Local file ./$SOURCE_VOLUME_NAME.tgz is not deleted." -# function db { -# bash spin exec -u webuser coolify php artisan db -# } -# function db:seed { -# bash spin exec -u webuser coolify php artisan migrate --seed -# } - -# function db:migrate { -# bash spin exec -u webuser coolify php artisan migrate --step -# } - -function db:reset { - bash spin exec -u webuser coolify php artisan migrate:fresh --seed -} -function db:reset-prod { - bash spin exec -u webuser coolify php artisan migrate:fresh --force --seed --seeder=ProductionSeeder || - php artisan migrate:fresh --force --seed --seeder=ProductionSeeder -} - -function mfs { - db:reset -} - -function coolify { - bash spin exec -u webuser coolify bash -} - -function coolify:root { - bash spin exec coolify bash -} -function coolify:proxy { - docker exec -ti coolify-proxy sh -} - -function redis { - docker exec -ti coolify-redis redis-cli -} - -function vite { - bash spin exec vite bash -} - -function tinker { - bash spin exec -u webuser coolify php artisan tinker -} - -# function build:helper { -# act -W .github/workflows/coolify-helper.yml --secret-file .env.secrets -# } -function default { - help -} - -TIMEFORMAT="Task completed in %3lR" -time "${@:-default}" + echo "" + echo "WARNING: If you are copying a database volume, you need to set the right users/passwords on the destination service's environment variables." + echo "Why? Because we are copying the volume as-is, so the database credentials will bethe same as on the source volume." +fi diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index e0f925f22..b08c25402 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -1,23 +1,17 @@ #!/bin/bash ## Do not modify this file. You will lose the ability to autoupdate! -VERSION="1.0.5" +VERSION="1.1" CDN="https://cdn.lasthourhosting.org/lasthourcloudprod" +LATEST_IMAGE=${1:-latest} +LATEST_HELPER_VERSION=${2:-latest} curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production -# echo "Copying required files from Last Hour Cloud git repo..." -# cp /home/lasthour/lasthourcloud/docker-compose.yml /data/coolify/source/docker-compose.yml -# cp /home/lasthour/lasthourcloud/docker-compose.prod.yml /data/coolify/source/docker-compose.prod.yml -# cp /home/lasthour/lasthourcloud/.env.production /data/coolify/source/.env.production -# cp /home/lasthour/lasthourcloud/scripts/upgrade.sh /data/coolify/source/upgrade.sh - - # Merge .env and .env.production. New values will be added to .env -sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env - +awk -F '=' '!seen[$1]++' /data/coolify/source/.env /data/coolify/source/.env.production > /data/coolify/source/.env.tmp && mv /data/coolify/source/.env.tmp /data/coolify/source/.env # Check if PUSHER_APP_ID or PUSHER_APP_KEY or PUSHER_APP_SECRET is empty in /data/coolify/source/.env if grep -q "PUSHER_APP_ID=$" /data/coolify/source/.env; then sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env @@ -31,13 +25,14 @@ if grep -q "PUSHER_APP_SECRET=$" /data/coolify/source/.env; then sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env fi -# Make sure coolify network exists +# Make sure Last Hour Cloud network exists +# It is created when starting Last Hour Cloud with docker compose docker network create --attachable coolify 2>/dev/null # docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null if [ -f /data/coolify/source/docker-compose.custom.yml ]; then echo "docker-compose.custom.yml detected." - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --force-recreate" + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper:${LATEST_HELPER_VERSION:-latest} bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --force-recreate --wait --wait-timeout 60" else - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --force-recreate" -fi + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper:${LATEST_HELPER_VERSION:-latest} bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --force-recreate --wait --wait-timeout 60" +fi \ No newline at end of file