shuttles #2
|
|
@ -0,0 +1,82 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "postgres_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "psql_db" {
|
||||||
|
source = "../postgres/tenant"
|
||||||
|
|
||||||
|
tenant_name = "affine"
|
||||||
|
admin_username = "postgres"
|
||||||
|
admin_password = var.postgres_password
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
module "affine" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
app_name = "affine"
|
||||||
|
image = "ghcr.io/toeverything/affine:stable"
|
||||||
|
ports = ["3020:3010"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/affine/storage:/root/.affine/storage:Z",
|
||||||
|
"/opt/storage/data/affine/config:/root/.affine/config:Z"
|
||||||
|
]
|
||||||
|
environment = {
|
||||||
|
DATABASE_URL = "postgresql://affine:${module.psql_db.password}@systemd-postgres:5432/affine"
|
||||||
|
REDIS_SERVER_HOST = "systemd-valkey"
|
||||||
|
AFFINE_INDEXER_ENABLED = "false"
|
||||||
|
}
|
||||||
|
command = [
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"\"node ./scripts/self-host-predeploy.js && node ./dist/main.js\""
|
||||||
|
]
|
||||||
|
healthcmd = "curl -f http://localhost:3010 || exit 1"
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "affine"
|
||||||
|
domain = "affine.${var.server_domain}"
|
||||||
|
port = "3020"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
depends_on_services = ["postgres.service", "valkey.service"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.affine.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.affine.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "airsonic-advanced" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "airsonic-advanced"
|
||||||
|
image = "lscr.io/linuxserver/airsonic-advanced:latest"
|
||||||
|
ports = ["4040:4040"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/airsonic-advanced:/config:Z",
|
||||||
|
"/opt/storage/media/music:/music:Z",
|
||||||
|
]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
PUID="1001"
|
||||||
|
PGID="1001"
|
||||||
|
TZ="Etc/UTC"
|
||||||
|
#JAVA_OPTS="-Dserver.address=127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "airsonic-advanced"
|
||||||
|
domain = "airsonic.${var.server_domain}"
|
||||||
|
port = "4040"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.airsonic-advanced.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.airsonic-advanced.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "beets" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "beets"
|
||||||
|
image = "lscr.io/linuxserver/beets:latest"
|
||||||
|
ports = ["8337:8337"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/beets/config:/config:Z",
|
||||||
|
"/opt/storage/media/music:/music:z",
|
||||||
|
"/opt/storage/media/downloads:/downloads:z"
|
||||||
|
]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
PUID = "1001"
|
||||||
|
PGID = "1001"
|
||||||
|
TZ = "Europe/Amsterdam"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "beets"
|
||||||
|
domain = "beets.${var.server_domain}"
|
||||||
|
port = "8337"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.beets.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.beets.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "deeptutor" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "deeptutor"
|
||||||
|
image = "ghcr.io/hkuds/deeptutor:latest"
|
||||||
|
ports = ["8001:8001", "3782:3782"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/deeptutor/config:/app/config:Z",
|
||||||
|
"/opt/storage/data/deeptutor/user:/app/data/user:Z",
|
||||||
|
"/opt/storage/data/deeptutor/knowledge_bases:/app/data/knowledge_bases:Z",
|
||||||
|
]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
# LLM Configuration (Required - set these in terraform.tfvars)
|
||||||
|
LLM_BINDING = "openai"
|
||||||
|
LLM_MODEL = "deepseek/deepseek-v3.2"
|
||||||
|
LLM_BINDING_API_KEY = "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"
|
||||||
|
LLM_BINDING_HOST = "https://openrouter.ai/api/v1"
|
||||||
|
DISABLE_SSL_VERIFY = "false"
|
||||||
|
|
||||||
|
# Embedding Configuration (Required for Knowledge Base)
|
||||||
|
EMBEDDING_BINDING = "openai"
|
||||||
|
EMBEDDING_MODEL = "text-embedding-3-large"
|
||||||
|
EMBEDDING_BINDING_API_KEY = "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"
|
||||||
|
EMBEDDING_BINDING_HOST = "https://openrouter.ai/api/v1"
|
||||||
|
EMBEDDING_DIM = "3072"
|
||||||
|
EMBEDDING_MAX_TOKENS = "8192"
|
||||||
|
|
||||||
|
# TTS Configuration (Optional)
|
||||||
|
TTS_MODEL = ""
|
||||||
|
TTS_API_KEY = ""
|
||||||
|
TTS_URL = ""
|
||||||
|
TTS_VOICE = "alloy"
|
||||||
|
|
||||||
|
# Web Search Configuration (Optional)
|
||||||
|
PERPLEXITY_API_KEY = ""
|
||||||
|
|
||||||
|
# Logging Configuration
|
||||||
|
RAG_TOOL_MODULE_LOG_LEVEL = "INFO"
|
||||||
|
|
||||||
|
# Service Ports
|
||||||
|
BACKEND_PORT = "8001"
|
||||||
|
FRONTEND_PORT = "3782"
|
||||||
|
|
||||||
|
# External API URL (for browser access if different from localhost)
|
||||||
|
NEXT_PUBLIC_API_BASE_EXTERNAL = "https://deeptutor-api.${var.server_domain}"
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcmd = "curl -f http://localhost:8001/ || exit 1"
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "deeptutor-frontend"
|
||||||
|
domain = "deeptutor.${var.server_domain}"
|
||||||
|
port = "3782"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "deeptutor-api"
|
||||||
|
domain = "deeptutor-api.${var.server_domain}"
|
||||||
|
port = "8001"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.deeptutor.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.deeptutor.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "username" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "password" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
module "redis" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "documentdb"
|
||||||
|
image = "ghcr.io/documentdb/documentdb/documentdb-local:latest"
|
||||||
|
ports = ["27017:27017"]
|
||||||
|
volumes = ["/opt/storage/data/documentdb:/data:Z"]
|
||||||
|
environment = {
|
||||||
|
USERNAME = random_password.username.result
|
||||||
|
PASSWORD = random_password.password.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.redis.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.redis.installed]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "username" {
|
||||||
|
value = random_password.username.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "password" {
|
||||||
|
value = random_password.password.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "postgres_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "forgejo_secret_key" {
|
||||||
|
length = 64
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "forgejo_internal_token" {
|
||||||
|
length = 64
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "forgejo_jwt_secret" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
module "psql_db" {
|
||||||
|
source = "../postgres/tenant"
|
||||||
|
|
||||||
|
tenant_name = "forgejo"
|
||||||
|
admin_username = "postgres"
|
||||||
|
admin_password = var.postgres_password
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
module "forgejo" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "forgejo"
|
||||||
|
image = "codeberg.org/forgejo/forgejo:13.0"
|
||||||
|
ports = ["3000:3000", "2222:22"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/forgejo:/data:Z",
|
||||||
|
"/etc/localtime:/etc/localtime:ro"
|
||||||
|
]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
USER_UID = "1000"
|
||||||
|
USER_GID = "1000"
|
||||||
|
FORGEJO__database__DB_TYPE = "postgres"
|
||||||
|
FORGEJO__database__HOST = "systemd-postgres:5432"
|
||||||
|
FORGEJO__database__NAME = "forgejo"
|
||||||
|
FORGEJO__database__USER = module.psql_db.username
|
||||||
|
FORGEJO__database__PASSWD = module.psql_db.password
|
||||||
|
FORGEJO__server__DOMAIN = "forgejo.${var.server_domain}"
|
||||||
|
FORGEJO__server__ROOT_URL = "https://forgejo.${var.server_domain}/"
|
||||||
|
FORGEJO__server__SSH_DOMAIN = "forgejo.${var.server_domain}"
|
||||||
|
FORGEJO__server__SSH_PORT = "2222"
|
||||||
|
FORGEJO__security__INSTALL_LOCK = "true"
|
||||||
|
FORGEJO__security__SECRET_KEY = random_password.forgejo_secret_key.result
|
||||||
|
FORGEJO__security__INTERNAL_TOKEN = random_password.forgejo_internal_token.result
|
||||||
|
FORGEJO__oauth2__JWT_SECRET = random_password.forgejo_jwt_secret.result
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcmd = "curl -f http://localhost:3000/api/v1/version || exit 1"
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "forgejo"
|
||||||
|
domain = "forgejo.${var.server_domain}"
|
||||||
|
port = "3000"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.forgejo.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.forgejo.installed]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "secret_key" {
|
||||||
|
value = random_password.forgejo_secret_key.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "internal_token" {
|
||||||
|
value = random_password.forgejo_internal_token.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "jwt_secret" {
|
||||||
|
value = random_password.forgejo_jwt_secret.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "gonic" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "gonic"
|
||||||
|
image = "docker.io/sentriz/gonic:latest"
|
||||||
|
ports = ["4747:80"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/gonic:/config:Z",
|
||||||
|
"/opt/storage/media/music:/music:Z",
|
||||||
|
"/opt/storage/media/podcasts:/podcasts:Z",
|
||||||
|
"/opt/storage/media/playlists:/playlists:Z",
|
||||||
|
"/opt/storage/cache/gonic:/cache:Z",
|
||||||
|
]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
TZ="Etc/UTC"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "gonic"
|
||||||
|
domain = "gonic.${var.server_domain}"
|
||||||
|
port = "4747"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.gonic.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.gonic.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "mopidy" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "mopidy"
|
||||||
|
image = "docker.io/ivdata/mopidy"
|
||||||
|
ports = ["6600:6600", "6680:6680"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/mopidy:/var/lib/mopidy:Z",
|
||||||
|
"/opt/storage/data/mopidy/mopidy.conf:/home/mopidy/.config/mopidy/mopidy.conf:Z",
|
||||||
|
"/opt/storage/media/music:/media/music:ro,Z",
|
||||||
|
]
|
||||||
|
|
||||||
|
# environment = {
|
||||||
|
# SPOTIFY_CLIENT_ID="ff4a141c-cb52-4569-9848-c0468da66bd1"
|
||||||
|
# SPOTIFY_CLIENT_SECRET="VVXVtoiY8DanFLl9BnpHvLygXCchboHoEewoALTHIaA="
|
||||||
|
# }
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "mopidy"
|
||||||
|
domain = "mopidy.${var.server_domain}"
|
||||||
|
port = "6680"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.mopidy.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.mopidy.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "navidrome" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "navidrome"
|
||||||
|
image = "docker.io/deluan/navidrome:latest"
|
||||||
|
ports = ["4533:4533"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/navidrome:/data:Z",
|
||||||
|
"/opt/storage/media/music:/music:ro,Z",
|
||||||
|
]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
ND_LOGLEVEL="info"
|
||||||
|
ND_ENABLEINSIGHTSCOLLECTOR="false"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "navidrome"
|
||||||
|
domain = "navidrome.${var.server_domain}"
|
||||||
|
port = "4533"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.navidrome.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.navidrome.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate random keys for esign
|
||||||
|
resource "random_password" "app_id" {
|
||||||
|
length = 12
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "master_key" {
|
||||||
|
length = 32
|
||||||
|
special = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "esign" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "esign"
|
||||||
|
image = "docker.io/esign/esignserver:main"
|
||||||
|
ports = ["8180:8080"]
|
||||||
|
volumes = ["/opt/storage/data/esign:/usr/src/app/files:Z"]
|
||||||
|
environment = {
|
||||||
|
# Node environment
|
||||||
|
NODE_ENV = "production"
|
||||||
|
|
||||||
|
# App identifiers
|
||||||
|
APP_ID = random_password.app_id.result
|
||||||
|
REACT_APP_APPID = random_password.app_id.result
|
||||||
|
appName = "Four Lights - eSign"
|
||||||
|
MASTER_KEY = random_password.master_key.result
|
||||||
|
|
||||||
|
# Frontend config
|
||||||
|
PUBLIC_URL = "https://esign.${var.server_domain}"
|
||||||
|
REACT_APP_SERVERURL = "https://esign.${var.server_domain}/api/app"
|
||||||
|
GENERATE_SOURCEMAP = "false"
|
||||||
|
|
||||||
|
# Backend API config
|
||||||
|
SERVER_URL = "https://esign.${var.server_domain}/api/app"
|
||||||
|
PARSE_MOUNT = "/app"
|
||||||
|
|
||||||
|
# MongoDB connection settings
|
||||||
|
MONGODB_URI = "mongodb://systemd-documentdb:27017/esign"
|
||||||
|
|
||||||
|
# MinIO/S3 storage configuration
|
||||||
|
DO_SPACE = "esign"
|
||||||
|
DO_ENDPOINT = "systemd-minio:9000"
|
||||||
|
DO_BASEURL = "https://esign.${var.server_domain}/minio"
|
||||||
|
DO_ACCESS_KEY_ID = "esign"
|
||||||
|
DO_SECRET_ACCESS_KEY = "esign"
|
||||||
|
DO_REGION = "us-east-1"
|
||||||
|
|
||||||
|
# Email config (commented out - configure as needed)
|
||||||
|
# SMTP_ENABLE = ""
|
||||||
|
# SMTP_HOST = ""
|
||||||
|
# SMTP_PORT = ""
|
||||||
|
# SMTP_USER_EMAIL = ""
|
||||||
|
# SMTP_PASS = ""
|
||||||
|
|
||||||
|
# Document signing (optional)
|
||||||
|
PFX_BASE64 = ""
|
||||||
|
PASS_PHRASE = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "esign"
|
||||||
|
domain = "esign.${var.server_domain}"
|
||||||
|
port = "8180"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.esign.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_id" {
|
||||||
|
value = random_password.app_id.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "master_key" {
|
||||||
|
value = random_password.master_key.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.esign.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "admin" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "plane-admin"
|
||||||
|
image = "docker.io/makeplane/plane-admin:v1.0.0"
|
||||||
|
ports = ["3010:3000"]
|
||||||
|
environment = {
|
||||||
|
# API settings
|
||||||
|
NEXT_PUBLIC_API_BASE_URL = "https://plane.${var.server_domain}/api"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
NEXT_PUBLIC_WEB_URL = "https://plane.${var.server_domain}/god-mode"
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
NODE_ENV = "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "plane-admin"
|
||||||
|
domain = "plane.${var.server_domain}/god-mode/"
|
||||||
|
port = "3010"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.admin.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.admin.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
# Accept credentials from parent Plane module
|
||||||
|
variable "db_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "redis_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "rabbitmq_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_access_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "api" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
app_name = "plane-api"
|
||||||
|
image = "docker.io/makeplane/plane-backend:v1.0.0"
|
||||||
|
ports = ["3011:8000"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/plane-api:/var/www/plane:Z"
|
||||||
|
]
|
||||||
|
environment = {
|
||||||
|
# Database settings
|
||||||
|
DATABASE_URL = "postgresql://plane:${var.db_password}@systemd-postgres:5432/plane"
|
||||||
|
POSTGRES_USER = "plane"
|
||||||
|
POSTGRES_PASSWORD = var.db_password
|
||||||
|
POSTGRES_DB = "plane"
|
||||||
|
|
||||||
|
# Redis settings
|
||||||
|
REDIS_URL = "redis://:${var.redis_password}@systemd-valkey:6379/0"
|
||||||
|
REDIS_HOST = "systemd-valkey"
|
||||||
|
REDIS_PORT = "6379"
|
||||||
|
|
||||||
|
# RabbitMQ settings
|
||||||
|
RABBITMQ_URL = "amqp://plane:${var.rabbitmq_password}@systemd-rabbitmq:5672/plane"
|
||||||
|
RABBITMQ_HOST = "systemd-rabbitmq"
|
||||||
|
RABBITMQ_PORT = "5672"
|
||||||
|
RABBITMQ_USER = "plane"
|
||||||
|
RABBITMQ_PASSWORD = var.rabbitmq_password
|
||||||
|
RABBITMQ_VHOST = "plane"
|
||||||
|
|
||||||
|
# MinIO/S3 settings
|
||||||
|
AWS_S3_ENDPOINT_URL = "http://systemd-minio:9000"
|
||||||
|
AWS_ACCESS_KEY_ID = var.minio_access_key
|
||||||
|
AWS_SECRET_ACCESS_KEY = var.minio_secret_key
|
||||||
|
AWS_S3_BUCKET_NAME = "plane"
|
||||||
|
AWS_REGION = "us-east-1"
|
||||||
|
USE_MINIO = "1"
|
||||||
|
|
||||||
|
# Application settings
|
||||||
|
SECRET_KEY = var.secret_key
|
||||||
|
DEBUG = "0"
|
||||||
|
ENVIRONMENT = "production"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
WEB_URL = "https://plane.${var.server_domain}/api/"
|
||||||
|
ADMIN_BASE_URL = "https://plane.${var.server_domain}/god-mode/"
|
||||||
|
SPACE_BASE_URL = "https://plane.${var.server_domain}/spaces/"
|
||||||
|
APP_BASE_URL = "https://plane.${var.server_domain}/"
|
||||||
|
LIVE_BASE_URL = "https://plane.${var.server_domain}/live/"
|
||||||
|
|
||||||
|
# File upload settings
|
||||||
|
FILE_SIZE_LIMIT = "5242880"
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
API_KEY_RATE_LIMIT = "60/minute"
|
||||||
|
|
||||||
|
# Workers
|
||||||
|
GUNICORN_WORKERS=3
|
||||||
|
|
||||||
|
# Email settings (optional - can be configured later)
|
||||||
|
# EMAIL_HOST = ""
|
||||||
|
# EMAIL_PORT = ""
|
||||||
|
# EMAIL_HOST_USER = ""
|
||||||
|
# EMAIL_HOST_PASSWORD = ""
|
||||||
|
# EMAIL_USE_TLS = "1"
|
||||||
|
|
||||||
|
# OpenAI/GPT settings (optional)
|
||||||
|
# OPENAI_API_KEY = ""
|
||||||
|
# GPT_ENGINE = "gpt-3.5-turbo"
|
||||||
|
}
|
||||||
|
command = ["./bin/docker-entrypoint-api.sh"]
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "plane-api"
|
||||||
|
domain = "plane.${var.server_domain}/api/"
|
||||||
|
port = "3011"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "plane-auth"
|
||||||
|
domain = "plane.${var.server_domain}/auth/"
|
||||||
|
port = "3011"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.api.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.api.installed]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "db_password" {
|
||||||
|
value = var.db_password
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "redis_password" {
|
||||||
|
value = var.redis_password
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "rabbitmq_password" {
|
||||||
|
value = var.rabbitmq_password
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "minio_access_key" {
|
||||||
|
value = var.minio_access_key
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "minio_secret_key" {
|
||||||
|
value = var.minio_secret_key
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "secret_key" {
|
||||||
|
value = var.secret_key
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "redis_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "rabbitmq_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_access_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "beat-worker" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
app_name = "plane-beat-worker"
|
||||||
|
image = "docker.io/makeplane/plane-backend:v1.0.0"
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/plane-beat-worker:/var/www/plane:Z"
|
||||||
|
]
|
||||||
|
environment = {
|
||||||
|
# Database settings
|
||||||
|
DATABASE_URL = "postgresql://plane:${var.db_password}@systemd-postgres:5432/plane"
|
||||||
|
POSTGRES_USER = "plane"
|
||||||
|
POSTGRES_PASSWORD = var.db_password
|
||||||
|
POSTGRES_DB = "plane"
|
||||||
|
|
||||||
|
# Redis settings
|
||||||
|
REDIS_URL = "redis://:${var.redis_password}@systemd-valkey:6379/0"
|
||||||
|
REDIS_HOST = "systemd-valkey"
|
||||||
|
REDIS_PORT = "6379"
|
||||||
|
|
||||||
|
# RabbitMQ settings
|
||||||
|
RABBITMQ_URL = "amqp://plane:${var.rabbitmq_password}@systemd-rabbitmq:5672/plane"
|
||||||
|
RABBITMQ_HOST = "systemd-rabbitmq"
|
||||||
|
RABBITMQ_PORT = "5672"
|
||||||
|
RABBITMQ_USER = "plane"
|
||||||
|
RABBITMQ_PASSWORD = var.rabbitmq_password
|
||||||
|
RABBITMQ_VHOST = "plane"
|
||||||
|
|
||||||
|
# MinIO/S3 settings
|
||||||
|
AWS_S3_ENDPOINT_URL = "http://systemd-minio:9000"
|
||||||
|
AWS_ACCESS_KEY_ID = var.minio_access_key
|
||||||
|
AWS_SECRET_ACCESS_KEY = var.minio_secret_key
|
||||||
|
AWS_S3_BUCKET_NAME = "plane-uploads"
|
||||||
|
AWS_REGION = "us-east-1"
|
||||||
|
USE_MINIO = "1"
|
||||||
|
|
||||||
|
# Application settings
|
||||||
|
SECRET_KEY = var.secret_key
|
||||||
|
DEBUG = "0"
|
||||||
|
ENVIRONMENT = "production"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
WEB_URL = "https://plane.${var.server_domain}"
|
||||||
|
API_BASE_URL = "https://plane.${var.server_domain}/api"
|
||||||
|
|
||||||
|
# File upload settings
|
||||||
|
FILE_SIZE_LIMIT = "5242880"
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
API_KEY_RATE_LIMIT = "60/minute"
|
||||||
|
}
|
||||||
|
command = ["./bin/docker-entrypoint-beat.sh"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.beat-worker.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "live" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "plane-live"
|
||||||
|
image = "docker.io/makeplane/plane-live:v1.0.0"
|
||||||
|
ports = ["3012:3000"]
|
||||||
|
environment = {
|
||||||
|
# API settings
|
||||||
|
NEXT_PUBLIC_API_BASE_URL = "https://plane.${var.server_domain}/api/"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
NEXT_PUBLIC_WEB_URL = "https://plane.${var.server_domain}/live/"
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
NODE_ENV = "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "plane-live"
|
||||||
|
domain = "plane.${var.server_domain}/live/"
|
||||||
|
port = "3012"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.live.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.live.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,239 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
# Credentials from existing services
|
||||||
|
variable "postgres_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_server" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_access_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "rabbitmq_username" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "rabbitmq_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate Plane-specific credentials
|
||||||
|
resource "random_password" "redis_password" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "secret_key" {
|
||||||
|
length = 64
|
||||||
|
special = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "psql_db" {
|
||||||
|
source = "../postgres/tenant"
|
||||||
|
|
||||||
|
tenant_name = "plane"
|
||||||
|
admin_username = "postgres"
|
||||||
|
admin_password = var.postgres_password
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
module "aqmp_db" {
|
||||||
|
source = "../rabbitmq/tenant"
|
||||||
|
|
||||||
|
tenant_name = "plane"
|
||||||
|
admin_username = var.rabbitmq_username
|
||||||
|
admin_password = var.rabbitmq_password
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
module "minio" {
|
||||||
|
source = "../minio/tenant"
|
||||||
|
|
||||||
|
name = "plane"
|
||||||
|
server = var.minio_server
|
||||||
|
access_key = var.minio_access_key
|
||||||
|
secret_key = var.minio_secret_key
|
||||||
|
tls = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deploy the core API service first
|
||||||
|
module "api" {
|
||||||
|
source = "./api"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
# Use credentials from existing services
|
||||||
|
redis_password = "" #random_password.redis_password.result
|
||||||
|
db_password = module.psql_db.password
|
||||||
|
rabbitmq_password = module.aqmp_db.password
|
||||||
|
|
||||||
|
minio_access_key = module.minio.access_key
|
||||||
|
minio_secret_key = module.minio.secret_key
|
||||||
|
secret_key = random_password.secret_key.result
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run database migrations after API is ready
|
||||||
|
module "migrator" {
|
||||||
|
source = "./migrator"
|
||||||
|
wait_on = module.api.installed
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
# Use credentials from API module
|
||||||
|
db_password = module.api.db_password
|
||||||
|
redis_password = module.api.redis_password
|
||||||
|
rabbitmq_password = module.api.rabbitmq_password
|
||||||
|
minio_access_key = module.minio.access_key
|
||||||
|
minio_secret_key = module.minio.secret_key
|
||||||
|
secret_key = module.api.secret_key
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start background workers after migrations
|
||||||
|
module "worker" {
|
||||||
|
source = "./worker"
|
||||||
|
wait_on = module.migrator.installed
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
# Use credentials from API module
|
||||||
|
db_password = module.api.db_password
|
||||||
|
redis_password = module.api.redis_password
|
||||||
|
rabbitmq_password = module.api.rabbitmq_password
|
||||||
|
minio_access_key = module.minio.access_key
|
||||||
|
minio_secret_key = module.minio.secret_key
|
||||||
|
secret_key = module.api.secret_key
|
||||||
|
}
|
||||||
|
|
||||||
|
module "beat-worker" {
|
||||||
|
source = "./beat-worker"
|
||||||
|
wait_on = module.migrator.installed
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
# Use credentials from API module
|
||||||
|
db_password = module.api.db_password
|
||||||
|
redis_password = module.api.redis_password
|
||||||
|
rabbitmq_password = module.api.rabbitmq_password
|
||||||
|
minio_access_key = module.minio.access_key
|
||||||
|
minio_secret_key = module.minio.secret_key
|
||||||
|
secret_key = module.api.secret_key
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start web interfaces after API is ready
|
||||||
|
module "web" {
|
||||||
|
source = "./web"
|
||||||
|
wait_on = module.api.installed
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
module "admin" {
|
||||||
|
source = "./admin"
|
||||||
|
wait_on = module.api.installed
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
module "space" {
|
||||||
|
source = "./space"
|
||||||
|
wait_on = module.api.installed
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
module "live" {
|
||||||
|
source = "./live"
|
||||||
|
wait_on = module.api.installed
|
||||||
|
server_ip = var.server_ip
|
||||||
|
server_domain = var.server_domain
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
|
||||||
|
# Aggregate all app URLs
|
||||||
|
locals {
|
||||||
|
app_urls = {
|
||||||
|
web = module.web.app_urls
|
||||||
|
admin = module.admin.app_urls
|
||||||
|
space = module.space.app_urls
|
||||||
|
live = module.live.app_urls
|
||||||
|
api = module.api.app_urls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = local.app_urls
|
||||||
|
description = "All Plane application URLs"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [
|
||||||
|
module.api.installed,
|
||||||
|
module.migrator.installed,
|
||||||
|
module.worker.installed,
|
||||||
|
module.beat-worker.installed,
|
||||||
|
module.web.installed,
|
||||||
|
module.admin.installed,
|
||||||
|
module.space.installed,
|
||||||
|
module.live.installed
|
||||||
|
]
|
||||||
|
description = "Whether all Plane services are installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "credentials" {
|
||||||
|
value = {
|
||||||
|
db_password = module.api.db_password
|
||||||
|
redis_password = module.api.redis_password
|
||||||
|
minio_access_key = module.api.minio_access_key
|
||||||
|
minio_secret_key = module.api.minio_secret_key
|
||||||
|
rabbitmq_password = module.api.rabbitmq_password
|
||||||
|
secret_key = module.api.secret_key
|
||||||
|
}
|
||||||
|
sensitive = true
|
||||||
|
description = "All Plane service credentials"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "main_url" {
|
||||||
|
value = "https://plane.${var.server_domain}"
|
||||||
|
description = "Main Plane application URL"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "redis_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "rabbitmq_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_access_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "migrator" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
app_name = "plane-migrator"
|
||||||
|
image = "docker.io/makeplane/plane-backend:v1.0.0"
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/plane-migrator:/var/www/plane:Z"
|
||||||
|
]
|
||||||
|
environment = {
|
||||||
|
# Database settings
|
||||||
|
DATABASE_URL = "postgresql://plane:${var.db_password}@systemd-postgres:5432/plane"
|
||||||
|
POSTGRES_USER = "plane"
|
||||||
|
POSTGRES_PASSWORD = var.db_password
|
||||||
|
POSTGRES_DB = "plane"
|
||||||
|
|
||||||
|
# Redis settings
|
||||||
|
REDIS_URL = "redis://:${var.redis_password}@systemd-valkey:6379/0"
|
||||||
|
REDIS_HOST = "systemd-valkey"
|
||||||
|
REDIS_PORT = "6379"
|
||||||
|
|
||||||
|
# RabbitMQ settings
|
||||||
|
RABBITMQ_URL = "amqp://plane:${var.rabbitmq_password}@systemd-rabbitmq:5672/plane"
|
||||||
|
RABBITMQ_HOST = "systemd-rabbitmq"
|
||||||
|
RABBITMQ_PORT = "5672"
|
||||||
|
RABBITMQ_USER = "plane"
|
||||||
|
RABBITMQ_PASSWORD = var.rabbitmq_password
|
||||||
|
RABBITMQ_VHOST = "plane"
|
||||||
|
|
||||||
|
# MinIO/S3 settings
|
||||||
|
AWS_S3_ENDPOINT_URL = "http://systemd-minio:9000"
|
||||||
|
AWS_ACCESS_KEY_ID = var.minio_access_key
|
||||||
|
AWS_SECRET_ACCESS_KEY = var.minio_secret_key
|
||||||
|
AWS_S3_BUCKET_NAME = "plane-uploads"
|
||||||
|
AWS_REGION = "us-east-1"
|
||||||
|
USE_MINIO = "1"
|
||||||
|
|
||||||
|
# Application settings
|
||||||
|
SECRET_KEY = var.secret_key
|
||||||
|
DEBUG = "0"
|
||||||
|
ENVIRONMENT = "production"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
WEB_URL = "https://plane.${var.server_domain}"
|
||||||
|
API_BASE_URL = "https://plane.${var.server_domain}/api"
|
||||||
|
|
||||||
|
# File upload settings
|
||||||
|
FILE_SIZE_LIMIT = "5242880"
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
API_KEY_RATE_LIMIT = "60/minute"
|
||||||
|
}
|
||||||
|
command = ["./bin/docker-entrypoint-migrator.sh"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.migrator.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "space" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
app_name = "plane-space"
|
||||||
|
image = "docker.io/makeplane/plane-space:v1.0.0"
|
||||||
|
ports = ["3013:3000"]
|
||||||
|
environment = {
|
||||||
|
# API settings
|
||||||
|
NEXT_PUBLIC_API_BASE_URL = "https://plane.${var.server_domain}/api"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
NEXT_PUBLIC_WEB_URL = "https://plane.${var.server_domain}/spaces"
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
NODE_ENV = "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "plane-space"
|
||||||
|
domain = "plane.${var.server_domain}/spaces/"
|
||||||
|
port = "3013"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.space.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.space.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "web" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
app_name = "plane-web"
|
||||||
|
image = "docker.io/makeplane/plane-frontend:v1.0.0"
|
||||||
|
ports = ["3014:3000"]
|
||||||
|
environment = {
|
||||||
|
# API settings
|
||||||
|
NEXT_PUBLIC_API_BASE_URL = "https://plane.${var.server_domain}/api"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
NEXT_PUBLIC_WEB_BASE_URL = "https://plane.${var.server_domain}"
|
||||||
|
|
||||||
|
NEXT_PUBLIC_ADMIN_BASE_URL = "https://plane.${var.server_domain}/god-mode"
|
||||||
|
NEXT_PUBLIC_SPACE_BASE_URL = "https://plane.${var.server_domain}/spaces"
|
||||||
|
NEXT_PUBLIC_LIVE_BASE_URL = "https://plane.${var.server_domain}/live"
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
NODE_ENV = "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "plane-web"
|
||||||
|
domain = "plane.${var.server_domain}"
|
||||||
|
port = "3014"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.web.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.web.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "db_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "redis_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "rabbitmq_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_access_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "minio_secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "secret_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "worker" {
|
||||||
|
source = "../../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
restart_policy = "never"
|
||||||
|
|
||||||
|
app_name = "plane-worker"
|
||||||
|
image = "docker.io/makeplane/plane-backend:v1.0.0"
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/plane-worker:/var/www/plane:Z"
|
||||||
|
]
|
||||||
|
environment = {
|
||||||
|
# Database settings
|
||||||
|
DATABASE_URL = "postgresql://plane:${var.db_password}@systemd-postgres:5432/plane"
|
||||||
|
POSTGRES_USER = "plane"
|
||||||
|
POSTGRES_PASSWORD = var.db_password
|
||||||
|
POSTGRES_DB = "plane"
|
||||||
|
PGDATA = "/var/lib/postgresql/data"
|
||||||
|
|
||||||
|
# Redis settings
|
||||||
|
REDIS_URL = "redis://:${var.redis_password}@systemd-valkey:6379/0"
|
||||||
|
REDIS_HOST = "systemd-valkey"
|
||||||
|
REDIS_PORT = "6379"
|
||||||
|
|
||||||
|
# RabbitMQ settings
|
||||||
|
RABBITMQ_URL = "amqp://plane:${var.rabbitmq_password}@systemd-rabbitmq:5672/plane"
|
||||||
|
RABBITMQ_HOST = "systemd-rabbitmq"
|
||||||
|
RABBITMQ_PORT = "5672"
|
||||||
|
RABBITMQ_USER = "plane"
|
||||||
|
RABBITMQ_PASSWORD = var.rabbitmq_password
|
||||||
|
RABBITMQ_VHOST = "plane"
|
||||||
|
|
||||||
|
# MinIO/S3 settings
|
||||||
|
AWS_S3_ENDPOINT_URL = "http://systemd-minio:9000"
|
||||||
|
AWS_ACCESS_KEY_ID = var.minio_access_key
|
||||||
|
AWS_SECRET_ACCESS_KEY = var.minio_secret_key
|
||||||
|
AWS_S3_BUCKET_NAME = "plane-uploads"
|
||||||
|
AWS_REGION = "us-east-1"
|
||||||
|
USE_MINIO = "1"
|
||||||
|
|
||||||
|
# Application settings
|
||||||
|
SECRET_KEY = var.secret_key
|
||||||
|
DEBUG = "0"
|
||||||
|
ENVIRONMENT = "production"
|
||||||
|
|
||||||
|
# Web settings
|
||||||
|
WEB_URL = "https://plane.${var.server_domain}"
|
||||||
|
API_BASE_URL = "https://plane.${var.server_domain}/api"
|
||||||
|
|
||||||
|
# File upload settings
|
||||||
|
FILE_SIZE_LIMIT = "5242880"
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
API_KEY_RATE_LIMIT = "60/minute"
|
||||||
|
}
|
||||||
|
command = ["./bin/docker-entrypoint-worker.sh"]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.worker.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "password" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
module "postgres" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "postgres"
|
||||||
|
image = "docker.io/postgres:17"
|
||||||
|
ports = ["5432:5432"]
|
||||||
|
volumes = ["/opt/storage/data/postgres:/var/lib/postgresql/data:Z"]
|
||||||
|
environment = {
|
||||||
|
POSTGRES_PASSWORD = random_password.password.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.postgres.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.postgres.installed]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "password" {
|
||||||
|
value = random_password.password.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
# PostgreSQL Tenant Module
|
||||||
|
|
||||||
|
This module creates a PostgreSQL database and user (tenant) on a VPS-hosted PostgreSQL instance using SSH tunneling for secure access.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Creates SSH tunnel to VPS-hosted PostgreSQL
|
||||||
|
- Creates isolated database and user for tenant
|
||||||
|
- Generates secure random password
|
||||||
|
- Outputs connection details for application use
|
||||||
|
- Supports explicit dependencies via `wait_on`
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- SSH access to VPS with key-based authentication
|
||||||
|
- PostgreSQL admin credentials
|
||||||
|
- `terraform-ssh-tunnel` module (automatically downloaded)
|
||||||
|
- `cyrilgdn/postgresql` provider (automatically configured)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Example
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "app_tenant" {
|
||||||
|
source = "./quadlets/modules/postgres/tenant"
|
||||||
|
|
||||||
|
tenant_name = "myapp"
|
||||||
|
admin_username = "postgres"
|
||||||
|
admin_password = var.postgres_admin_password
|
||||||
|
server_ip = "203.0.113.10"
|
||||||
|
ssh_user = "fourlights"
|
||||||
|
|
||||||
|
# Wait for PostgreSQL service to be ready
|
||||||
|
wait_on = module.postgres.installed
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use the connection details
|
||||||
|
output "app_db_connection" {
|
||||||
|
value = {
|
||||||
|
host = module.app_tenant.host
|
||||||
|
port = module.app_tenant.port
|
||||||
|
database = module.app_tenant.database
|
||||||
|
username = module.app_tenant.username
|
||||||
|
password = module.app_tenant.password
|
||||||
|
}
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Custom Port
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "custom_tenant" {
|
||||||
|
source = "./quadlets/modules/postgres/tenant"
|
||||||
|
|
||||||
|
tenant_name = "custom_app"
|
||||||
|
admin_username = "postgres"
|
||||||
|
admin_password = var.postgres_admin_password
|
||||||
|
server_ip = "203.0.113.10"
|
||||||
|
target_port = 5433 # Custom PostgreSQL port
|
||||||
|
|
||||||
|
wait_on = module.postgres.installed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Tenants
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "tenant_1" {
|
||||||
|
source = "./quadlets/modules/postgres/tenant"
|
||||||
|
|
||||||
|
tenant_name = "app1"
|
||||||
|
admin_username = "postgres"
|
||||||
|
admin_password = var.postgres_admin_password
|
||||||
|
server_ip = var.vps_ip
|
||||||
|
|
||||||
|
wait_on = module.postgres.installed
|
||||||
|
}
|
||||||
|
|
||||||
|
module "tenant_2" {
|
||||||
|
source = "./quadlets/modules/postgres/tenant"
|
||||||
|
|
||||||
|
tenant_name = "app2"
|
||||||
|
admin_username = "postgres"
|
||||||
|
admin_password = var.postgres_admin_password
|
||||||
|
server_ip = var.vps_ip
|
||||||
|
|
||||||
|
wait_on = module.postgres.installed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Name | Description | Type | Default | Required |
|
||||||
|
|------|-------------|------|---------|----------|
|
||||||
|
| tenant_name | Name of the tenant (used for database name and username) | string | - | yes |
|
||||||
|
| admin_username | PostgreSQL admin username | string | "postgres" | no |
|
||||||
|
| admin_password | PostgreSQL admin password | string | - | yes |
|
||||||
|
| server_ip | VPS server IP address | string | - | yes |
|
||||||
|
| ssh_user | SSH user for connecting to VPS | string | "fourlights" | no |
|
||||||
|
| ssh_private_key_path | Path to SSH private key | string | "~/.ssh/id_rsa" | no |
|
||||||
|
| target_port | PostgreSQL port on VPS | number | 5432 | no |
|
||||||
|
| wait_on | Resources to wait on before creating tenant | any | null | no |
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
| Name | Description | Sensitive |
|
||||||
|
|------|-------------|-----------|
|
||||||
|
| host | PostgreSQL connection host (via SSH tunnel) | no |
|
||||||
|
| port | PostgreSQL connection port (via SSH tunnel) | no |
|
||||||
|
| database | Database name | no |
|
||||||
|
| username | Database username | no |
|
||||||
|
| password | Database password | yes |
|
||||||
|
| connection_string | Full PostgreSQL connection string | yes |
|
||||||
|
| installed | Installation complete flag | no |
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. **SSH Tunnel**: The module uses `terraform-ssh-tunnel` to create a secure SSH tunnel from your local machine to the VPS PostgreSQL instance
|
||||||
|
2. **Provider Configuration**: The PostgreSQL provider is configured to connect through the tunnel
|
||||||
|
3. **Resource Creation**: Creates a PostgreSQL role (user) and database with appropriate permissions
|
||||||
|
4. **Password Generation**: Generates a cryptographically secure random password
|
||||||
|
5. **Outputs**: Provides connection details for your application to use
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The SSH tunnel is created dynamically when Terraform needs to connect
|
||||||
|
- Ensure your SSH agent is running and has the appropriate key loaded
|
||||||
|
- The database and user will have the same name as `tenant_name`
|
||||||
|
- The user is granted full privileges on their database
|
||||||
|
- Password is stored in Terraform state (sensitive)
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
random = {
|
||||||
|
source = "hashicorp/random"
|
||||||
|
version = "~> 3.6"
|
||||||
|
}
|
||||||
|
postgresql = {
|
||||||
|
source = "cyrilgdn/postgresql"
|
||||||
|
version = "~> 1.22"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate random password for the tenant user
|
||||||
|
resource "random_password" "tenant_password" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
min_upper = 2
|
||||||
|
min_lower = 2
|
||||||
|
min_numeric = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create SSH tunnel to PostgreSQL on VPS
|
||||||
|
module "db_tunnel" {
|
||||||
|
source = "flaupretre/tunnel/ssh"
|
||||||
|
version = "2.3.0"
|
||||||
|
|
||||||
|
target_host = "127.0.0.1"
|
||||||
|
target_port = var.target_port
|
||||||
|
|
||||||
|
gateway_host = var.server_ip
|
||||||
|
gateway_user = var.ssh_user
|
||||||
|
gateway_port = 22
|
||||||
|
}
|
||||||
|
|
||||||
|
provider postgresql {
|
||||||
|
alias = "tunnel"
|
||||||
|
host = module.db_tunnel.host
|
||||||
|
port = module.db_tunnel.port
|
||||||
|
username = var.admin_username
|
||||||
|
password = var.admin_password
|
||||||
|
database = "postgres"
|
||||||
|
sslmode = "disable"
|
||||||
|
superuser = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#---- Database
|
||||||
|
|
||||||
|
resource postgresql_database db {
|
||||||
|
provider = postgresql.tunnel
|
||||||
|
|
||||||
|
name = var.tenant_name
|
||||||
|
owner = var.tenant_name
|
||||||
|
|
||||||
|
encoding = "UTF8"
|
||||||
|
lc_collate = "C"
|
||||||
|
lc_ctype = "C"
|
||||||
|
}
|
||||||
|
|
||||||
|
#---- DB user/role
|
||||||
|
|
||||||
|
resource postgresql_role role {
|
||||||
|
provider = postgresql.tunnel
|
||||||
|
|
||||||
|
name = var.tenant_name
|
||||||
|
login = true
|
||||||
|
password = random_password.tenant_password.result
|
||||||
|
}
|
||||||
|
|
||||||
|
#---- Grant
|
||||||
|
# SQL: grant all on <db>.* to <user>@'%';
|
||||||
|
|
||||||
|
resource postgresql_grant grant {
|
||||||
|
provider = postgresql.tunnel
|
||||||
|
|
||||||
|
object_type = "database"
|
||||||
|
database = var.tenant_name
|
||||||
|
role = var.tenant_name
|
||||||
|
privileges = ["ALL"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
output "database" {
|
||||||
|
description = "Database name"
|
||||||
|
value = var.tenant_name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "username" {
|
||||||
|
description = "Database username"
|
||||||
|
value = var.tenant_name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "password" {
|
||||||
|
description = "Database password"
|
||||||
|
value = random_password.tenant_password.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "connection_string" {
|
||||||
|
description = "PostgreSQL connection string"
|
||||||
|
value = "postgresql://${var.tenant_name}:${random_password.tenant_password.result}@localhost:${var.target_port}/${var.tenant_name}"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
description = "Installation complete flag"
|
||||||
|
value = true
|
||||||
|
depends_on = [postgresql_role.role, postgresql_grant.grant, postgresql_database.db, random_password.tenant_password]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
variable "tenant_name" {
|
||||||
|
description = "Name of the tenant (used for database name and username)"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "admin_username" {
|
||||||
|
description = "PostgreSQL admin username"
|
||||||
|
type = string
|
||||||
|
default = "postgres"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "admin_password" {
|
||||||
|
description = "PostgreSQL admin password"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
description = "VPS server IP address"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_user" {
|
||||||
|
description = "SSH user for connecting to VPS"
|
||||||
|
type = string
|
||||||
|
default = "fourlights"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
description = "Path to SSH private key for authentication"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "target_port" {
|
||||||
|
description = "PostgreSQL port on VPS"
|
||||||
|
type = number
|
||||||
|
default = 5432
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "wait_on" {
|
||||||
|
description = "Resources to wait on before creating tenant"
|
||||||
|
type = any
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {}
|
||||||
|
|
||||||
|
resource "random_password" "api_key" {
|
||||||
|
length = 64
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
module "qdrant" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "qdrant"
|
||||||
|
image = "docker.io/qdrant/qdrant"
|
||||||
|
ports = ["6333:6333", "6334:6334"]
|
||||||
|
volumes = ["/opt/storage/data/qdrant:/qdrant/storage:z"]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
"QDRANT__SERVICE__API_KEY" = random_password.api_key.result
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "qdrant"
|
||||||
|
domain = "qdrant.${var.server_domain}"
|
||||||
|
port = "6333"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
#grpc_port = "6334"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.qdrant.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.qdrant.installed]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "api_key" {
|
||||||
|
value = random_password.api_key.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "username" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "random_password" "password" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
}
|
||||||
|
|
||||||
|
module "rabbitmq" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "rabbitmq"
|
||||||
|
image = "docker.io/rabbitmq:management"
|
||||||
|
ports = ["5672:5672", "15672:15672"]
|
||||||
|
volumes = ["/opt/storage/data/rabbitmq:/var/lib/rabbitmq:Z"]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
RABBITMQ_DEFAULT_USER = random_password.username.result
|
||||||
|
RABBITMQ_DEFAULT_PASS = random_password.password.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.rabbitmq.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.rabbitmq.installed]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "username" {
|
||||||
|
value = random_password.username.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "password" {
|
||||||
|
value = random_password.password.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
# RabbitMQ Tenant Module
|
||||||
|
|
||||||
|
This module creates a RabbitMQ virtual host (vhost) and user (tenant) on a VPS-hosted RabbitMQ instance using SSH tunneling for secure access to the Management API.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Creates SSH tunnel to VPS-hosted RabbitMQ Management API
|
||||||
|
- Creates isolated virtual host and user for tenant
|
||||||
|
- Configures full permissions for user on their vhost
|
||||||
|
- Generates secure random password
|
||||||
|
- Outputs connection details for application use
|
||||||
|
- Supports explicit dependencies via `wait_on`
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- SSH access to VPS with key-based authentication
|
||||||
|
- RabbitMQ admin credentials
|
||||||
|
- RabbitMQ Management plugin enabled
|
||||||
|
- `terraform-ssh-tunnel` module (automatically downloaded)
|
||||||
|
- `cyrilgdn/rabbitmq` provider (automatically configured)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Example
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "app_tenant" {
|
||||||
|
source = "./quadlets/modules/rabbitmq/tenant"
|
||||||
|
|
||||||
|
tenant_name = "myapp"
|
||||||
|
admin_username = var.rabbitmq_admin_username
|
||||||
|
admin_password = var.rabbitmq_admin_password
|
||||||
|
server_ip = "203.0.113.10"
|
||||||
|
ssh_user = "fourlights"
|
||||||
|
|
||||||
|
# Wait for RabbitMQ service to be ready
|
||||||
|
wait_on = module.rabbitmq.installed
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use the connection details
|
||||||
|
output "app_mq_connection" {
|
||||||
|
value = {
|
||||||
|
host = module.app_tenant.host
|
||||||
|
port = module.app_tenant.port
|
||||||
|
vhost = module.app_tenant.vhost
|
||||||
|
username = module.app_tenant.username
|
||||||
|
password = module.app_tenant.password
|
||||||
|
}
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Custom Management Port
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "custom_tenant" {
|
||||||
|
source = "./quadlets/modules/rabbitmq/tenant"
|
||||||
|
|
||||||
|
tenant_name = "custom_app"
|
||||||
|
admin_username = var.rabbitmq_admin_username
|
||||||
|
admin_password = var.rabbitmq_admin_password
|
||||||
|
server_ip = "203.0.113.10"
|
||||||
|
target_port = 25672 # Custom Management API port
|
||||||
|
|
||||||
|
wait_on = module.rabbitmq.installed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Tenants
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "tenant_1" {
|
||||||
|
source = "./quadlets/modules/rabbitmq/tenant"
|
||||||
|
|
||||||
|
tenant_name = "app1"
|
||||||
|
admin_username = var.rabbitmq_admin_username
|
||||||
|
admin_password = var.rabbitmq_admin_password
|
||||||
|
server_ip = var.vps_ip
|
||||||
|
|
||||||
|
wait_on = module.rabbitmq.installed
|
||||||
|
}
|
||||||
|
|
||||||
|
module "tenant_2" {
|
||||||
|
source = "./quadlets/modules/rabbitmq/tenant"
|
||||||
|
|
||||||
|
tenant_name = "app2"
|
||||||
|
admin_username = var.rabbitmq_admin_username
|
||||||
|
admin_password = var.rabbitmq_admin_password
|
||||||
|
server_ip = var.vps_ip
|
||||||
|
|
||||||
|
wait_on = module.rabbitmq.installed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Connection String
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "app_tenant" {
|
||||||
|
source = "./quadlets/modules/rabbitmq/tenant"
|
||||||
|
|
||||||
|
tenant_name = "myapp"
|
||||||
|
admin_username = var.rabbitmq_admin_username
|
||||||
|
admin_password = var.rabbitmq_admin_password
|
||||||
|
server_ip = var.vps_ip
|
||||||
|
|
||||||
|
wait_on = module.rabbitmq.installed
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use in application configuration
|
||||||
|
resource "kubernetes_secret" "rabbitmq_credentials" {
|
||||||
|
metadata {
|
||||||
|
name = "rabbitmq-connection"
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
connection_string = module.app_tenant.connection_string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
| Name | Description | Type | Default | Required |
|
||||||
|
|------|-------------|------|---------|----------|
|
||||||
|
| tenant_name | Name of the tenant (used for vhost name and username) | string | - | yes |
|
||||||
|
| admin_username | RabbitMQ admin username | string | - | yes |
|
||||||
|
| admin_password | RabbitMQ admin password | string | - | yes |
|
||||||
|
| server_ip | VPS server IP address | string | - | yes |
|
||||||
|
| ssh_user | SSH user for connecting to VPS | string | "fourlights" | no |
|
||||||
|
| ssh_private_key_path | Path to SSH private key | string | "~/.ssh/id_rsa" | no |
|
||||||
|
| target_port | RabbitMQ Management API port on VPS | number | 15672 | no |
|
||||||
|
| wait_on | Resources to wait on before creating tenant | any | null | no |
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
| Name | Description | Sensitive |
|
||||||
|
|------|-------------|-----------|
|
||||||
|
| host | RabbitMQ connection host (VPS IP for AMQP) | no |
|
||||||
|
| port | RabbitMQ AMQP connection port (5672) | no |
|
||||||
|
| management_host | RabbitMQ Management API host (via SSH tunnel) | no |
|
||||||
|
| management_port | RabbitMQ Management API port (via SSH tunnel) | no |
|
||||||
|
| vhost | Virtual host name | no |
|
||||||
|
| username | RabbitMQ username | no |
|
||||||
|
| password | RabbitMQ password | yes |
|
||||||
|
| connection_string | Full AMQP connection string | yes |
|
||||||
|
| installed | Installation complete flag | no |
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. **SSH Tunnel**: The module uses `terraform-ssh-tunnel` to create a secure SSH tunnel from your local machine to the VPS RabbitMQ Management API (port 15672)
|
||||||
|
2. **Provider Configuration**: The RabbitMQ provider is configured to connect through the tunnel
|
||||||
|
3. **Resource Creation**: Creates a RabbitMQ virtual host, user, and permissions
|
||||||
|
4. **Password Generation**: Generates a cryptographically secure random password
|
||||||
|
5. **Outputs**: Provides connection details for your application to use
|
||||||
|
|
||||||
|
## Connection Details
|
||||||
|
|
||||||
|
- **Management API**: Accessed via SSH tunnel (for Terraform operations)
|
||||||
|
- **AMQP Protocol**: Direct connection to VPS on port 5672 (for application connections)
|
||||||
|
- **Virtual Host**: Isolated messaging environment for the tenant
|
||||||
|
- **Permissions**: User has full configure, write, and read permissions on their vhost
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The SSH tunnel is only used for management operations (creating vhost/user/permissions)
|
||||||
|
- Applications connect directly to the VPS AMQP port (5672), not through the tunnel
|
||||||
|
- The virtual host and user will have the same name as `tenant_name`
|
||||||
|
- User is granted `configure=".*"`, `write=".*"`, `read=".*"` on their vhost
|
||||||
|
- Password is stored in Terraform state (sensitive)
|
||||||
|
- Ensure RabbitMQ Management plugin is enabled on the VPS instance
|
||||||
|
|
||||||
|
## Permissions Explained
|
||||||
|
|
||||||
|
The tenant user receives the following permissions on their vhost:
|
||||||
|
|
||||||
|
- **configure**: Can declare exchanges, queues, and bindings
|
||||||
|
- **write**: Can publish messages
|
||||||
|
- **read**: Can consume messages
|
||||||
|
|
||||||
|
All permissions use the `.*` regex pattern, granting full access within the vhost.
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
random = {
|
||||||
|
source = "hashicorp/random"
|
||||||
|
version = "~> 3.6"
|
||||||
|
}
|
||||||
|
rabbitmq = {
|
||||||
|
source = "cyrilgdn/rabbitmq"
|
||||||
|
version = "~> 1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate random password for the tenant user
|
||||||
|
resource "random_password" "tenant_password" {
|
||||||
|
length = 32
|
||||||
|
special = false
|
||||||
|
min_upper = 2
|
||||||
|
min_lower = 2
|
||||||
|
min_numeric = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create SSH tunnel to RabbitMQ Management API on VPS
|
||||||
|
module "ssh_tunnel" {
|
||||||
|
source = "flaupretre/tunnel/ssh"
|
||||||
|
version = "2.2.1"
|
||||||
|
|
||||||
|
target_host = "127.0.0.1"
|
||||||
|
target_port = var.target_port
|
||||||
|
|
||||||
|
gateway_host = var.server_ip
|
||||||
|
gateway_user = var.ssh_user
|
||||||
|
|
||||||
|
# Tunnel will be created when needed
|
||||||
|
timeout = "30s"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure RabbitMQ provider to use the SSH tunnel
|
||||||
|
provider "rabbitmq" {
|
||||||
|
endpoint = "http://${module.ssh_tunnel.host}:${module.ssh_tunnel.port}"
|
||||||
|
username = var.admin_username
|
||||||
|
password = var.admin_password
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create the virtual host
|
||||||
|
resource "rabbitmq_vhost" "tenant_vhost" {
|
||||||
|
depends_on = [var.wait_on]
|
||||||
|
|
||||||
|
name = var.tenant_name
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create the user
|
||||||
|
resource "rabbitmq_user" "tenant_user" {
|
||||||
|
depends_on = [var.wait_on]
|
||||||
|
|
||||||
|
name = var.tenant_name
|
||||||
|
password = random_password.tenant_password.result
|
||||||
|
tags = []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grant permissions to the user on the vhost
|
||||||
|
resource "rabbitmq_permissions" "tenant_permissions" {
|
||||||
|
user = rabbitmq_user.tenant_user.name
|
||||||
|
vhost = rabbitmq_vhost.tenant_vhost.name
|
||||||
|
|
||||||
|
permissions {
|
||||||
|
configure = ".*"
|
||||||
|
write = ".*"
|
||||||
|
read = ".*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
output "host" {
|
||||||
|
description = "RabbitMQ connection host (via SSH tunnel)"
|
||||||
|
value = var.server_ip
|
||||||
|
}
|
||||||
|
|
||||||
|
output "port" {
|
||||||
|
description = "RabbitMQ AMQP connection port"
|
||||||
|
value = 5672
|
||||||
|
}
|
||||||
|
|
||||||
|
output "management_host" {
|
||||||
|
description = "RabbitMQ Management API host (via SSH tunnel)"
|
||||||
|
value = module.ssh_tunnel.host
|
||||||
|
}
|
||||||
|
|
||||||
|
output "management_port" {
|
||||||
|
description = "RabbitMQ Management API port (via SSH tunnel)"
|
||||||
|
value = module.ssh_tunnel.port
|
||||||
|
}
|
||||||
|
|
||||||
|
output "vhost" {
|
||||||
|
description = "Virtual host name"
|
||||||
|
value = rabbitmq_vhost.tenant_vhost.name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "username" {
|
||||||
|
description = "RabbitMQ username"
|
||||||
|
value = rabbitmq_user.tenant_user.name
|
||||||
|
}
|
||||||
|
|
||||||
|
output "password" {
|
||||||
|
description = "RabbitMQ password"
|
||||||
|
value = random_password.tenant_password.result
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "connection_string" {
|
||||||
|
description = "RabbitMQ AMQP connection string"
|
||||||
|
value = "amqp://${rabbitmq_user.tenant_user.name}:${random_password.tenant_password.result}@${var.server_ip}:5672/${rabbitmq_vhost.tenant_vhost.name}"
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
description = "Installation complete flag"
|
||||||
|
value = true
|
||||||
|
depends_on = [rabbitmq_vhost.tenant_vhost, rabbitmq_user.tenant_user, rabbitmq_permissions.tenant_permissions]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
variable "tenant_name" {
|
||||||
|
description = "Name of the tenant (used for vhost name and username)"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "admin_username" {
|
||||||
|
description = "RabbitMQ admin username"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "admin_password" {
|
||||||
|
description = "RabbitMQ admin password"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
description = "VPS server IP address"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_user" {
|
||||||
|
description = "SSH user for connecting to VPS"
|
||||||
|
type = string
|
||||||
|
default = "fourlights"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
description = "Path to SSH private key for authentication"
|
||||||
|
type = string
|
||||||
|
default = "~/.ssh/id_rsa"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "target_port" {
|
||||||
|
description = "RabbitMQ Management API port on VPS"
|
||||||
|
type = number
|
||||||
|
default = 15672
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "wait_on" {
|
||||||
|
description = "Resources to wait on before creating tenant"
|
||||||
|
type = any
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_url" {
|
||||||
|
type = string
|
||||||
|
default = "https://mail.binarysunset.dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
# tmail-web reads its configuration from a file at
|
||||||
|
# /usr/share/nginx/html/assets/env.file inside the container. Setting
|
||||||
|
# SERVER_URL as a container environment variable does NOT work (see
|
||||||
|
# https://github.com/linagora/tmail-flutter/issues/4240); the file must
|
||||||
|
# be injected. We write it to the host and bind-mount it into the
|
||||||
|
# container.
|
||||||
|
locals {
|
||||||
|
env_file_host_path = "/opt/storage/data/tmail-web/env.file"
|
||||||
|
env_file_container_path = "/usr/share/nginx/html/assets/env.file"
|
||||||
|
env_file_content = "SERVER_URL=${var.server_url}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "null_resource" "tmail_env_file" {
|
||||||
|
triggers = {
|
||||||
|
content = local.env_file_content
|
||||||
|
server_ip = var.server_ip
|
||||||
|
host_path = local.env_file_host_path
|
||||||
|
}
|
||||||
|
|
||||||
|
provisioner "remote-exec" {
|
||||||
|
inline = [
|
||||||
|
"mkdir -p $(dirname ${local.env_file_host_path})",
|
||||||
|
"cat > ${local.env_file_host_path} << 'EOF'",
|
||||||
|
local.env_file_content,
|
||||||
|
"EOF",
|
||||||
|
]
|
||||||
|
|
||||||
|
connection {
|
||||||
|
type = "ssh"
|
||||||
|
host = var.server_ip
|
||||||
|
user = "fourlights"
|
||||||
|
agent = true
|
||||||
|
agent_identity = var.ssh_private_key_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "tmail-web" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "tmail-web"
|
||||||
|
image = "docker.io/linagora/tmail-web:latest"
|
||||||
|
ports = ["8080:80"]
|
||||||
|
volumes = ["${local.env_file_host_path}:${local.env_file_container_path}:ro"]
|
||||||
|
|
||||||
|
environment = {}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "tmail-web"
|
||||||
|
domain = "mail.${var.server_domain}"
|
||||||
|
port = "8080"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
healthcmd = "curl -f http://localhost:80 || exit 1"
|
||||||
|
|
||||||
|
depends_on = [null_resource.tmail_env_file]
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.tmail-web.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.tmail-web.installed]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
variable "wait_on" {
|
||||||
|
type = any
|
||||||
|
description = "Resources to wait on"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_ip" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_private_key_path" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "server_domain" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
module "zot" {
|
||||||
|
source = "../quadlet-app"
|
||||||
|
wait_on = var.wait_on
|
||||||
|
|
||||||
|
server_ip = var.server_ip
|
||||||
|
ssh_private_key_path = var.ssh_private_key_path
|
||||||
|
|
||||||
|
app_name = "zot"
|
||||||
|
image = "ghcr.io/project-zot/zot:latest"
|
||||||
|
ports = ["5000:5000"]
|
||||||
|
volumes = [
|
||||||
|
"/opt/storage/data/zot/config.json:/etc/zot/config.json:Z",
|
||||||
|
"/opt/storage/data/zot:/var/lib/registry:Z",
|
||||||
|
]
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
ND_LOGLEVEL="info"
|
||||||
|
ND_ENABLEINSIGHTSCOLLECTOR="false"
|
||||||
|
}
|
||||||
|
|
||||||
|
haproxy_services = [
|
||||||
|
{
|
||||||
|
name = "registry"
|
||||||
|
domain = "registry.${var.server_domain}"
|
||||||
|
port = "5000"
|
||||||
|
host = "127.0.0.1"
|
||||||
|
tls = false
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
output "app_urls" {
|
||||||
|
value = module.zot.app_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
output "installed" {
|
||||||
|
value = true
|
||||||
|
depends_on = [module.zot.installed]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue