# Sources used for this configuration:
# - https://github.com/penpot/penpot/blob/2.1.0/docker/images/docker-compose.yaml
# - https://raw.githubusercontent.com/penpot/penpot/2.1.0/docker/images/docker-compose.yaml
# - https://help.penpot.app/technical-guide/configuration/
# - https://medium.com/@social.iodols/managing-docker-containers-in-nixos-fbda0f666dd1
# - https://madison-technologies.com/take-your-nixos-container-config-and-shove-it/

{ config, pkgs, ... }:

let
  # Flags for both frontend and backend.
  # https://help.penpot.app/technical-guide/configuration/#common
  # https://github.com/penpot/penpot/commit/ea7ad2aaa096f8d190d740f693f22f3ed1f05088
  commonPenpotFlags = "disable-registration enable-oidc-registration disable-login-with-password enable-login-with-oidc";
  penpotVersion = "2.1.3";
in
{
  virtualisation.docker.enable = true;
  virtualisation.oci-containers = {
    backend = "docker";
    containers = {
      "penpot-frontend" = {
        autoStart = true;
        image = "docker.io/penpotapp/frontend:${penpotVersion}";
        extraOptions = [ "--network=penpot" ];
        ports = [ "9001:80" ];
        volumes = [ "penpot_assets:/opt/data/assets" ];
        dependsOn = [
          "penpot-backend"
          "penpot-exporter"
        ];
        environment = {
          # https://help.penpot.app/technical-guide/configuration/#frontend
          # https://github.com/penpot/penpot/blob/develop/docker/images/docker-compose.yaml#L78

          PENPOT_FLAGS = "${commonPenpotFlags} disable-onboarding";
        };
      };

      "penpot-backend" = {
        autoStart = true;
        image = "docker.io/penpotapp/backend:${penpotVersion}";
        extraOptions = [ "--network=penpot" ];
        volumes = [ "penpot_assets:/opt/data/assets" ];
        dependsOn = [
          "penpot-postgres"
          "penpot-redis"
        ];
        environment = {
          # https://help.penpot.app/technical-guide/configuration/#backend
          # https://github.com/penpot/penpot/blob/develop/docker/images/docker-compose.yaml#L112

          PENPOT_FLAGS = "${commonPenpotFlags} enable-smtp";

          # PENPOT_SECRET_KEY st via environmentFile.
          PENPOT_TELEMETRY_ENABLED = "false";

          # OpenID Connect configuration.
          # https://help.penpot.app/technical-guide/configuration/#openid-connect
          PENPOT_OIDC_CLIENT_ID = "penpot";
          PENPOT_OIDC_BASE_URI = "https://id.hamburg.ccc.de/realms/ccchh/";
          # PENPOT_OIDC_CLIENT_SECRET set via environmentFile.
          PENPOT_OIDC_ROLES = "user";
          PENPOT_OIDC_ROLES_ATTR = "roles";

          # Database configuration.
          # https://help.penpot.app/technical-guide/configuration/#database
          PENPOT_DATABASE_USERNAME = "penpot";
          # PENPOT_DATABASE_PASSWORD set via environmentFile.
          PENPOT_DATABASE_URI = "postgresql://penpot-postgres/penpot";

          # Email configuration.
          # https://help.penpot.app/technical-guide/configuration/#email-(smtp)
          PENPOT_SMTP_HOST = "cow.hamburg.ccc.de";
          PENPOT_SMTP_PORT = "465";
          PENPOT_SMTP_USERNAME = "no-reply@design.hamburg.ccc.de";
          # PENPOT_SMTP_PASSWORD set via environmentFile.
          PENPOT_SMTP_SSL = "true";
          PENPOT_SMTP_DEFAULT_REPLY_TO = "Penpot <no-reply@design.hamburg.ccc.de>";
          PENPOT_SMTP_DEFAULT_FROM = "Penpot <no-reply@design.hamburg.ccc.de>";

          # Storage
          # https://help.penpot.app/technical-guide/configuration/#storage
          PENPOT_ASSETS_STORAGE_BACKEND = "assets-fs";
          PENPOT_STORAGE_ASSETS_FS_DIRECTORY = "/opt/data/assets";

          # Redis
          # https://help.penpot.app/technical-guide/configuration/#redis
          PENPOT_REDIS_URI = "redis://penpot-redis/0";

          PENPOT_PUBLIC_URI = "https://design.hamburg.ccc.de";
        };
        environmentFiles = [ "/run/secrets/penpot_backend_environment_file" ];
      };

      "penpot-exporter" = {
        autoStart = true;
        image = "docker.io/penpotapp/exporter:${penpotVersion}";
        extraOptions = [ "--network=penpot" ];
        environment = {
          # https://help.penpot.app/technical-guide/configuration/#exporter
          # https://github.com/penpot/penpot/blob/develop/docker/images/docker-compose.yaml#L221
          PENPOT_PUBLIC_URI = "http://penpot-frontend";
          PENPOT_REDIS_URI = "redis://penpot-redis/0";
        };
      };

      "penpot-postgres" = {
        autoStart = true;
        image = "docker.io/library/postgres:15";
        extraOptions = [ "--stop-signal=SIGINT" "--network=penpot" ];
        volumes = [ "penpot_postgres_v15:/var/lib/postgresql/data" ];
        environment = {
          # https://github.com/penpot/penpot/blob/develop/docker/images/docker-compose.yaml#L240

          POSTGRES_INITDB_ARGS = "--data-checksums";
          POSTGRES_DB = "penpot";
          POSTGRES_USER = "penpot";
          # POSTGRES_PASSWORD set via environmentFile.
        };
        environmentFiles = [ "/run/secrets/penpot_postgres_environment_file" ];
      };

      "penpot-redis" = {
        autoStart = true;
        image = "docker.io/library/redis:7";
        extraOptions = [ "--network=penpot" ];
      };
    };
  };

  # Docker networks.
  systemd.services."docker-network-penpot" = {
    serviceConfig = {
      Type = "oneshot";
      RemainAfterExit = true;
      ExecStop = "${pkgs.docker}/bin/docker network rm -f penpot";
    };
    script = "${pkgs.docker}/bin/docker network inspect penpot || ${pkgs.docker}/bin/docker network create penpot";
    requiredBy = [
      "docker-penpot-frontend.service"
      "docker-penpot-backend.service"
      "docker-penpot-exporter.service"
      "docker-penpot-postgres.service"
      "docker-penpot-redis.service"
    ];
    before = [
      "docker-penpot-frontend.service"
      "docker-penpot-backend.service"
      "docker-penpot-exporter.service"
      "docker-penpot-postgres.service"
      "docker-penpot-redis.service"
    ];
  };

  # Pull docker images prior to starting container services, so that a container
  # service isn't considered up, if it actually is still just pulling the
  # relevant image.
  systemd.services."docker-images-penpot" = {
    serviceConfig = {
      Type = "oneshot";
      RemainAfterExit = true;
    };
    script = ''
      ${pkgs.docker}/bin/docker pull ${config.virtualisation.oci-containers.containers."penpot-frontend".image}
      ${pkgs.docker}/bin/docker pull ${config.virtualisation.oci-containers.containers."penpot-backend".image}
      ${pkgs.docker}/bin/docker pull ${config.virtualisation.oci-containers.containers."penpot-exporter".image}
      ${pkgs.docker}/bin/docker pull ${config.virtualisation.oci-containers.containers."penpot-postgres".image}
      ${pkgs.docker}/bin/docker pull ${config.virtualisation.oci-containers.containers."penpot-redis".image}
    '';
    requiredBy = [
      "docker-penpot-frontend.service"
      "docker-penpot-backend.service"
      "docker-penpot-exporter.service"
      "docker-penpot-postgres.service"
      "docker-penpot-redis.service"
    ];
    before = [
      "docker-penpot-frontend.service"
      "docker-penpot-backend.service"
      "docker-penpot-exporter.service"
      "docker-penpot-postgres.service"
      "docker-penpot-redis.service"
    ];
  };

  sops.secrets."penpot_backend_environment_file" = {
    mode = "0440";
    owner = "root";
    group = "root";
  };

  sops.secrets."penpot_postgres_environment_file" = {
    mode = "0440";
    owner = "root";
    group = "root";
  };
}