Compare commits
10 Commits
8324aed9e0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| cabf453a5d | |||
| 766943bbb1 | |||
| bb8e720ddf | |||
| b11d9b2fb9 | |||
| d83f4ec903 | |||
| 1ca4a59fe5 | |||
| 190c2ee5c5 | |||
| eba36f9d56 | |||
| 9d7b8082c0 | |||
| 11e7b74140 |
@@ -33,16 +33,19 @@
|
|||||||
# microvm
|
# microvm
|
||||||
./modules/microvm.nix
|
./modules/microvm.nix
|
||||||
./modules/gitea-vm.nix
|
./modules/gitea-vm.nix
|
||||||
|
./modules/forgejo-runner.nix
|
||||||
# ./modules/vscode-server.nix # Add VS Code Server microvm
|
# ./modules/vscode-server.nix # Add VS Code Server microvm
|
||||||
|
|
||||||
./modules/ai-mailer.nix
|
./modules/ai-mailer.nix
|
||||||
# ./modules/wazuh.nix
|
# ./modules/wazuh.nix
|
||||||
|
./modules/moltbot.nix
|
||||||
|
|
||||||
# web
|
# web
|
||||||
./modules/web
|
./modules/web
|
||||||
|
|
||||||
# git
|
# git
|
||||||
./modules/gitea.nix
|
./modules/gitea.nix
|
||||||
|
./modules/forgejo.nix # Migration: autoStart=false, start after migration script
|
||||||
# ./modules/fwmetrics.nix
|
# ./modules/fwmetrics.nix
|
||||||
|
|
||||||
# ha customers
|
# ha customers
|
||||||
@@ -77,7 +80,7 @@
|
|||||||
networkPrefix = "10.42";
|
networkPrefix = "10.42";
|
||||||
|
|
||||||
# Systemd services to monitor
|
# Systemd services to monitor
|
||||||
services.victoriametrics.monitoredServices = [ "ai-mailer" "container@git" "microvm@git-runner-" ];
|
services.victoriametrics.monitoredServices = [ "ai-mailer" "container@git" "microvm@git-runner-" "microvm@fj-runner-" ];
|
||||||
|
|
||||||
nixpkgs.overlays = [
|
nixpkgs.overlays = [
|
||||||
(import ./utils/overlays/packages.nix)
|
(import ./utils/overlays/packages.nix)
|
||||||
|
|||||||
@@ -102,6 +102,7 @@
|
|||||||
"/snapcast.cloonar.com/${config.networkPrefix}.97.21"
|
"/snapcast.cloonar.com/${config.networkPrefix}.97.21"
|
||||||
"/lms.cloonar.com/${config.networkPrefix}.97.21"
|
"/lms.cloonar.com/${config.networkPrefix}.97.21"
|
||||||
"/git.cloonar.com/${config.networkPrefix}.97.50"
|
"/git.cloonar.com/${config.networkPrefix}.97.50"
|
||||||
|
"/forgejo.cloonar.com/${config.networkPrefix}.97.55"
|
||||||
"/feeds.cloonar.com/188.34.191.144"
|
"/feeds.cloonar.com/188.34.191.144"
|
||||||
"/nukibridge1a753f72.cloonar.smart/${config.networkPrefix}.100.112"
|
"/nukibridge1a753f72.cloonar.smart/${config.networkPrefix}.100.112"
|
||||||
"/allywatch.cloonar.com/${config.networkPrefix}.97.5"
|
"/allywatch.cloonar.com/${config.networkPrefix}.97.5"
|
||||||
@@ -138,6 +139,7 @@
|
|||||||
"/dl.cloonar.com/${config.networkPrefix}.97.5"
|
"/dl.cloonar.com/${config.networkPrefix}.97.5"
|
||||||
"/jellyfin.cloonar.com/${config.networkPrefix}.97.5"
|
"/jellyfin.cloonar.com/${config.networkPrefix}.97.5"
|
||||||
"/audiobooks.cloonar.com/${config.networkPrefix}.97.5"
|
"/audiobooks.cloonar.com/${config.networkPrefix}.97.5"
|
||||||
|
"/moltbot.cloonar.com/${config.networkPrefix}.97.5"
|
||||||
|
|
||||||
"/deconz.cloonar.multimedia/${config.networkPrefix}.97.22"
|
"/deconz.cloonar.multimedia/${config.networkPrefix}.97.22"
|
||||||
|
|
||||||
|
|||||||
87
hosts/fw/modules/forgejo-runner.nix
Normal file
87
hosts/fw/modules/forgejo-runner.nix
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{ config, lib, pkgs, ... }: let
|
||||||
|
# Short names to fit Linux interface name limit (15 chars for vm-fj-runner-1)
|
||||||
|
runners = ["fj-runner-1" "fj-runner-2"];
|
||||||
|
# Offset by 5 to avoid conflicts with Gitea runners (01-02)
|
||||||
|
runnerOffset = 5;
|
||||||
|
in {
|
||||||
|
microvm.vms = lib.mapAttrs (runner: idx: {
|
||||||
|
config = {
|
||||||
|
microvm = {
|
||||||
|
mem = 8096;
|
||||||
|
shares = [
|
||||||
|
{
|
||||||
|
source = "/nix/store";
|
||||||
|
mountPoint = "/nix/.ro-store";
|
||||||
|
tag = "ro-store";
|
||||||
|
proto = "virtiofs";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
source = "/run/secrets";
|
||||||
|
mountPoint = "/run/secrets";
|
||||||
|
tag = "ro-token";
|
||||||
|
proto = "virtiofs";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
volumes = [
|
||||||
|
{
|
||||||
|
image = "rootfs.img";
|
||||||
|
mountPoint = "/";
|
||||||
|
size = 51200;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
interfaces = [
|
||||||
|
{
|
||||||
|
type = "tap";
|
||||||
|
id = "vm-${runner}";
|
||||||
|
mac = "02:00:00:00:00:0${toString (idx + runnerOffset)}";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.network.networks."10-lan" = {
|
||||||
|
matchConfig.PermanentMACAddress = "02:00:00:00:00:0${toString (idx + runnerOffset)}";
|
||||||
|
address = [ "${config.networkPrefix}.97.5${toString (idx + runnerOffset)}/24" ];
|
||||||
|
gateway = [ "${config.networkPrefix}.97.1" ];
|
||||||
|
dns = [ "${config.networkPrefix}.97.1" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.hostName = runner;
|
||||||
|
|
||||||
|
virtualisation.podman.enable = true;
|
||||||
|
|
||||||
|
services.gitea-actions-runner.instances.${runner} = {
|
||||||
|
enable = true;
|
||||||
|
url = "https://forgejo.cloonar.com";
|
||||||
|
name = runner;
|
||||||
|
tokenFile = "/run/secrets/forgejo-runner-token";
|
||||||
|
labels = [
|
||||||
|
"ubuntu-latest:docker://git.cloonar.com/infrastructure/gitea-runner:1.0.0"
|
||||||
|
];
|
||||||
|
settings = {
|
||||||
|
container = {
|
||||||
|
network = "podman";
|
||||||
|
};
|
||||||
|
cache = {
|
||||||
|
enabled = true;
|
||||||
|
host = "${config.networkPrefix}.97.5${toString (idx + runnerOffset)}";
|
||||||
|
port = 8088;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.openssh.enable = true;
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
allowedTCPPorts = [ 8088 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
system.stateVersion = "22.05";
|
||||||
|
};
|
||||||
|
}) (lib.listToAttrs (lib.lists.imap1 (i: v: { name=v; value=i; }) runners));
|
||||||
|
|
||||||
|
sops.secrets.forgejo-runner-token = {};
|
||||||
|
}
|
||||||
152
hosts/fw/modules/forgejo.nix
Normal file
152
hosts/fw/modules/forgejo.nix
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cids = import ../modules/staticids.nix;
|
||||||
|
domain = "git.cloonar.com";
|
||||||
|
networkPrefix = config.networkPrefix;
|
||||||
|
|
||||||
|
user = {
|
||||||
|
isSystemUser = true;
|
||||||
|
uid = cids.uids.forgejo;
|
||||||
|
group = "forgejo";
|
||||||
|
home = "/var/lib/forgejo";
|
||||||
|
createHome = true;
|
||||||
|
};
|
||||||
|
group = {
|
||||||
|
gid = cids.gids.forgejo;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
users.users.forgejo = user;
|
||||||
|
users.groups.forgejo = group;
|
||||||
|
|
||||||
|
# Reuse the existing git.cloonar.com ACME cert from gitea.nix
|
||||||
|
security.acme.certs."forgejo.cloonar.com" = {
|
||||||
|
group = "nginx";
|
||||||
|
};
|
||||||
|
|
||||||
|
containers.forgejo = {
|
||||||
|
autoStart = false; # Don't start until migration is complete
|
||||||
|
ephemeral = false; # because of ssh key
|
||||||
|
privateNetwork = true;
|
||||||
|
hostBridge = "server";
|
||||||
|
hostAddress = "${networkPrefix}.97.1";
|
||||||
|
localAddress = "${networkPrefix}.97.55/24"; # Different from gitea's .50
|
||||||
|
bindMounts = {
|
||||||
|
"/var/lib/forgejo" = {
|
||||||
|
hostPath = "/var/lib/forgejo/";
|
||||||
|
isReadOnly = false;
|
||||||
|
};
|
||||||
|
"/var/lib/acme/forgejo/" = {
|
||||||
|
# hostPath = config.security.acme.certs.${domain}.directory;
|
||||||
|
hostPath = config.security.acme.certs."forgejo.cloonar.com".directory;
|
||||||
|
isReadOnly = true;
|
||||||
|
};
|
||||||
|
"/run/secrets/forgejo-mailer-password" = {
|
||||||
|
hostPath = config.sops.secrets.forgejo-mailer-password.path;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = { lib, config, pkgs, ... }: {
|
||||||
|
imports = [
|
||||||
|
../fleet.nix
|
||||||
|
../modules/cloonar-assistant-config-server.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
vim # my preferred editor
|
||||||
|
];
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
hostName = "forgejo";
|
||||||
|
useHostResolvConf = false;
|
||||||
|
defaultGateway = {
|
||||||
|
address = "${networkPrefix}.96.1";
|
||||||
|
interface = "eth0";
|
||||||
|
};
|
||||||
|
firewall.enable = false;
|
||||||
|
nameservers = [ "${networkPrefix}.97.1" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx.enable = true;
|
||||||
|
services.nginx.virtualHosts."${domain}" = {
|
||||||
|
sslCertificate = "/var/lib/acme/forgejo/fullchain.pem";
|
||||||
|
sslCertificateKey = "/var/lib/acme/forgejo/key.pem";
|
||||||
|
sslTrustedCertificate = "/var/lib/acme/forgejo/chain.pem";
|
||||||
|
forceSSL = true;
|
||||||
|
extraConfig = ''
|
||||||
|
client_max_body_size 2048M;
|
||||||
|
'';
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://localhost:3001/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.forgejo = {
|
||||||
|
enable = true;
|
||||||
|
stateDir = "/var/lib/forgejo";
|
||||||
|
settings = {
|
||||||
|
DEFAULT = {
|
||||||
|
APP_NAME = "Cloonar Forgejo server";
|
||||||
|
};
|
||||||
|
server = {
|
||||||
|
ROOT_URL = "https://${domain}/";
|
||||||
|
HTTP_PORT = 3001;
|
||||||
|
DOMAIN = domain;
|
||||||
|
};
|
||||||
|
repository = {
|
||||||
|
DEFAULT_BRANCH = "main";
|
||||||
|
};
|
||||||
|
openid = {
|
||||||
|
ENABLE_OPENID_SIGNIN = false;
|
||||||
|
ENABLE_OPENID_SIGNUP = true;
|
||||||
|
WHITELISTED_URIS = "auth.cloonar.com";
|
||||||
|
};
|
||||||
|
service = {
|
||||||
|
DISABLE_REGISTRATION = false;
|
||||||
|
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
|
||||||
|
SHOW_REGISTRATION_BUTTON = false;
|
||||||
|
ENABLE_NOTIFY_MAIL = true;
|
||||||
|
REQUIRE_SIGNIN_VIEW = false;
|
||||||
|
};
|
||||||
|
mailer = {
|
||||||
|
ENABLED = true;
|
||||||
|
FROM = "Forgejo Cloonar <gitea@cloonar.com>";
|
||||||
|
PROTOCOL = "smtp+starttls";
|
||||||
|
SMTP_ADDR = "mail.cloonar.com";
|
||||||
|
SMTP_PORT = 587;
|
||||||
|
USER = "gitea@cloonar.com";
|
||||||
|
};
|
||||||
|
actions = {
|
||||||
|
ENABLED = true;
|
||||||
|
DEFAULT_ACTIONS_URL = "github"; # Pull actions from GitHub
|
||||||
|
};
|
||||||
|
attachment = {
|
||||||
|
MAX_SIZE = 2048; # 2GB in MB for general attachments
|
||||||
|
};
|
||||||
|
packages = {
|
||||||
|
ENABLED = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Configure mailer password
|
||||||
|
systemd.services.forgejo.serviceConfig.EnvironmentFile = "/run/secrets/forgejo-mailer-password";
|
||||||
|
|
||||||
|
services.openssh.enable = true;
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.forgejo = user;
|
||||||
|
users.groups.forgejo = group;
|
||||||
|
|
||||||
|
system.stateVersion = "23.05";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets.forgejo-mailer-password = {
|
||||||
|
owner = "forgejo";
|
||||||
|
# restartUnits removed - would start the container even with autoStart=false
|
||||||
|
# Re-add after migration: restartUnits = [ "container@forgejo.service" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ in {
|
|||||||
microvm.vms = lib.mapAttrs (runner: idx: {
|
microvm.vms = lib.mapAttrs (runner: idx: {
|
||||||
config = {
|
config = {
|
||||||
microvm = {
|
microvm = {
|
||||||
mem = 4048;
|
mem = 8096;
|
||||||
shares = [
|
shares = [
|
||||||
{
|
{
|
||||||
source = "/nix/store";
|
source = "/nix/store";
|
||||||
|
|||||||
27
hosts/fw/modules/home-assistant/coming-home.nix
Normal file
27
hosts/fw/modules/home-assistant/coming-home.nix
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
services.home-assistant.config = {
|
||||||
|
rest_command = {
|
||||||
|
moltbot_home_arrival = {
|
||||||
|
url = "https://moltbot.cloonar.com/hooks/agent";
|
||||||
|
method = "POST";
|
||||||
|
headers = {
|
||||||
|
Authorization = "!secret moltbot_home_arrival";
|
||||||
|
Content-Type = "application/json";
|
||||||
|
};
|
||||||
|
payload = ''{"message":"Home arrival. Read memory/arrival-reminders.json silently. For each item: if it's a task (fetch weather, check calendar, look something up, etc.), execute it. If it's a simple reminder, include it. Combine everything into ONE message with just the results - no preamble, no explanations, no mentioning files or process. Then clear the file. If empty: reply NO_REPLY","name":"HomeArrival","deliver":true,"channel":"whatsapp","to":"+436607055308"}'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"automation home_arrival" = {
|
||||||
|
alias = "home_arrival";
|
||||||
|
trigger = {
|
||||||
|
platform = "zone";
|
||||||
|
entity_id = "person.dominik";
|
||||||
|
zone = "zone.home";
|
||||||
|
event = "enter";
|
||||||
|
};
|
||||||
|
action = {
|
||||||
|
service = "rest_command.moltbot_home_arrival";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -101,6 +101,9 @@ in
|
|||||||
./shelly.nix
|
./shelly.nix
|
||||||
./sleep.nix
|
./sleep.nix
|
||||||
./snapcast.nix
|
./snapcast.nix
|
||||||
|
|
||||||
|
./coming-home.nix
|
||||||
|
./morning-active.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
let
|
let
|
||||||
devices = [
|
persons = [
|
||||||
"device_tracker.dominiks_iphone"
|
"person.dominiks"
|
||||||
"device_tracker.dominiks_mp01"
|
|
||||||
"device_tracker.dominiks_fairphone_6"
|
|
||||||
];
|
];
|
||||||
in {
|
in {
|
||||||
services.home-assistant.extraComponents = [
|
services.home-assistant.extraComponents = [
|
||||||
@@ -14,18 +12,12 @@ in {
|
|||||||
alias = "house_door";
|
alias = "house_door";
|
||||||
mode = "restart";
|
mode = "restart";
|
||||||
trigger = {
|
trigger = {
|
||||||
platform = "state";
|
platform = "zone";
|
||||||
entity_id = devices;
|
entity_id = "person.dominik";
|
||||||
from = "not_home";
|
zone = "zone.home";
|
||||||
to = "home";
|
event = "enter";
|
||||||
};
|
};
|
||||||
action = [
|
action = [
|
||||||
{
|
|
||||||
service = "script.turn_on";
|
|
||||||
target = {
|
|
||||||
entity_id = "script.turn_on_circuits";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
service = "lock.unlock";
|
service = "lock.unlock";
|
||||||
target = {
|
target = {
|
||||||
|
|||||||
76
hosts/fw/modules/home-assistant/morning-active.nix
Normal file
76
hosts/fw/modules/home-assistant/morning-active.nix
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
services.home-assistant.config = {
|
||||||
|
# Track if morning hook already triggered today
|
||||||
|
input_boolean = {
|
||||||
|
morning_active_triggered = {
|
||||||
|
name = "Morning Active Triggered";
|
||||||
|
icon = "mdi:weather-sunny";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# REST command to call Moltbot
|
||||||
|
rest_command = {
|
||||||
|
moltbot_morning_active = {
|
||||||
|
url = "https://moltbot.cloonar.com/hooks/agent";
|
||||||
|
method = "POST";
|
||||||
|
headers = {
|
||||||
|
Authorization = "!secret moltbot_home_arrival"; # reuse same token
|
||||||
|
Content-Type = "application/json";
|
||||||
|
};
|
||||||
|
payload = ''{"message":"Morning briefing. Give a brief, friendly summary: 1) Today's weather for Vienna 2) Calendar events for today (check CalDAV) 3) Any pending reminders. Keep it concise, no fluff. Just the info.","name":"MorningBriefing","deliver":true,"channel":"whatsapp","to":"+436607055308"}'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Main automation: detect morning activity
|
||||||
|
"automation morning_active" = {
|
||||||
|
alias = "morning_active";
|
||||||
|
trigger = [
|
||||||
|
{
|
||||||
|
platform = "state";
|
||||||
|
entity_id = "light.toilet_lights";
|
||||||
|
to = "on";
|
||||||
|
}
|
||||||
|
# Future: add kitchen motion sensor here
|
||||||
|
# {
|
||||||
|
# platform = "state";
|
||||||
|
# entity_id = "binary_sensor.kitchen_motion";
|
||||||
|
# to = "on";
|
||||||
|
# }
|
||||||
|
];
|
||||||
|
condition = [
|
||||||
|
{
|
||||||
|
condition = "time";
|
||||||
|
after = "05:00:00";
|
||||||
|
before = "12:00:00";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
condition = "state";
|
||||||
|
entity_id = "input_boolean.morning_active_triggered";
|
||||||
|
state = "off";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
action = [
|
||||||
|
{
|
||||||
|
service = "input_boolean.turn_on";
|
||||||
|
target.entity_id = "input_boolean.morning_active_triggered";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
service = "rest_command.moltbot_morning_active";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Reset automation: reset triggered state at 3:00 AM
|
||||||
|
"automation morning_active_reset" = {
|
||||||
|
alias = "morning_active_reset";
|
||||||
|
trigger = {
|
||||||
|
platform = "time";
|
||||||
|
at = "03:00:00";
|
||||||
|
};
|
||||||
|
action = {
|
||||||
|
service = "input_boolean.turn_off";
|
||||||
|
target.entity_id = "input_boolean.morning_active_triggered";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -23,12 +23,10 @@
|
|||||||
"automation arrive home power" = {
|
"automation arrive home power" = {
|
||||||
alias = "arrive home power";
|
alias = "arrive home power";
|
||||||
trigger = {
|
trigger = {
|
||||||
platform = "state";
|
platform = "zone";
|
||||||
entity_id = [
|
entity_id = "person.dominik";
|
||||||
"device_tracker.dominiks_iphone"
|
zone = "zone.home";
|
||||||
];
|
event = "enter";
|
||||||
from = "not_home";
|
|
||||||
to = "home";
|
|
||||||
};
|
};
|
||||||
action = [
|
action = [
|
||||||
{
|
{
|
||||||
|
|||||||
58
hosts/fw/modules/moltbot.nix
Normal file
58
hosts/fw/modules/moltbot.nix
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
{
|
||||||
|
# Moltbot - AI assistant with WebChat
|
||||||
|
# Container with browser support for web automation
|
||||||
|
|
||||||
|
virtualisation.oci-containers.backend = "podman";
|
||||||
|
|
||||||
|
# Secret for gateway authentication token
|
||||||
|
sops.secrets.moltbot-gateway-token = {
|
||||||
|
key = "moltbot-gateway-token";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Persistent directories on host for backup
|
||||||
|
# UID 1000 is the 'node' user inside the container
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /var/lib/moltbot 0755 1000 1000 - -"
|
||||||
|
"d /var/lib/moltbot/home 0755 1000 1000 - -"
|
||||||
|
"d /var/lib/moltbot/extensions 0755 1000 1000 - -"
|
||||||
|
"d /run/moltbot 0700 root root - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
virtualisation.oci-containers.containers.moltbot = {
|
||||||
|
image = "ghcr.io/moltbot/moltbot:main";
|
||||||
|
|
||||||
|
# Run gateway mode, bind to all interfaces in container
|
||||||
|
cmd = [ "dist/index.js" "gateway" "--bind" "lan" "--port" "18789" "--allow-unconfigured" ];
|
||||||
|
|
||||||
|
ports = [
|
||||||
|
"${config.networkPrefix}.97.1:18789:18789" # Gateway/WebChat
|
||||||
|
"${config.networkPrefix}.97.1:18790:18790" # Bridge
|
||||||
|
];
|
||||||
|
|
||||||
|
volumes = [
|
||||||
|
"/var/lib/moltbot/home:/home/node:rw"
|
||||||
|
"/var/lib/moltbot/extensions:/app/extensions:rw"
|
||||||
|
];
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
HOME = "/home/node";
|
||||||
|
TERM = "xterm-256color";
|
||||||
|
MOLTBOT_STATE_DIR = "/home/node/.moltbot";
|
||||||
|
CLAWDBOT_STATE_DIR = "/home/node/.moltbot";
|
||||||
|
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "false";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraOptions = [
|
||||||
|
"--pull=newer"
|
||||||
|
"--network=server"
|
||||||
|
"--ip=${config.networkPrefix}.97.60"
|
||||||
|
"--init"
|
||||||
|
# Chrome sandbox capabilities
|
||||||
|
"--cap-add=SYS_ADMIN"
|
||||||
|
"--security-opt=seccomp=unconfined"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
pyload = 10006;
|
pyload = 10006;
|
||||||
jellyfin = 10007;
|
jellyfin = 10007;
|
||||||
filebot = 10008;
|
filebot = 10008;
|
||||||
|
forgejo = 10009;
|
||||||
};
|
};
|
||||||
gids = {
|
gids = {
|
||||||
unbound = 10001;
|
unbound = 10001;
|
||||||
@@ -18,5 +19,6 @@
|
|||||||
pyload = 10006;
|
pyload = 10006;
|
||||||
jellyfin = 10007;
|
jellyfin = 10007;
|
||||||
filebot = 10008;
|
filebot = 10008;
|
||||||
|
forgejo = 10009;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
proxyPass = "https://git.cloonar.com/";
|
proxyPass = "https://git.cloonar.com/";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
services.nginx.virtualHosts."forgejo.cloonar.com" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
acmeRoot = null;
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://${config.networkPrefix}.97.55:3001/";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
services.nginx.virtualHosts."foundry-vtt.cloonar.com" = {
|
services.nginx.virtualHosts."foundry-vtt.cloonar.com" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
@@ -94,4 +103,31 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
services.nginx.virtualHosts."moltbot.cloonar.com" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
acmeRoot = null;
|
||||||
|
|
||||||
|
# Restrict to internal networks only (LAN + VPN)
|
||||||
|
extraConfig = ''
|
||||||
|
allow ${config.networkPrefix}.96.0/24;
|
||||||
|
allow ${config.networkPrefix}.97.0/24;
|
||||||
|
allow ${config.networkPrefix}.98.0/24;
|
||||||
|
deny all;
|
||||||
|
'';
|
||||||
|
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://${config.networkPrefix}.97.60:18789";
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +1,72 @@
|
|||||||
ai-mailer-imap-password: ENC[AES256_GCM,data:q9eJ9Tom+X6KxQJhWQTUB61k5A==,iv:FH+IUWi2yZBBgMiL/kNW470GEVHEG3fImf0bel9og/c=,tag:RSlcpXwmNyLB8Oc/K2Epvw==,type:str]
|
ai-mailer-imap-password: ENC[AES256_GCM,data:gLSr5s/9YGd8DOD23k/MGZU58Q==,iv:ELdtCuD7Geofd9ElapMVX4UZ0gZgTtVvJpaDmY2NUq0=,tag:g4/ENc7/0PyUvY4VSg+mqQ==,type:str]
|
||||||
ai-mailer-openrouter-key: ENC[AES256_GCM,data:EvI0BuCBA1uYOderjAVcB8RSk7un7tiKmgsSe70KQcmfu3CxmQerP/2kQsRTJ0/6pWf4QqNpaes691O3nf+UG1qgG2CUcIaYRQ==,iv:OYEy0xMs+vkGa0qMtY4UP/iol5JPQ0eFVyPpPXLAmUE=,tag:5PeXZcI8TRSUOyuKs0STWg==,type:str]
|
ai-mailer-openrouter-key: ENC[AES256_GCM,data:2y9JyDBYzo9Tcx+t8rrr/TleS9Lq2D6jOVSCnm99DBMauJ1QlfLIJ4zXpX0gebxGb8BPA0jBYnJdNQxHfjvYJVmnG7+qIw7zCA==,iv:ytkagoqtrT9kGqUFo6xrXNJp4LKSO6UNGjWZemCg2A4=,tag:0OoSoYchvMUYNUi1MclWOQ==,type:str]
|
||||||
borg-passphrase: ENC[AES256_GCM,data:GGmf09zX5wQ8Fih1EyP1p3up9ckFjVKsktU6ZFwvuZnG/O2OyOod66qXc/IXx8GQordubZ3TgisOeMLNnSowp2qylh8=,iv:fFgw/x8Ww9cInkNlPIoE3stUfISbfk46PBj7aimuXNA=,tag:hnNYrkLgt1qJc+gN5s9L2Q==,type:str]
|
borg-passphrase: ENC[AES256_GCM,data:ajkDfsz1sLcxcM5VEsU8z8opB4qLXZr6BdOc5IxX4OKb/8cckd341+mXk431IWuN6bLpd1XmINimLRLin9bnb6y29L0=,iv:w8VsAJrbkBLIjR8o5L3L1l6xgsLEa1cdyEAVqfCE8y0=,tag:PvhBSrp4n9oyqskekEDBQA==,type:str]
|
||||||
borg-ssh-key: ENC[AES256_GCM,data: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,iv:f7rBK8aNqX8dGyzjoeRX6yl20XsnLU8b4gitaw9+O+0=,tag:WvfUw1JgFBAtS3vsVIvM6Q==,type:str]
|
borg-ssh-key: ENC[AES256_GCM,data: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,iv:XOQg3GqMhWAWJdLgcw6wLi/Jw0KZp4YpuoY5MhzizoU=,tag:2AHG2lyRClCa96qBZM9MLA==,type:str]
|
||||||
ddclient: ENC[AES256_GCM,data:dS6TVVNb6R7EE1JVMDfSnRYCZyHHqEPvwaYpkTSj+VA=,iv:9uMo+9X7dFdVW4wuSgrqIAaQelXuA4cek2oif0GRHow=,tag:ncQq4UeUzWtjPNxEUOlqNA==,type:str]
|
ddclient: ENC[AES256_GCM,data:bB0gOu82+124M8d+AcTrhnaexZn3IRx18OM7JkdXpdo=,iv:o7pI+mMlD11TVK7dpf1pIKLWZjFoJE0BUW+FWB1CNkk=,tag:2eiyrhFAfCRwh8kx+ox6VA==,type:str]
|
||||||
filebot-license: ENC[AES256_GCM,data:jY7E29fFJ/h9NIgIjuX++WBhnLk6Mm4iRfMh4P0pUDdqH231gXDsTZ6pJ1rpFXdEHSuNN4LfznDTKgZ2azKid4WprDUzGkN0uJD6CfSR8gTIx5Rq0M8vkRah51LC36bop4hTMzECYQd1YA47hOBV/gfyg3RIw95coWamV9FebnQjIBgWYxE+wTvO5iRvWpiCHd6VZQfkiiR0KF1DrkYkuxlX0piGEKmIgyYCiKMFZ4nrrIe58x5lEQA9uPVjE7vmq3c3ge6tJzjVVaaNocbJhxhA18GLMqTSHfnBsOLRlA8qSQ3xX/VRzKQmaYQHIM77Ylb9ZQsvFt6EDlzQMl5NqT7OJZUW/0jwNaEXHURjeTOC3Hr1HugiDGm+uLXEraaJ6Na2AbFDn28o+3J22p9xNg6vWL0FElzKuaz5TFDzdZLZsD9HOPQm95/ZM8JymDjN4qxkkd2o9rEKY6to1MVDarj+lDxIHhf4pL23YhZsn3esNlEbFswzHQiH7nMsu9Jg6a0rPu7IYylDnH/soBjxSKmf2dhH1LLsDm8It9K/7NnXwmvncFXaqNBqm/e7JzCDBCCyVUf/BXbBc3xwLwZf5MiirZ/iYiYnRtUssveh7BV7ICigRj5Ewtr+n97+IGI+FyonkvOgM0bn8nHf79ZzJCKMuntcw3FlGd1nIkmcehkC79PlKIS95oV/wypl1OmU0CVel+D8hsMuONmF9NPHgFk/ztp4GF+XXRO4ExNotX1XrUlvLOccoHDsl1TedUOISzgAK71edxfI8y110shIe9OfsCEUAbmWMmjGVWH2fKu/IrYYQTry6pYFOjG2bIEUXMaiIP0lALbq/QNgleqMNPY8wGzFbP+/jaYzTbw9KXH4bwYQCl1hSI8THfV7lLE=,iv:4ik/aQqi/hIqH8ix3ejgUiXGY7ycw0ymdVrV+CEQe1o=,tag:7ymc4QZEezJVPlYTlU4H/g==,type:str]
|
filebot-license: ENC[AES256_GCM,data:twfY90M2Qq4T0B2yXwFw1hW94JIjCsgXDtXw+sjJxxCwn3t3A7cil64jJ4cjSFHf7gnT6/ijgGVBh70+DzurSI5F5XhIg9vpl+NtNvbvNRwVfO+tvBgFsDpmhZ4iAY+9b4uJFSF3BRHF6cNfK7imRkQCrNLBxxrRKdL3TWqWrSyz8k8OCs9oVHXUTLv1qkdcOn8R0a3c9CM+u1/FA3d7wFVAhdgj8T7mubbBAtv4CJFrU9Qm0KZSx1PpRFrwjHTIzLL1on40SExhQLrKMzQs/Abv2+p6QeSAMWRFc7RKuWOyQUF+Cti+a7q63DZn/IlOUeDvdnjzqtv6xTdh2jcBb7zujXgkkBOvo9PosF4hYL5LMpl5IKKz67lVplo9CHiDOWDdjgfel331ItVJrPnwQFpv0EanzjyWC98/WVjOrLqAK+zXsayDrVpYOGGAiCmBe5ucJSy7xQV/z1qMQxVDqw+zt6e2HLz4zfMBWLTzxtOjX/eLSk/omcrpPx+wDWjXBjFJtcLHvTtdq3nU8i/a7XFCKsywu9UkyShqaYj0zS3+pg4GkolcpohH9c/fQTX1HFOdRXFgMoRd5pgvhHE3aAGXr83d+Euvdo/NuKnQWbU6XYWeOwJLRevayoACd8luIAB/gm+1lTJux0Vjxj6VEbDxJEXIqL7C9tDF2ikfYcrMytCrA/ZcBRJujIBLoOpCTiFhbHjT6F03T6Np49qOxYRIHSA/GaZzGHhBpb7srZVsSi5qhTfOl+EiiShsgmZjhse0lXoyNWBllo5Dy3E9vB26d2QL0lEI30kqrO4XdgaLtZuCC4lWP/RxHXbYSah1xm5K3T8z454mGBRHJqMHrCMEhuk1NHdX07xLbVeGM8qnfX9y519hzQEPSf6Cmz0=,iv:wWL5EcM25VSjsAB79FO5lv+8/q5JBYd34dhIyyjJiuY=,tag:MyaQAWslwW3caXE/XiRdNw==,type:str]
|
||||||
gitea-mailer-password: ENC[AES256_GCM,data:lEv5euTCHG6pyNqrVtKK7oE8wLvk+q8ABXOzFSizQ2TVFi35lyGPzOTel/dCCC0Je5GAHE1KQQ4Y4/iHghZgb5Ft,iv:gt/mCzLbDrHFNqW+Lkd2dy9nRIBKO+rqsVuXM45zJ8k=,tag:gCxTSzY7GZ+jQP9SCsdUtw==,type:str]
|
forgejo-mailer-password: ENC[AES256_GCM,data:anUrMCIKbWCqNSN5HJKjMaqhlXVT+QsKfi1YdW4sDKACzL9LpMbdT4cThr779QDSvGFhbRuTysEs0jEQjDUdam00,iv:pBlGfyuPbKzp+QXHlR3eZpvy6Uhcj5rM3T1rx47P+us=,tag:lCcNRj7xo90kx0dknRU4Vw==,type:str]
|
||||||
gitea-runner: ENC[AES256_GCM,data:HLjSETmu2C2ROf6kqUuIzQl/t4Fe5EOVkMqdTeLNnb6AJ95l6M/WUk//dnPMrWVvEq7rV07awUiyvyJcYQzMgPNddCrfcn2Xr0dYK4XFenz/sdhknVex9uS/RhK8fOqdYJ6djpynikMKddZMQr9AOVfpF5mea//87+Az9rOrlzLdgNtf5HyBEAFKaOFbkZboAsP+jlxyyYurGHPr8LxxikewDVxnpB+XzMc6RAnesrZPOTDQlkMiPZ2t2o0klhD/4VomgiHEklULxCCmIAHaqDo=,iv:1FwTespqVTnKFbyf9Unbbod08D36MKsVbDhIBNGBkHg=,tag:rgVvyxUCwzYB2CqWm2fwgg==,type:str]
|
forgejo-runner-token: ENC[AES256_GCM,data:HPn7kdxG570G0R74oT8IhGb+ZgIOgiqzio+GAPBXuO1Enq5ygm9xsFPeY+m7kBM=,iv:Sc9oRZctOAe9JEAy+JotKFFErMA3J0lc+0S6N1W+MGo=,tag:PY8G6SasJgpZUP25CP1r1g==,type:str]
|
||||||
gitea-runner-token: ENC[AES256_GCM,data:pzJp7j1Ktz+27oU+qtESk7D32w7+BSEUkPSX4xuFml0i10z12Gzu0QHXL9s3734=,iv:U77b5515H1URfz5BCdzuY03zVkhSRsL9d+HdHUJFx9U=,tag:QvooaT4TS/X5R5KGdaVpVQ==,type:str]
|
gitea-mailer-password: ENC[AES256_GCM,data:ahsBBVjmUse9VrZOGQ++3C4WVOkFHJdTPYg3b3PGowdHheZkoSe2uEeKmnflDPHGD+lMtFoLAES18pIv8G2/tDAr,iv:QADR4/YZ4ikJskcHwfqiGvnCKB7WG4VTDtJkVuNaho0=,tag:E8WSmvw6IwLa6CxaVu9GhA==,type:str]
|
||||||
home-assistant-ldap: ENC[AES256_GCM,data:4kofJzPbiLXILxjuAZWiTb9hu2Gver/IHBCXDnrmrKuCSII6SJ9FrSi67nl7SHdoA6xe22GSMfmPrKzy5sGiow==,iv:F8mIHhWHpaI6kzRV9du6uW/Fj07PbEIU1goSDmeSD5E=,tag:6NIC6sN8OclinribZhrLLw==,type:str]
|
gitea-runner: ENC[AES256_GCM,data:eoGF7AlQqGWUQT1mtbgGFhloDd8WJp9qcc6XNohWz4oLS3Y3hdx2hcBL6VnF/vgtXZOHLZ9Bib3JFEzViYDf1p1gouvcfsK/4hKNfsoe5rswKvPRb3m4jDJnuOUf8JCFoh5XYBjCH6X9EG7WHtWTzYprRJ9EzMLwIHUyGULT2BmfLNHkEBDkfPffp5Rh2Kc/d2VpGM/qBDkDb6eDskiXC0UeOHfPyIyDsORD9bWx+1YYiUu1S7fpLD5nlN0JW3eaw683yvczNsgSoR1DWl5/6/I=,iv:UtRDVC1TATS2I0wWXHfOrfgFTJpML9TS9AN2sXGqtPA=,tag:XhDdZl66RRvxGNWYK8iQTg==,type:str]
|
||||||
home-assistant-secrets.yaml: ENC[AES256_GCM,data:rns9heAmVMxB6WWlGMXvF/ianFUnja3FObiLTEKJmodePNsJ8ah3OhuCAX5jON+/7NZ+3JN/hIJjXsORC5WYhr01DvO9meykf0aMpbmAnYI+cmPEPvcunF4NNInl96rpcI519nMiHDSh5J7pD74CxHZcXSV4c9ZR5UBymchrwmHyZMF6dVrD9Jbr9yph1r7iq6S5wlI2ZImWRjaoGDZ1x+ZU8XnsUmYcP4pa1Yt8JBxSnyUw5gxgBkVCh4eSZBsUCt0cd9P0i7qWVg==,iv:YXQsawXZsQb9ZUt1/lkpfTa4tfKIQrLkkyShFtBRaIQ=,tag:/vSnipGiMntdMqHLePSEQw==,type:str]
|
gitea-runner-token: ENC[AES256_GCM,data:7z3aE/HNuZ0H8wsc/cy5ZiX0cBjtEUYPU7vabkh9AXgOBd0Gfv+bCyrCzvN8MyI=,iv:VYfJw/g2R5Unok+e9/wJjHS4gYNmbF+yxoRzyHsm8iU=,tag:mLwUu1GSWcq7vzc9PEJKWQ==,type:str]
|
||||||
piped-db-password: ENC[AES256_GCM,data:5atQccdHYDEf638bpiON9VO14jqNDtzZ8nnXVW0/cqtWkZJc8RYn9N7QhAw=,iv:Gwyf1R+mpmX+TFuoYLPHjXwSDwzJhSEpnj5ZsJgmrtk=,tag:zm4zNkzbqbCyTN6o3lQQfg==,type:str]
|
home-assistant-ldap: ENC[AES256_GCM,data:P+yqFcbfqQvgzNj3wu488HgTUFd7bE35cQCpe2nWUQ1SqsXVT4+Q8i+WlnpWaxLAP0QlWQqKBzqUJiU3/k9PWA==,iv:VjlAXLAs134gopU4oaKaPoHfTKoEK5SUlD+IuMw+3hQ=,tag:G0RFhr4AOXbhCSJPJA35Kg==,type:str]
|
||||||
pushover-api-token: ENC[AES256_GCM,data:cMBDdySEBQ7vS7FUC2DsCcSvEMpapWvMFmnuCsY6,iv:SVDrrDm2pcAfwUVAC5j47YwF4s/FWNARlZdIZ1Wgwgw=,tag:w7ZeNMPXWc9j+zVaSxq1cQ==,type:str]
|
home-assistant-secrets.yaml: ENC[AES256_GCM,data:naM/fFaLtlRWEkVaCkfUa1RvdYK/pJl3mREGSI3QA+3vqOGRj46yTDdTvBhcdi6hKRatJr9HJMj229gyJSneUUFIb1cz+rPyrXnIxBMl9fsjQfBF8s7YoZy1UJxO8TIrdBkgKPKg+olk8aoR2jkafEwix96g8JR8C3nqJF86JT+LgJ4jeoPDBLUG3Ae01fRNkhKWbo1JK3RCp61m/cR6Mp9H+EbgO1bQ9puRCAXESabEwF/TgcQQuv56h9v1glU9kqfe602zOzyUxuUOo1VB9+lRCiAV462vtZ99kKxIvRbNWd4PQ0xoPI5j7mTkXIpxZSUkrIsXdrbZuAYvHERD,iv:KycHSWt6nXdf9MoRf7cNWJgQ3e3JYK6gbJhSnHu3/2Q=,tag:QmiYIF1FYjDa3I86KB9oMA==,type:str]
|
||||||
pushover-user-key: ENC[AES256_GCM,data:fjoA2YQxmeWEbSKWWE5iyi+CUh1vtW9usVCm5EGk,iv:p4YwYIhpgn/bY9t61//CDrDmZrsj9B/naZit62lCpwo=,tag:pqEw3pDlX7i87tE0Nsy0/Q==,type:str]
|
moltbot-gateway-token: ENC[AES256_GCM,data:TIw7yqHbyNLdka0PHCrX1UNgK+PYj13sjJY9QoyMVIuMvFhFh1Fg9I8vTqD5/AWCypkcmmQullx3t/rOU/NI3Q==,iv:fkZn4u81Q+ZdEBM8l4YVhDVpAqdLEMFXRQMuZ3mdeC0=,tag:/ZFOiNCvI1holTkOtvgF9Q==,type:str]
|
||||||
wrwks_vpn_key: ENC[AES256_GCM,data:VEHqnr/bDtmyLzs0wnmZ0jCWS0BGJWu6Wjq0ZHJuEz8PH3j/E54S9NUe6WRIo+BJCsh1PlRqw/PD9xSqlW5uPg==,iv:OMP0s8Lc2CmFgwRuwB3UWJVuQFqvpy+BiyhnIKbVIb8=,tag:x1LvSf6i8khd8jKgv/284g==,type:str]
|
piped-db-password: ENC[AES256_GCM,data:JM1ZyHOhYDo+fgiVRrYB+iF6ITL+hSpVY+h/xVH+aP85HEoaF+Ryo3iFxpk=,iv:iM67fueJ1ebGF79Mj/6YH8mEDc6uz0uTUGsKF43xhAI=,tag:oPBws8hO0fmS+o859RdsMQ==,type:str]
|
||||||
wg_cloonar_key: ENC[AES256_GCM,data:1OfHD8yX+pgCXqqxn7cddnnCA9HBjGra4eht7uLxdcbdG9vDvxUoE1x6aWg=,iv:/NBEbmA3wP/zwrqCeBKDzaoSMqz3f4ZeMlWbu81R5Pg=,tag:Apt8x/j0qiJAKR4UEVSkrA==,type:str]
|
pushover-api-token: ENC[AES256_GCM,data:EBdqKj3ac/H9vYWdMWBKuRo18ucuAZHXEiS2LNLW,iv:vIx2/15QgfT14GcYFVdUcsNEk3On5nZ8jbqeP5fFwG8=,tag:sR+j0iqjbMPaFePWVRID4g==,type:str]
|
||||||
wg_epicenter_works_key: ENC[AES256_GCM,data:CTZkVGEVRlCdt6W0BGPmX0SZbuBBH5IIlUsi44SGXi7gdmrZNwv2zDv6zjA=,iv:4ZDDKqR6pBq8cjX763tBxOvWFaS2IiGaBxJu6L2JYig=,tag:H8p63BvXSx1SKPFw5gnptw==,type:str]
|
pushover-user-key: ENC[AES256_GCM,data:/dKxdB/eM0MtNSVcr4NYGv7tw1Cvkge8p/HcWv/+,iv:RzLuLyg+2KSGH9UW2495KeKEyiTo5OzMWtlZhgg48uw=,tag:2q7rAvy8bWyLPLNONmagig==,type:str]
|
||||||
wg_epicenter_works_psk: ENC[AES256_GCM,data:K0SDlDWfUk9vIGP5U1j8p6TJ9GsydJTuKPb4kMgde1CILOia0S9/+4AkMWY=,iv:ITwLoWZXR6NxRFF3eBvOogiWHLmXnf7S1e2FW0ofr/M=,tag:2OVi3OBFYT0nlCx8gf2AdA==,type:str]
|
wrwks_vpn_key: ENC[AES256_GCM,data:8LmRG8yVFfMTwgRnT5dQg5H0b5Yaz/fM15l4TsaVaEQ0PZsSHY2PvVacv+6iZdDZOeyVZfslg+12dCD5OicN3g==,iv:QGRs/d8HK77PwJRpGFu+7ciX7sqs8ZV+3KEh2BlHZ/M=,tag:EwebFPtI4TfAR7b9ps7vJw==,type:str]
|
||||||
wg_ghetto_at_key: ENC[AES256_GCM,data:+bonpVjV1hxwaqtR7ywshmoDxCnFPD11q0OiNLzxUJIaYrDeS1srpyo6rlE=,iv:Djn16kuXTWqJZy/AT77GpH8RcNtUMZ6zcIdKIMHv+PM=,tag:LP2JCaPKpzeOKvBc2bMr4w==,type:str]
|
wg_cloonar_key: ENC[AES256_GCM,data:9FgI8sAGXgn680jhzUvWY1IsmcuGfk2lPalE5xWN7iFi2KnSbj6inawwJmQ=,iv:qahuBL2U2ncS4SPUPYNJ4Eqaq4hc2zkgVAiyF7+0jVM=,tag:Ony3Fd1F08Dxy3fTGmp2sA==,type:str]
|
||||||
matrix-shared-secret: ENC[AES256_GCM,data:nVSHwPa8xYUaDCxL+5neFtzc11DDNzJtoDCSHYXZ+bZXVAAbp6/Pjx6UkTdAA8B2GOM09nFAsBuLnQfJ3w==,iv:WU3hnRlWVwx7Qin3ejw7V4VhAmYLf6oXzVk6xQgZPgA=,tag:O2hJ2q8XDxYF+rHPNgATgA==,type:str]
|
wg_epicenter_works_key: ENC[AES256_GCM,data:2gtqs64Zzz3Uy7RPWHszideTtzooA3YMaw4+WfmTxBbQNKREaeySV2+Vdls=,iv:sE0CRkgz7FCiH3cWg3ozzgjEMjQ1PxSm06wFKqqi/DY=,tag:DkgJISsUh0v2yIGZFVcQzA==,type:str]
|
||||||
phpldapadmin: ENC[AES256_GCM,data:94jCcgGJ89Er5ENLqhFZ1qY44Qp709SuUhBUuED6v/a7mPPjrJGDmi0Gm3r1Hb4CDPGkWf+x4NStY7LSQ2bHEzjyMPMS23wvSLTmC5b2TVca1UI8vZRTD1R7OvdWo8d1oNweSpYEnAXGv3USYF0NZo8DrPLM5G8lG5Tk/rKS/mxU5ZRhPyA60rbmIiy3Mk4yNcs1tvTEckxU/zMVl7zUPAsOOlmYGuwJrHmmh9p7YIWHGIgZNiLs3U0BvSKzN7WktmlwqjfWpeLn4dusqgov4SSQ2otAkxLHIH8mGhyotd1wgXJDZc6tilMe+WPHQDz9db7FT0VdeKggQ94FD+8rP0OsIjR4AdjZ,iv:C8X10wtA9jPgS41pxasaZJTO/XFcRymOyTDZCWJlhmg=,tag:xkMJsGubny+Di+GucAqypQ==,type:str]
|
wg_epicenter_works_psk: ENC[AES256_GCM,data:gl/6kg+QT+y3InIcx6OcVlEckhyKYzDvCFbc62CjFTLq7pCDuNbAMSpLJFA=,iv:0QuR2twfIMuyhT11tblvZ7A6BHqBJzZcx4IprTVlqw0=,tag:oJlLXnsy8w1Dcbs81MGsjA==,type:str]
|
||||||
palworld: ENC[AES256_GCM,data:iR9nceVotLKrFHnPIVskCYVLev9OzGLLlmfCGQq5hqB1HveXjhjkfm/NMmqnSi9o776+Ezy7l3kkS0R+0cFJ2B9kaWGsdJtdYDwQevmf6Nq5eaBYmvu8kTnaatqZ5e/1BQzcF3to6MA061XL54YGqsAV5FpnDVLhyyzIvaR3gMvMqJ748NL7K+hbBqMFuWcSH3hKXwxtDK7SLtcgx93W5ZgXkZMMumtlH9hSSlZL4yxuQDAQUwHrEBL+rdphA0m27dyS87DA3Av5ZL1MZ+Vlm4uAHM68T+rtVYXTakNImDTc0WrhIP8FZD/UKTAhVYAbA9oz6cbeC574vchuEY1z19SY9+2HshZZBOiPDMqdvrqyszMQCo5I9dUzAJCemQQTlYG8ekREQ0wxARnBYi3iy5PbmgDQWdM3+ff4yhMmGiHtiMQLHzrquKy8nvS9lDp9uT7njkaI0QAt3eNWa2DAQRqXQAtmuRVob5+GS2Nt6XMTWRkbeEb1phwbTqZD5mH4p2TiyMKCn6KOXsgQTxqGr35Izbe+bfptCmUeyscTKq01IZg77w/dvg3AX4iHAcMNgJ9LIHDLibGHQzu9fGN6alpeyy788GDwRY4glYyKxPhCasKkBSj/uhcDAtdg+c63vDTBhqRjNr6+v1NeRW6lVBzrgq+f9QvO5RVKrvdsVHnTA3CMGQzUPAaluNZMpzV5KqxqIrpAAPXnN0ktig==,iv:kkcm/alLHwC84IKK//OJpa36ec9ddOARTIM+KJlOHHs=,tag:jV1DjfNzRgNaCGgJTKIy5g==,type:str]
|
wg_ghetto_at_key: ENC[AES256_GCM,data:mpKsGzoWz8U/v/aZdN+z/U4z9kzlSo6IRK81yEkGjrOqhc4IHEuYe6U6I1s=,iv:qityQlwmZMo+Dst48hGhegN04cpMwyB0soeWRiZiVZI=,tag:uBtl/jdXF7BihNfIYlqJ5w==,type:str]
|
||||||
ark: ENC[AES256_GCM,data:TRTwxqkeUGbtgrWuj1YEFr73+nxCXmt/fR5vVnYR+k4FpNBB2FoY/gXl0kqeFKPDcajwn8nYBs8YE9vmYtAX/Qs4g5OyU9qC/pkmSV7/gbGfqLLqcbIlbWrZzeM8gRW0fp6h1TMPsGO8/iYdF4bmInfuZW+fKr0i7ZRgrtOpPiRCOI/ztPGkFaduuwGIy+yVoS64b9r7ZLRnOZT7ghVv80GKorJuuOQIipNAJMzEqtSA2IqaxWeb13v8wdQoKuMNcD6dCYVJnvgwf4R+,iv:+F9+yJUZBzPSSIt4uLHxjjXAjzRojLxKAyrd8grMXkk=,tag:VrIr4FFbIGTq9RBJMz8/Ig==,type:str]
|
matrix-shared-secret: ENC[AES256_GCM,data:IyeA3VvLhgGzEpTrQC85MlK5ngrPMvw/GmQhk9mWQ58NJsC942t8LcQO4AGMQBtrq17eLv6Ke2rOuoxlRA==,iv:zLKhiv01ViSH8dN9j3XJA520KdgBFQWO1bo/cuJVDuM=,tag:fiQ4NWhr+TtNN+AbGAtjxg==,type:str]
|
||||||
firefox-sync: ENC[AES256_GCM,data:guNgEVi9n8uJuLkkX2Z3tMY/NVqzQ2tdIutZAqleah9qBri0/3dzVHF2xvztLeAgm/59tN7TtAlAH2SMK6gcfAZDasAWOJ/rGEASxLi6VRjqCe25glDMp2YrA0/mcqZVYMCg+QZ5OPA56b55WDqPHPoBJkPDuTm9axwm6AOxdNi5BkDzMw12fVBxlJL/Rm8=,iv:yD+MkZK5vvZ85vYGd9X2Dv6KkSvMUsMGLrwlJ1pRqlk=,tag:YA379QupHh7aJZKcQxB7bA==,type:str]
|
phpldapadmin: ENC[AES256_GCM,data:Xv7G0iCfuPG7rXWfddgLV2Ztftwh1/lCY1KU+hGJDSGxbXKMkjThS9HL8+2BkOwHr46YVp0JHtxEcK4dxOQ/QTCF0xU6eo92dneXJ8ZyPe4UVWX+3x26vp1iOEpaDqL5n55FqKX0vJHffJBUS0mBu403fkJS463Mgyd8i9GPYBGZrGiiiApj49DUqA4bKdnxZMfOvY1SLk5wLfoY10uUuWlG/hwKrp3y5EkyQdUuD43kyDUMG0Zcka5ovz9TFGCQqGERWWnasOlduYTlR057h3w6TKi/I4wupbp2IHu+hyvrRtkM2/EcVPXpvWgEE/i+EFto3ku/Go+L5yjahJoJEhog0oIsZOg3,iv:26JI37tNe85LM88gg/AOoTqmSPjXD4hXbePwSJQrqWw=,tag:pFwr+73n5s/cGFwNnBlLsg==,type:str]
|
||||||
knot-tsig-key: ENC[AES256_GCM,data:CBFaRKPr+HRVM01fA9/OLWeD1O33axQKEKJuqDRfcGmuDeP3oXf+ccEJhQE=,iv:2O5y24YenpiMc9txPx8kz8x0aO37LpLjIcwlNywPEak=,tag:J4bVZ7RNSR9fiOBQ2HKpnQ==,type:str]
|
palworld: ENC[AES256_GCM,data:ihtdcEXcqiSr2RqsYreg9HlYB9SVkPdNTDThZTU390Lxu8SfNkbruYmYAALo8H8N2nMX83FAyuLGQ0OHlN0qSPzroK8QVUF2oI+JAcyjMnKB/gqhLMMgr9UN+72X2/3K2OwH+rY873uiBVAVbTBtfOJzDGDCgINERSjVtyw00n1Wxzlz7JWti80b8Hdilpg6LZj7VshAZf19s0VWljxz/drGBHCetWLn9dCop5Q00Hqd7dC4rn+efVelL0M7OT8KiGfYyoUL5cQhGb4ihYXa5+QuvIazm6C5hoQn0Ou7PFQGitB13gziwTu/OXkZeNLm4+xuayr52G3Min3HwzEtiGVP0DtYJmx+ONUOal/scZXuhkC1fjtkIdSQZi3L78CKvvkxQz6VGXi2gWxeZ63fvxGEitiEhE4JG1XwERDQQup1OneKIeelNqiQKSaYmQe3nJjn8lrFW+TKoj2hgFHw9u2xH4Qad2HcsJPiz8w2MdwBVj5H88vBSBWGUWyi6D2cxC7uaJ+qEINzjkWodgdUfD+OyPrRqQcuB6geRgadYpmpr+fqH4sIbYg31GfUgXtEfCxm9muAywMzhhmEq4173F2JNTYHGJG+R+2CoMpw9nJSidQJNaSJnZRPpNlShmBbgPt2rvqrFVXeKpaIpk4OjT57U+vlV6OzP0x8CnYe77cspCntm5VcP5lzqTJ+Xn4AZWY8gPolQM87VZ+oOQ==,iv:7M4FSofk3eYlLKuVIdxL49g4bwTF4ju8omO0PLMnZVw=,tag:5E9q+as6oLdF++dapuG6TA==,type:str]
|
||||||
mopidy-spotify: ENC[AES256_GCM,data:irBeIh2FieNkdf6Hls/Oj+qYxj1U7R7/Ffq6dx+JCS0PdOiFWIHXtccY+PXPKP7RhhaQOgZtIcgPyqTiML52P0c8AwN6UHMl7kgUcKnk60AI0IUZNWorCBZluHhEpf2e2OISlFzDGjSHk+zAzh2eDS1lJ9lCRYEC,iv:r6aZmlVHdRsA9DxkelcIVVpwwm32jaOgP429h61NL/U=,tag:FvPIr0HX/V7+G9kal4nO8w==,type:str]
|
ark: ENC[AES256_GCM,data:M3ztO6/LUCD6Zik+g1SuKf+2ne4ZSDaaD0R/kWX+qwHJZ8Scfzku63a8qAfytfICQ/XhTEF+f6s5pxTkiN1mgPMfdIda73d+Rv2yeVTkdgsamY9kTrTx9v3wZHiNyUvQM+IjNUje2CsF4iivMzyJhIF0112qYH7bMuvbKydHO5EQw4WPBonIXfLC1vd5wqAXWgyuQmvHTwHLgTQXSLiKbP/MhoBrpuzQtNM479VjMNVy5FpCf8+hl9ffj9MEcsORCB/hbG7HT7tdkP4w,iv:EU0ofqpq6qDCgwc9wrI32o1f20bhIASVcymYSuUMy2I=,tag:tvtVedgFpyosA/kMsxIGGw==,type:str]
|
||||||
lms-spotify: ENC[AES256_GCM,data:E53aUSNxE30SSrG6Y6SWKVzmsv0lu8aZvjk1RBgSj3q4m65dPLwGM9HcagN3BPoVTc0tKJaccrjoL2k5FOMnwcTXIz3qgiZGbnB6hVCoOhMrrkoFRN2JzSIA5WxKOT8VuMoC4/a6WaWbY8SWAdhgRQb9uq1hUxdkMCoNRLNJnPqR/0w07lCDVHvkj8XuBV4rGl93VVT3rCzjVTL+Vigv38WZ2il2aANkCz3joNeN8Uod3K/HA5uXLw3cLFmD7eI7LBDSTHpMEg==,iv:iRKrij3TRaufB5BXy7Xhiu3asClZ6hpkbMV14aod7jk=,tag:hpUwP/OHygqfgI6j6q2sKQ==,type:str]
|
firefox-sync: ENC[AES256_GCM,data:ctJxQDELOxkXJAJusvwGT70jShSr2o+xtAFvX9EuWe5DxfXrXeUVdHo1tELp8kofPMnYq1dMGDvj0iBNzK6MPQ75jeehZSO+RVyeRQopEmIJUOOFKR/goCeP0gcTOkuKmyr1p01OBjUTIp1UWvcsY6QC0ZHjF602WsmEZ+KeWw3uBnR18+7dA5tAkvoy1O4=,iv:/eVCI11oCbRxuhQpX3BEgwJCaoPHPTBE0s1XgVT1rHE=,tag:USu3y/CGQlliVJzeloCtQQ==,type:str]
|
||||||
|
knot-tsig-key: ENC[AES256_GCM,data:JXz7YJGgxoEJV9KiaaaiDgE50cVcZhOyXmknOxpV4zdgximUrM+TsNXmd9k=,iv:hhOThVcAMWTwp0bqC+7JMDS6O1iZzpE50AxvDB0sy2c=,tag:IAdZlLxgNjACBZxKXCrh/A==,type:str]
|
||||||
|
mopidy-spotify: ENC[AES256_GCM,data:/InQ6bFDZMyP2Np6f8zOh/Ssdgr27tcrwaOZhodR7Gagau2RQCJ8QHYK42x8P/3TEDXLbR2umySv48cOa/XtI8CTQaPAttfw++11QLIaXGfiiKgw4NyjNAAnhB+qlvXBDaLrGyk2PuDcPBkXm1x87hh3Rtou0Wa/,iv:35drh5LsdQLhd3v5VfK1IeVOeTRM29PdZSY/dH9b7ZI=,tag:lqkiE1rUlUq3Ym5sl5Nsog==,type:str]
|
||||||
|
lms-spotify: ENC[AES256_GCM,data:7yiuiZc6/65ppPjzK5ngt6DOvFtnD0HRgKca+TfsZ8rI0CaNywVZceW1lA0v6l9a4FJaOcMegNIs+2cNa7BkVpia53uFRL0ikHTDyI0nB9XLIhmbnzlbGSJ26MMeczJNS3J6rEX758BcEXme9pAvEmSWUga/GTlRcjfuFkvbToEpbVe6oEhthtnf0kucH2Yr/7ETUOMJLaUfb8NhvUUt6+BOb4zy52cXRBmB+IWo1qM4djx4L15ESP7MAo7iah83lktyyJgn5g==,iv:Y0mWmoW5xxlKDEjX7NIFG36AhTfO8Yuz9nqwwvK/s9E=,tag:pQl6V3q/DojdqmJuMZBJHA==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age14grjcxaq4h55yfnjxvnqhtswxhj9sfdcvyas4lwvpa8py27pjy2sv3g6v7
|
- recipient: age14grjcxaq4h55yfnjxvnqhtswxhj9sfdcvyas4lwvpa8py27pjy2sv3g6v7
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrTDFvM2l3Tm5lU0paWXpF
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxRjRJU3ZXYk1wbndIRTRV
|
||||||
cjVBSFhENW5mNG9DSFM1NXh3UHdaKzlKMGlZCnRmNFBFVWY4N0FqLzF1bUMyUDdL
|
WmNTY1BxdTdtMThwbnNNYmVwdlBVV21UeEhZCjFpY0VMWjZSdlFWNjFkVjNrcXVY
|
||||||
U091VENiVFhYeEJ5K0xodXlHVkhHKzgKLS0tIGxta3A2TjJiMUtiR2RzcU02Rys5
|
NGIyR1QwOWYvbzA0bjBGdVljYURJUVUKLS0tIG0valMrZm5GLzVHL1ZFWFR6WEE4
|
||||||
U1c0SjRKK2UwbTVIQUMrT1pOOVFmOVkKY3UyGNIPZJLE8GG124y0pLgqGub9SMCq
|
SFZidDhhTGRWZ3N1OVRIck0zdU44enMKcvt5966NSlt6heJmmOk0BRHOZnimLzi+
|
||||||
plK5H+kASOB1X6pK+3PBFuDYT1AbsRxXvWgAEMvVI7eBcxQlSrrB4Q==
|
EPD1lnQH/Pq56Bcb+aFY4qymUwWov3TbshVBhh7CTiNtF8OSkgoEsw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1exny8unxynaw03yu8ppahu5z28uermghr8ag34e7kdqnaduq9stsyettzz
|
- recipient: age1exny8unxynaw03yu8ppahu5z28uermghr8ag34e7kdqnaduq9stsyettzz
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVaXBqMGl1UytNL3BkZEhQ
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBNSthOTgzZUhCVjBVb0tn
|
||||||
S3RFL3lZRVZKTGVRTGFMNlFlWFRCNDNvRTM4CnpWZWovSDZaclQvN2Vwa0dWZGgz
|
RnM4TWRPZmFvdzNMVUJuanlXdDZIMnpkMng0ClRrdHRNblNQQTRSdkZ0dzFWQW83
|
||||||
Q1ZLM0sveXBxOVpvNHkycWJWWXdmVE0KLS0tIHl2bFk3RE03N01IdDJPWk5HT1Np
|
azA2UkdqOFFxTDdTOGJEdXhXWkZQSWMKLS0tIGdyRndDOXd3MnI4cDAyRmQvZElW
|
||||||
Qm82Sit3Q0haaDdnbzFjendMUm04Wk0KYp09dxXjzvC4IlH6Ilip8YjTz0mFeu/0
|
Y25yZXdwQXJ4a1NGbzFlVi9oMWJOYVkKjMFhePSmIyDjjzn9y5wJN2yEx+88KGhM
|
||||||
5IDMYjT1BuW5YiKgIJVd+UgOd6ysZLFFwk+Us2AcV7z110xk/askqQ==
|
W2W3iUGBjLOhnsUdNzDtrc5mDM+OH6jckvAz3UQpAUBtEaf+TUv3VA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1v6p8dan2t3w9h94fz4flldl32082j3s9x6zqq7u5j66keth9aphsd6pvch
|
- recipient: age1v6p8dan2t3w9h94fz4flldl32082j3s9x6zqq7u5j66keth9aphsd6pvch
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4Wkd0YnBQRnExeVdUTGFu
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBib3l5NVZMMWlVbEc1Y29N
|
||||||
N3o3MnF2aTY2NlBmdDJYT01zRytWZ2w1dFg0ClAzcnJ0NFYrVWlBM2JQU1B0SEJi
|
THBXTmt6YVpnMG4xVjVNb3BJampuUVJoY3dnCnRMNk5wQnJzcWVLS0IyUk9ta2cv
|
||||||
MGE5aVh6KzNmaEoxaHFOTW90K0VmMGsKLS0tIDNkOGZyVmMzME80TlBWMzI5UVR2
|
U3dVWVJ1Tm1US2pROHphOGlidmxUK1kKLS0tIGtFdUpWdm9KMTVLS0tUdjBMZDlY
|
||||||
djB3Y2FIRDFKWlEwTnRBUnRIT3M2OXcK+SIt/7DRdQi6H1AZooJN2Pt2g1EwVTZe
|
Vzl6QVE3azNtQm5IblVnMnBadkVCcFEKSbU+++fmAfh5oXPnjHbXK9XYDoLbtn9Z
|
||||||
Q14cEt0sLyVYzLJugfz2JWRHDZX6wPueYcTSEs7w3wAPVwvJWju8bg==
|
qREcR1NZjTliJd5jJ8sgMMxDKo6+ml6nOsRLqyCqITllJpgFzSLe5A==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1wq82xjyj80htz33x7agxddjfumr3wkwh3r24tasagepxw7ka893sau68df
|
- recipient: age1wq82xjyj80htz33x7agxddjfumr3wkwh3r24tasagepxw7ka893sau68df
|
||||||
enc: |
|
enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzQXhNSFBnNUtMdkpwR0th
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2YzIxSkEvUHdlL0FYcDhm
|
||||||
M1NmOVorcUdlZTFDM3dVRHZlYWpJcDZiakVnCit6eTFOeW92SzhPYzJxR0VTem9r
|
OTlTR0ExNVRPZzVTdlhFUWZ3YS9ncm1ObWw0CkQwWGZyRG5iN2FHNk9lVnpvUlFt
|
||||||
MSs4cWxRbzVBQmlWaHIwMjB5RUlJMXcKLS0tIHNSVTloOEVVVndDWkVrWmQrYXlD
|
eTJKbzJYbXBuSjZwTitrRWtERnJyWHcKLS0tIDRZZU8rTUxCQnI3QkVhZ0h6WC9y
|
||||||
NTd1WGFJWHVLTnFNT3hYbDdtSnMzTTAKBmJOayZLbjmBejwVzVtUSYPki+qPkYwG
|
U1BDd1V3M1VnK0dqamVndGdVUysvbDAKPipxKNbjkE5VugEvKxt5If1iFules5ul
|
||||||
xdO3L7n0Z8Cv/kVYZpkuG5GqOUL+nCJuYDjF0g4PaLb6WWd0W8ZGFA==
|
WLH7rH8M7R4uTOufBomXAqx3vMxxaCqUQlfbqhUkN7AT8vDPt5gqFg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-12-01T11:01:54Z"
|
lastmodified: "2026-01-31T13:59:03Z"
|
||||||
mac: ENC[AES256_GCM,data:taGX5HHZCL7Zo4taS2Jz/5WxhvpBNNKZ13ZCtS3x/P17tC1Nrk2UDcxbOZ1pPVbVvvaAHJtDb3owFvBOM4nr2Eve0M9zT4HbXh3hke7AviQ6U7CT1ru6LjY7W8lBjbQ6uCt+Ldxd1PRPPGiyKdK5GAUPKg6avFjpJbhEikh8Gww=,iv:NNs5usVJ5izYvHKnNm1IgjSt4dg0QFQ7cClJ6zh+3wM=,tag:sYYbEWIUgOWthEItdy5PFg==,type:str]
|
mac: ENC[AES256_GCM,data:Nr7KPjlCuzWE4aAZj1MqD8Nm5TsC5FZWBpc9qQJMUOGjQMHYqwZU0fttRcY5Ik6MIH7+f+lPxHyRqqoy9ufYOqtAs5+fTDIgTGpYsBqN/MYqFLtwqAqOKoM3M+q0V8zmIotA13MQR8UxCF4WXCg37vwWKFKbNXlilpGOMOr1lHA=,iv:cjtfFHhqelIeNM7Xh6HIOJuQB2QzFp/vw8LcZujo6c0=,tag:Kb78AF9dswbO/MqjHDoQRg==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.11.0
|
version: 3.11.0
|
||||||
|
|||||||
19
scripts/migrate-gitea-to-forgejo.env.example
Normal file
19
scripts/migrate-gitea-to-forgejo.env.example
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Gitea to Forgejo Migration - Environment Configuration
|
||||||
|
#
|
||||||
|
# Copy this file to migrate-gitea-to-forgejo.env and adjust values.
|
||||||
|
# Then run: ./scripts/migrate-gitea-to-forgejo.sh
|
||||||
|
#
|
||||||
|
# IMPORTANT: Ensure Gitea is stopped before running migration.
|
||||||
|
|
||||||
|
# Source (Gitea) - READ ONLY, never modified
|
||||||
|
# This is the original Gitea data directory
|
||||||
|
SOURCE_DATA=/var/lib/gitea
|
||||||
|
|
||||||
|
# Target (Forgejo) - where data will be copied
|
||||||
|
# Must be on a filesystem with enough space (1.2x source size)
|
||||||
|
TARGET_DATA=/var/lib/forgejo
|
||||||
|
|
||||||
|
# User/group for target files
|
||||||
|
# These should match your Forgejo service user
|
||||||
|
TARGET_USER=forgejo
|
||||||
|
TARGET_GROUP=forgejo
|
||||||
497
scripts/migrate-gitea-to-forgejo.sh
Executable file
497
scripts/migrate-gitea-to-forgejo.sh
Executable file
@@ -0,0 +1,497 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Gitea 1.25.4 to Forgejo Migration Script
|
||||||
|
#
|
||||||
|
# This script copies data from Gitea to Forgejo and rolls back the database
|
||||||
|
# schema from version 322/323 to 304, allowing Forgejo to run its own migrations.
|
||||||
|
#
|
||||||
|
# IMPORTANT: This script NEVER modifies source data. All operations work on copies,
|
||||||
|
# so the original Gitea instance can be restarted as a rollback.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# 1. Copy migrate-gitea-to-forgejo.env.example to migrate-gitea-to-forgejo.env
|
||||||
|
# 2. Edit the .env file with your paths
|
||||||
|
# 3. Stop Gitea
|
||||||
|
# 4. Run: ./scripts/migrate-gitea-to-forgejo.sh
|
||||||
|
# 5. Update NixOS config and deploy
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ENV_FILE="${SCRIPT_DIR}/migrate-gitea-to-forgejo.env"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||||
|
log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
|
||||||
|
# Load environment file
|
||||||
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
|
log_error "Environment file not found: $ENV_FILE"
|
||||||
|
log_info "Copy migrate-gitea-to-forgejo.env.example to migrate-gitea-to-forgejo.env and configure it."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$ENV_FILE"
|
||||||
|
|
||||||
|
# Verify required variables
|
||||||
|
: "${SOURCE_DATA:?SOURCE_DATA must be set in $ENV_FILE}"
|
||||||
|
: "${TARGET_DATA:?TARGET_DATA must be set in $ENV_FILE}"
|
||||||
|
: "${TARGET_USER:?TARGET_USER must be set in $ENV_FILE}"
|
||||||
|
: "${TARGET_GROUP:?TARGET_GROUP must be set in $ENV_FILE}"
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo "Gitea to Forgejo Migration Script"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
echo "Source: $SOURCE_DATA (read-only)"
|
||||||
|
echo "Target: $TARGET_DATA"
|
||||||
|
echo "User: $TARGET_USER:$TARGET_GROUP"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 1: Pre-flight Checks
|
||||||
|
# ============================================
|
||||||
|
log_info "Phase 1: Pre-flight checks..."
|
||||||
|
|
||||||
|
# Check if running as root (needed for chown)
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
log_error "This script must be run as root (for chown operations)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify SQLite version >= 3.35 (required for DROP COLUMN)
|
||||||
|
if ! command -v sqlite3 &> /dev/null; then
|
||||||
|
log_error "sqlite3 command not found. Please install SQLite."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sqlite_version=$(sqlite3 --version | cut -d' ' -f1)
|
||||||
|
sqlite_major=$(echo "$sqlite_version" | cut -d'.' -f1)
|
||||||
|
sqlite_minor=$(echo "$sqlite_version" | cut -d'.' -f2)
|
||||||
|
if [[ "$sqlite_major" -lt 3 ]] || { [[ "$sqlite_major" -eq 3 ]] && [[ "$sqlite_minor" -lt 35 ]]; }; then
|
||||||
|
log_error "SQLite $sqlite_version is too old. Need 3.35+ for DROP COLUMN support."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "SQLite version: $sqlite_version"
|
||||||
|
|
||||||
|
# Verify rsync is available (needed for incremental copying)
|
||||||
|
if ! command -v rsync &> /dev/null; then
|
||||||
|
log_error "rsync command not found. Please install rsync."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "rsync available"
|
||||||
|
|
||||||
|
# Verify source exists
|
||||||
|
if [[ ! -d "$SOURCE_DATA" ]]; then
|
||||||
|
log_error "Source directory not found: $SOURCE_DATA"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Source directory exists"
|
||||||
|
|
||||||
|
# Find source database (could be gitea.db or forgejo.db depending on setup)
|
||||||
|
SOURCE_DB=""
|
||||||
|
if [[ -f "$SOURCE_DATA/data/gitea.db" ]]; then
|
||||||
|
SOURCE_DB="$SOURCE_DATA/data/gitea.db"
|
||||||
|
elif [[ -f "$SOURCE_DATA/gitea.db" ]]; then
|
||||||
|
SOURCE_DB="$SOURCE_DATA/gitea.db"
|
||||||
|
else
|
||||||
|
log_error "Source database not found in $SOURCE_DATA/data/ or $SOURCE_DATA/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Source database found: $SOURCE_DB"
|
||||||
|
|
||||||
|
# Verify source app.ini exists
|
||||||
|
SOURCE_INI=""
|
||||||
|
if [[ -f "$SOURCE_DATA/custom/conf/app.ini" ]]; then
|
||||||
|
SOURCE_INI="$SOURCE_DATA/custom/conf/app.ini"
|
||||||
|
elif [[ -f "$SOURCE_DATA/conf/app.ini" ]]; then
|
||||||
|
SOURCE_INI="$SOURCE_DATA/conf/app.ini"
|
||||||
|
else
|
||||||
|
log_error "Source app.ini not found in $SOURCE_DATA/custom/conf/ or $SOURCE_DATA/conf/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Source app.ini found: $SOURCE_INI"
|
||||||
|
|
||||||
|
# Check disk space (need 1.2x source size)
|
||||||
|
source_size=$(du -sb "$SOURCE_DATA" | cut -f1)
|
||||||
|
required=$((source_size * 12 / 10))
|
||||||
|
target_parent=$(dirname "$TARGET_DATA")
|
||||||
|
mkdir -p "$target_parent"
|
||||||
|
available=$(df --output=avail -B1 "$target_parent" | tail -1)
|
||||||
|
if [[ "$available" -lt "$required" ]]; then
|
||||||
|
log_error "Not enough disk space. Need $(numfmt --to=iec $required), have $(numfmt --to=iec $available)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "Disk space OK: need $(numfmt --to=iec $required), have $(numfmt --to=iec $available)"
|
||||||
|
|
||||||
|
# Warn if target exists (rsync will sync incrementally)
|
||||||
|
if [[ -d "$TARGET_DATA" ]]; then
|
||||||
|
log_warn "Target directory exists: $TARGET_DATA"
|
||||||
|
log_info "rsync will perform incremental sync (only copying changed files)"
|
||||||
|
read -p "Continue with incremental sync? (y/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
log_error "Aborted by user"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 2: Copy All Data
|
||||||
|
# ============================================
|
||||||
|
log_info "Phase 2: Copying data..."
|
||||||
|
|
||||||
|
mkdir -p "$TARGET_DATA/data"
|
||||||
|
mkdir -p "$TARGET_DATA/custom/conf"
|
||||||
|
|
||||||
|
# Copy database
|
||||||
|
log_info "Copying database..."
|
||||||
|
rsync -a --info=progress2 "$SOURCE_DB" "$TARGET_DATA/data/forgejo.db"
|
||||||
|
log_success "Database copied"
|
||||||
|
|
||||||
|
# Copy all data directories (preserve attributes, sync incrementally)
|
||||||
|
for dir in repositories avatars attachments packages lfs custom queues indexers; do
|
||||||
|
if [[ -d "$SOURCE_DATA/$dir" ]]; then
|
||||||
|
log_info "Syncing $dir..."
|
||||||
|
mkdir -p "$TARGET_DATA/$dir"
|
||||||
|
rsync -a --delete --info=progress2 "$SOURCE_DATA/$dir/" "$TARGET_DATA/$dir/"
|
||||||
|
log_success "Synced $dir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Also check data/ subdirectory structure
|
||||||
|
for dir in repositories avatars attachments packages lfs; do
|
||||||
|
if [[ -d "$SOURCE_DATA/data/$dir" ]]; then
|
||||||
|
log_info "Syncing data/$dir..."
|
||||||
|
mkdir -p "$TARGET_DATA/data/$dir"
|
||||||
|
rsync -a --delete --info=progress2 "$SOURCE_DATA/data/$dir/" "$TARGET_DATA/data/$dir/"
|
||||||
|
log_success "Synced data/$dir"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 3: Database Schema Rollback
|
||||||
|
# ============================================
|
||||||
|
log_info "Phase 3: Rolling back database schema..."
|
||||||
|
|
||||||
|
TARGET_DB="$TARGET_DATA/data/forgejo.db"
|
||||||
|
|
||||||
|
# Show current schema version
|
||||||
|
current_version=$(sqlite3 "$TARGET_DB" "SELECT version FROM version WHERE id=1;")
|
||||||
|
log_info "Current Gitea schema version: $current_version"
|
||||||
|
log_info "Target version: 304"
|
||||||
|
|
||||||
|
# Create rollback SQL script
|
||||||
|
ROLLBACK_SQL=$(mktemp)
|
||||||
|
cat > "$ROLLBACK_SQL" << 'ROLLBACK_EOF'
|
||||||
|
-- ================================================================
|
||||||
|
-- Gitea 1.25.4 to Forgejo Rollback Script
|
||||||
|
-- Rolls back migrations 305-322 to allow Forgejo to migrate cleanly
|
||||||
|
-- ================================================================
|
||||||
|
|
||||||
|
-- Enable foreign keys check after we're done
|
||||||
|
PRAGMA foreign_keys = OFF;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 305: Drop repo_license table
|
||||||
|
-- ============================================
|
||||||
|
DROP TABLE IF EXISTS repo_license;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 308 & 317: Drop action table indices
|
||||||
|
-- (These are the main conflict source)
|
||||||
|
-- ============================================
|
||||||
|
DROP INDEX IF EXISTS IDX_action_r_u_d;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_au_r_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_c_u;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_au_c_u;
|
||||||
|
-- Alternative naming conventions
|
||||||
|
DROP INDEX IF EXISTS UQE_action_r_u_d;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_au_r_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_c_u;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_au_c_u;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 309: Drop notification table indices
|
||||||
|
-- ============================================
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_u_s_uu;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_user_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_repo_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_status;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_source;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_issue_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_commit_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_updated_by;
|
||||||
|
DROP INDEX IF EXISTS UQE_notification_u_s_uu;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 313: Drop issue_pin table
|
||||||
|
-- (pin_order restoration handled separately)
|
||||||
|
-- ============================================
|
||||||
|
DROP TABLE IF EXISTS issue_pin;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 306: Drop protected_branch column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE protected_branch DROP COLUMN IF EXISTS block_admin_merge_override;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 310: Drop protected_branch column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE protected_branch DROP COLUMN IF EXISTS priority;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 311: Drop issue column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE issue DROP COLUMN IF EXISTS time_estimate;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 312: Drop pull_auto_merge column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE pull_auto_merge DROP COLUMN IF EXISTS delete_branch_after_merge;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 315: Drop action_runner column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE action_runner DROP COLUMN IF EXISTS ephemeral;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 316: Drop description columns
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE secret DROP COLUMN IF EXISTS description;
|
||||||
|
ALTER TABLE action_variable DROP COLUMN IF EXISTS description;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 318: Drop repo_unit column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE repo_unit DROP COLUMN IF EXISTS anonymous_access_mode;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 319: Drop label column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE label DROP COLUMN IF EXISTS exclusive_order;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- MIGRATION 320: Drop login_source column
|
||||||
|
-- ============================================
|
||||||
|
ALTER TABLE login_source DROP COLUMN IF EXISTS two_factor_policy;
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- SET VERSION TO 304
|
||||||
|
-- ============================================
|
||||||
|
UPDATE version SET version = 304 WHERE id = 1;
|
||||||
|
|
||||||
|
PRAGMA foreign_keys = ON;
|
||||||
|
ROLLBACK_EOF
|
||||||
|
|
||||||
|
log_info "Executing schema rollback..."
|
||||||
|
|
||||||
|
# SQLite doesn't support DROP COLUMN IF EXISTS, so we need to handle errors gracefully
|
||||||
|
# Execute each ALTER TABLE separately to handle missing columns
|
||||||
|
sqlite3 "$TARGET_DB" << 'SQL_PART1'
|
||||||
|
PRAGMA foreign_keys = OFF;
|
||||||
|
|
||||||
|
-- Drop tables
|
||||||
|
DROP TABLE IF EXISTS repo_license;
|
||||||
|
DROP TABLE IF EXISTS issue_pin;
|
||||||
|
|
||||||
|
-- Drop indices (these always work, even if index doesn't exist)
|
||||||
|
DROP INDEX IF EXISTS IDX_action_r_u_d;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_au_r_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_c_u;
|
||||||
|
DROP INDEX IF EXISTS IDX_action_au_c_u;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_r_u_d;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_au_r_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_c_u_d;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_c_u;
|
||||||
|
DROP INDEX IF EXISTS UQE_action_au_c_u;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_u_s_uu;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_user_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_repo_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_status;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_source;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_issue_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_commit_id;
|
||||||
|
DROP INDEX IF EXISTS IDX_notification_updated_by;
|
||||||
|
DROP INDEX IF EXISTS UQE_notification_u_s_uu;
|
||||||
|
SQL_PART1
|
||||||
|
|
||||||
|
# Function to drop column if it exists
|
||||||
|
drop_column_if_exists() {
|
||||||
|
local table="$1"
|
||||||
|
local column="$2"
|
||||||
|
local exists
|
||||||
|
exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM pragma_table_info('$table') WHERE name='$column';")
|
||||||
|
if [[ "$exists" -gt 0 ]]; then
|
||||||
|
log_info "Dropping column $table.$column..."
|
||||||
|
sqlite3 "$TARGET_DB" "ALTER TABLE $table DROP COLUMN $column;"
|
||||||
|
log_success "Dropped $table.$column"
|
||||||
|
else
|
||||||
|
log_info "Column $table.$column does not exist, skipping"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Drop columns added in migrations 306-320
|
||||||
|
drop_column_if_exists "protected_branch" "block_admin_merge_override"
|
||||||
|
drop_column_if_exists "protected_branch" "priority"
|
||||||
|
drop_column_if_exists "issue" "time_estimate"
|
||||||
|
drop_column_if_exists "pull_auto_merge" "delete_branch_after_merge"
|
||||||
|
drop_column_if_exists "action_runner" "ephemeral"
|
||||||
|
drop_column_if_exists "secret" "description"
|
||||||
|
drop_column_if_exists "action_variable" "description"
|
||||||
|
drop_column_if_exists "repo_unit" "anonymous_access_mode"
|
||||||
|
drop_column_if_exists "label" "exclusive_order"
|
||||||
|
drop_column_if_exists "login_source" "two_factor_policy"
|
||||||
|
|
||||||
|
# Check if pin_order column needs to be added back to issue table (migration 313 removed it)
|
||||||
|
log_info "Checking if pin_order column needs to be restored to issue table..."
|
||||||
|
has_pin_order=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM pragma_table_info('issue') WHERE name='pin_order';")
|
||||||
|
if [[ "$has_pin_order" -eq 0 ]]; then
|
||||||
|
log_info "Adding pin_order column back to issue table..."
|
||||||
|
sqlite3 "$TARGET_DB" "ALTER TABLE issue ADD COLUMN pin_order INTEGER DEFAULT 0;"
|
||||||
|
log_success "Added pin_order column to issue table"
|
||||||
|
else
|
||||||
|
log_info "pin_order column already exists in issue table"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set version to 304 (allows Forgejo to run migration 305 which converts two_factor.secret from TEXT to BLOB)
|
||||||
|
sqlite3 "$TARGET_DB" "UPDATE version SET version = 304 WHERE id = 1;"
|
||||||
|
log_success "Database version set to 304"
|
||||||
|
|
||||||
|
rm -f "$ROLLBACK_SQL"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 4: Clear Regeneratable Data
|
||||||
|
# ============================================
|
||||||
|
log_info "Phase 4: Clearing regeneratable data..."
|
||||||
|
|
||||||
|
# Remove indexers (will be rebuilt on first start)
|
||||||
|
if [[ -d "$TARGET_DATA/indexers" ]]; then
|
||||||
|
rm -rf "$TARGET_DATA/indexers"
|
||||||
|
log_success "Removed indexers (will be rebuilt)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove queues (will be recreated)
|
||||||
|
if [[ -d "$TARGET_DATA/queues" ]]; then
|
||||||
|
rm -rf "$TARGET_DATA/queues"
|
||||||
|
log_success "Removed queues (will be recreated)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 5: Update Configuration
|
||||||
|
# ============================================
|
||||||
|
log_info "Phase 5: Updating configuration..."
|
||||||
|
|
||||||
|
# Copy app.ini
|
||||||
|
rsync -a --info=progress2 "$SOURCE_INI" "$TARGET_DATA/custom/conf/app.ini"
|
||||||
|
log_success "Copied app.ini"
|
||||||
|
|
||||||
|
# Update paths from gitea to forgejo
|
||||||
|
sed -i 's|/var/lib/gitea|/var/lib/forgejo|g' "$TARGET_DATA/custom/conf/app.ini"
|
||||||
|
log_success "Updated paths in app.ini"
|
||||||
|
|
||||||
|
# Check if WAL mode is already configured
|
||||||
|
if ! grep -q "SQLITE_JOURNAL_MODE" "$TARGET_DATA/custom/conf/app.ini"; then
|
||||||
|
# Add WAL mode after [database] section
|
||||||
|
sed -i '/^\[database\]/a SQLITE_JOURNAL_MODE = WAL' "$TARGET_DATA/custom/conf/app.ini"
|
||||||
|
log_success "Enabled SQLite WAL mode"
|
||||||
|
else
|
||||||
|
log_info "SQLite journal mode already configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 6: Set Permissions
|
||||||
|
# ============================================
|
||||||
|
log_info "Phase 6: Setting permissions..."
|
||||||
|
|
||||||
|
chown -R "$TARGET_USER:$TARGET_GROUP" "$TARGET_DATA"
|
||||||
|
chmod 750 "$TARGET_DATA"
|
||||||
|
chmod 640 "$TARGET_DATA/data/forgejo.db"
|
||||||
|
log_success "Permissions set for $TARGET_USER:$TARGET_GROUP"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 7: Verify Database Integrity
|
||||||
|
# ============================================
|
||||||
|
log_info "Phase 7: Verifying database integrity..."
|
||||||
|
|
||||||
|
sqlite3 "$TARGET_DB" << 'VERIFY_SQL'
|
||||||
|
.headers off
|
||||||
|
.mode list
|
||||||
|
|
||||||
|
-- Verify version was set correctly
|
||||||
|
SELECT 'Version: ' || CASE WHEN version = 304 THEN 'PASS (304)' ELSE 'FAIL (version=' || version || ')' END
|
||||||
|
FROM version WHERE id = 1;
|
||||||
|
|
||||||
|
-- Check critical tables exist
|
||||||
|
SELECT 'Users: ' || CASE WHEN COUNT(*) > 0 THEN 'PASS (' || COUNT(*) || ' users)' ELSE 'WARN (empty)' END FROM user;
|
||||||
|
SELECT 'Repositories: ' || CASE WHEN COUNT(*) > 0 THEN 'PASS (' || COUNT(*) || ' repos)' ELSE 'WARN (empty)' END FROM repository;
|
||||||
|
SELECT 'Secrets: PASS (' || COUNT(*) || ' secrets)' FROM secret;
|
||||||
|
SELECT 'Runners: PASS (' || COUNT(*) || ' runners)' FROM action_runner;
|
||||||
|
SELECT 'Variables: PASS (' || COUNT(*) || ' variables)' FROM action_variable;
|
||||||
|
VERIFY_SQL
|
||||||
|
|
||||||
|
# Verify dropped tables are gone
|
||||||
|
repo_license_exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='repo_license';")
|
||||||
|
issue_pin_exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='issue_pin';")
|
||||||
|
|
||||||
|
if [[ "$repo_license_exists" -eq 0 ]]; then
|
||||||
|
log_success "repo_license table: DROPPED"
|
||||||
|
else
|
||||||
|
log_warn "repo_license table: STILL EXISTS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$issue_pin_exists" -eq 0 ]]; then
|
||||||
|
log_success "issue_pin table: DROPPED"
|
||||||
|
else
|
||||||
|
log_warn "issue_pin table: STILL EXISTS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# PHASE 8: Print Next Steps
|
||||||
|
# ============================================
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo -e "${GREEN}Migration complete!${NC}"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
echo "Data copied to: $TARGET_DATA"
|
||||||
|
echo "Database schema rolled back to version 304"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Update NixOS configuration:"
|
||||||
|
echo " - Create hosts/fw/modules/forgejo.nix based on gitea.nix"
|
||||||
|
echo " - Change services.gitea to services.forgejo"
|
||||||
|
echo " - Update bind mount paths in container config"
|
||||||
|
echo " - Update runner configuration for Forgejo"
|
||||||
|
echo ""
|
||||||
|
echo "2. Deploy:"
|
||||||
|
echo " nixos-rebuild switch"
|
||||||
|
echo ""
|
||||||
|
echo "3. Monitor first startup:"
|
||||||
|
echo " journalctl -u container@git -f"
|
||||||
|
echo ""
|
||||||
|
echo "4. Verify functionality:"
|
||||||
|
echo " [ ] Forgejo starts without errors"
|
||||||
|
echo " [ ] Login via OpenID (auth.cloonar.com)"
|
||||||
|
echo " [ ] All repositories visible"
|
||||||
|
echo " [ ] Can push/pull to repositories"
|
||||||
|
echo " [ ] CI/CD runners connect"
|
||||||
|
echo " [ ] Workflow with secrets runs"
|
||||||
|
echo " [ ] Packages registry accessible"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}ROLLBACK:${NC} If anything fails, original Gitea data is untouched."
|
||||||
|
echo "Just revert NixOS config and restart Gitea container."
|
||||||
|
echo "========================================"
|
||||||
Reference in New Issue
Block a user