forked from mirrors_public/oddlama_nix-config
chore: ente -> immich (large library >50k photos and videos has performance issues on ente, slow loading)
This commit is contained in:
parent
140dba323c
commit
5fc809f4d6
15 changed files with 2 additions and 764 deletions
|
@ -50,7 +50,7 @@ I've included the major components in the lists below.
|
||||||
🔒 SSO | Kanidm | [Link](./hosts/ward/guests/kanidm.nix) | Identity provider for Single-Sign-On on my hosted services, with provisioning.
|
🔒 SSO | Kanidm | [Link](./hosts/ward/guests/kanidm.nix) | Identity provider for Single-Sign-On on my hosted services, with provisioning.
|
||||||
🐙 Git | Forgejo | [Link](./hosts/ward/guests/forgejo.nix) | Forgejo with SSO
|
🐙 Git | Forgejo | [Link](./hosts/ward/guests/forgejo.nix) | Forgejo with SSO
|
||||||
🔑 Passwords | Vaultwarden | [Link](./hosts/ward/guests/vaultwarden.nix) | Self-hosted password manager
|
🔑 Passwords | Vaultwarden | [Link](./hosts/ward/guests/vaultwarden.nix) | Self-hosted password manager
|
||||||
📷 Photos | Ente | [Link](./hosts/sire/guests/ente.nix) | E2E encrypted photo and video backup solution
|
📷 Photos | Immich | [Link](./hosts/sire/guests/immich.nix) | Self-hosted photo and video management solution
|
||||||
📄 Documents | Paperless | [Link](./hosts/sire/guests/paperless.nix) | Document management system. With per-user Samba share integration (consume & archive)
|
📄 Documents | Paperless | [Link](./hosts/sire/guests/paperless.nix) | Document management system. With per-user Samba share integration (consume & archive)
|
||||||
🗓️ CalDAV/CardDAV | Radicale | [Link](./hosts/ward/guests/radicale.nix) | Contacts, Calender and Tasks synchronization
|
🗓️ CalDAV/CardDAV | Radicale | [Link](./hosts/ward/guests/radicale.nix) | Contacts, Calender and Tasks synchronization
|
||||||
📁 NAS | Samba | [Link](./hosts/sire/guests/samba.nix) | Network attached storage. Cross-integration with paperless
|
📁 NAS | Samba | [Link](./hosts/sire/guests/samba.nix) | Network attached storage. Cross-integration with paperless
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
immich = uidGid 965;
|
immich = uidGid 965;
|
||||||
redis-immich = uidGid 964;
|
redis-immich = uidGid 964;
|
||||||
avahi = uidGid 963;
|
avahi = uidGid 963;
|
||||||
ente = uidGid 962;
|
# ente = uidGid 962;
|
||||||
minio = uidGid 961;
|
minio = uidGid 961;
|
||||||
kea = uidGid 960;
|
kea = uidGid 960;
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,7 +29,6 @@ in
|
||||||
cidrv6 = "fd00:44::/120";
|
cidrv6 = "fd00:44::/120";
|
||||||
hosts = {
|
hosts = {
|
||||||
sausebiene.id = 10;
|
sausebiene.id = 10;
|
||||||
sire-ente.id = 101;
|
|
||||||
sire-grafana.id = 221;
|
sire-grafana.id = 221;
|
||||||
sire-immich.id = 225;
|
sire-immich.id = 225;
|
||||||
sire-influxdb.id = 141;
|
sire-influxdb.id = 141;
|
||||||
|
@ -51,7 +50,6 @@ in
|
||||||
envoy.id = 135;
|
envoy.id = 135;
|
||||||
sentinel.id = 29;
|
sentinel.id = 29;
|
||||||
sire-ai.id = 171;
|
sire-ai.id = 171;
|
||||||
sire-ente.id = 101;
|
|
||||||
sire-grafana.id = 221;
|
sire-grafana.id = 221;
|
||||||
sire-immich.id = 225;
|
sire-immich.id = 225;
|
||||||
sire-influxdb.id = 141;
|
sire-influxdb.id = 141;
|
||||||
|
|
|
@ -58,15 +58,4 @@
|
||||||
# This node shall monitor the infrastructure
|
# This node shall monitor the infrastructure
|
||||||
availableMonitoringNetworks = [ "internet" ];
|
availableMonitoringNetworks = [ "internet" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.ente.web = {
|
|
||||||
enable = true;
|
|
||||||
domains = {
|
|
||||||
api = "api.photos.${globals.domains.me}";
|
|
||||||
accounts = "accounts.photos.${globals.domains.me}";
|
|
||||||
albums = "albums.photos.${globals.domains.me}";
|
|
||||||
cast = "cast.photos.${globals.domains.me}";
|
|
||||||
photos = "photos.${globals.domains.me}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,6 @@ let
|
||||||
# FIXME: new entry here? make new firezone gateway on ward entry too.
|
# FIXME: new entry here? make new firezone gateway on ward entry too.
|
||||||
homeDomains = [
|
homeDomains = [
|
||||||
globals.services.grafana.domain
|
globals.services.grafana.domain
|
||||||
"accounts.photos.${globals.domains.me}"
|
|
||||||
"albums.photos.${globals.domains.me}"
|
|
||||||
"api.photos.${globals.domains.me}"
|
|
||||||
"cast.photos.${globals.domains.me}"
|
|
||||||
"photos.${globals.domains.me}"
|
|
||||||
"s3.photos.${globals.domains.me}"
|
|
||||||
globals.services.mealie.domain
|
globals.services.mealie.domain
|
||||||
globals.services.immich.domain
|
globals.services.immich.domain
|
||||||
globals.services.influxdb.domain
|
globals.services.influxdb.domain
|
||||||
|
|
|
@ -150,9 +150,5 @@
|
||||||
}
|
}
|
||||||
// mkMicrovm "ai" { }
|
// mkMicrovm "ai" { }
|
||||||
// mkMicrovm "minecraft" { }
|
// mkMicrovm "minecraft" { }
|
||||||
// mkMicrovm "ente" {
|
|
||||||
enableStorageDataset = true;
|
|
||||||
}
|
|
||||||
#// mkMicrovm "fasten-health" {}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,256 +0,0 @@
|
||||||
{
|
|
||||||
config,
|
|
||||||
globals,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
# NOTE: To increase storage for all users:
|
|
||||||
# $ runuser -u ente -- psql
|
|
||||||
# ente => UPDATE subscriptions SET storage = 6597069766656;
|
|
||||||
let
|
|
||||||
enteAccountsDomain = "accounts.photos.${globals.domains.me}";
|
|
||||||
enteAlbumsDomain = "albums.photos.${globals.domains.me}";
|
|
||||||
enteApiDomain = "api.photos.${globals.domains.me}";
|
|
||||||
enteCastDomain = "cast.photos.${globals.domains.me}";
|
|
||||||
entePhotosDomain = "photos.${globals.domains.me}";
|
|
||||||
s3Domain = "s3.photos.${globals.domains.me}";
|
|
||||||
|
|
||||||
proxyConfig = remoteAddr: nginxExtraConfig: {
|
|
||||||
upstreams.museum = {
|
|
||||||
servers."${remoteAddr}:8080" = { };
|
|
||||||
extraConfig = ''
|
|
||||||
zone museum 64k;
|
|
||||||
keepalive 20;
|
|
||||||
'';
|
|
||||||
monitoring = {
|
|
||||||
enable = true;
|
|
||||||
path = "/ping";
|
|
||||||
expectedStatus = 200;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
upstreams.minio = {
|
|
||||||
servers."${remoteAddr}:9000" = { };
|
|
||||||
extraConfig = ''
|
|
||||||
zone minio 64k;
|
|
||||||
keepalive 20;
|
|
||||||
'';
|
|
||||||
monitoring = {
|
|
||||||
enable = true;
|
|
||||||
path = "/minio/health/live";
|
|
||||||
expectedStatus = 200;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualHosts = {
|
|
||||||
${enteApiDomain} = {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEWildcardHost = true;
|
|
||||||
locations."/".proxyPass = "http://museum";
|
|
||||||
extraConfig = ''
|
|
||||||
client_max_body_size 4M;
|
|
||||||
${nginxExtraConfig}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
${s3Domain} = {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEWildcardHost = true;
|
|
||||||
locations."/".proxyPass = "http://minio";
|
|
||||||
extraConfig = ''
|
|
||||||
client_max_body_size 32M;
|
|
||||||
proxy_buffering off;
|
|
||||||
proxy_request_buffering off;
|
|
||||||
${nginxExtraConfig}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//
|
|
||||||
lib.genAttrs
|
|
||||||
[
|
|
||||||
enteAccountsDomain
|
|
||||||
enteAlbumsDomain
|
|
||||||
enteCastDomain
|
|
||||||
entePhotosDomain
|
|
||||||
]
|
|
||||||
(_domain: {
|
|
||||||
useACMEWildcardHost = true;
|
|
||||||
extraConfig = nginxExtraConfig;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
globals.wireguard.proxy-sentinel.hosts.${config.node.name}.firewallRuleForNode.sentinel.allowedTCPPorts =
|
|
||||||
[
|
|
||||||
8080
|
|
||||||
9000
|
|
||||||
];
|
|
||||||
|
|
||||||
globals.wireguard.proxy-home.hosts.${config.node.name}.firewallRuleForNode.ward-web-proxy.allowedTCPPorts =
|
|
||||||
[
|
|
||||||
8080
|
|
||||||
9000
|
|
||||||
];
|
|
||||||
|
|
||||||
globals.services.ente.domain = entePhotosDomain;
|
|
||||||
# FIXME: also monitor from internal network
|
|
||||||
globals.monitoring.http.ente = {
|
|
||||||
url = "https://${entePhotosDomain}";
|
|
||||||
expectedBodyRegex = "Ente Photos";
|
|
||||||
network = "internet";
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/storage".neededForBoot = true;
|
|
||||||
environment.persistence."/storage".directories = [
|
|
||||||
{
|
|
||||||
directory = "/var/lib/minio";
|
|
||||||
user = "minio";
|
|
||||||
group = "minio";
|
|
||||||
mode = "0750";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
environment.persistence."/persist".directories = [
|
|
||||||
{
|
|
||||||
directory = "/var/lib/ente";
|
|
||||||
user = "ente";
|
|
||||||
group = "ente";
|
|
||||||
mode = "0750";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
# NOTE: don't use the root user for access. In this case it doesn't matter
|
|
||||||
# since the whole minio server is only for ente anyway, but it would be a
|
|
||||||
# good practice.
|
|
||||||
age.secrets.minio-access-key = {
|
|
||||||
generator.script = "alnum";
|
|
||||||
mode = "440";
|
|
||||||
group = "ente";
|
|
||||||
};
|
|
||||||
age.secrets.minio-secret-key = {
|
|
||||||
generator.script = "alnum";
|
|
||||||
mode = "440";
|
|
||||||
group = "ente";
|
|
||||||
};
|
|
||||||
age.secrets.minio-root-credentials = {
|
|
||||||
generator.dependencies = [
|
|
||||||
config.age.secrets.minio-access-key
|
|
||||||
config.age.secrets.minio-secret-key
|
|
||||||
];
|
|
||||||
generator.script =
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
decrypt,
|
|
||||||
deps,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
''
|
|
||||||
echo -n "MINIO_ROOT_USER="
|
|
||||||
${decrypt} ${lib.escapeShellArg (builtins.elemAt deps 0).file}
|
|
||||||
echo -n "MINIO_ROOT_PASSWORD="
|
|
||||||
${decrypt} ${lib.escapeShellArg (builtins.elemAt deps 1).file}
|
|
||||||
'';
|
|
||||||
mode = "440";
|
|
||||||
group = "minio";
|
|
||||||
};
|
|
||||||
|
|
||||||
# base64 (url)
|
|
||||||
age.secrets.ente-jwt = {
|
|
||||||
generator.script =
|
|
||||||
{ pkgs, ... }: "${pkgs.openssl}/bin/openssl rand -base64 32 | tr -d '\n' | tr '/+' '_-'";
|
|
||||||
mode = "440";
|
|
||||||
group = "ente";
|
|
||||||
};
|
|
||||||
# base64 (standard)
|
|
||||||
age.secrets.ente-encryption-key = {
|
|
||||||
generator.script = "base64";
|
|
||||||
mode = "440";
|
|
||||||
group = "ente";
|
|
||||||
};
|
|
||||||
# base64 (standard)
|
|
||||||
age.secrets.ente-hash-key = {
|
|
||||||
generator.script = { pkgs, ... }: "${pkgs.openssl}/bin/openssl rand -base64 64 | tr -d '\n'";
|
|
||||||
mode = "440";
|
|
||||||
group = "ente";
|
|
||||||
};
|
|
||||||
age.secrets.ente-smtp-password = {
|
|
||||||
generator.script = "alnum";
|
|
||||||
mode = "440";
|
|
||||||
group = "ente";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.minio = {
|
|
||||||
enable = true;
|
|
||||||
rootCredentialsFile = config.age.secrets.minio-root-credentials.path;
|
|
||||||
};
|
|
||||||
systemd.services.minio = {
|
|
||||||
environment.MINIO_SERVER_URL = "https://${s3Domain}";
|
|
||||||
postStart = ''
|
|
||||||
# Wait until minio is up
|
|
||||||
${lib.getExe pkgs.curl} --retry 5 --retry-connrefused --fail --no-progress-meter -o /dev/null "http://localhost:9000/minio/health/live"
|
|
||||||
|
|
||||||
# Make sure bucket exists
|
|
||||||
mkdir -p ${lib.escapeShellArg config.services.minio.dataDir}/ente
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.ente.after = [ "minio.service" ];
|
|
||||||
services.ente.api = {
|
|
||||||
enable = true;
|
|
||||||
enableLocalDB = true;
|
|
||||||
domain = enteApiDomain;
|
|
||||||
settings = {
|
|
||||||
apps = {
|
|
||||||
accounts = "https://${enteAccountsDomain}";
|
|
||||||
cast = "https://${enteCastDomain}";
|
|
||||||
public-albums = "https://${enteAlbumsDomain}";
|
|
||||||
};
|
|
||||||
|
|
||||||
webauthn = {
|
|
||||||
rpid = enteAccountsDomain;
|
|
||||||
rporigins = [ "https://${enteAccountsDomain}" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# FIXME: blocked on https://github.com/ente-io/ente/issues/5958
|
|
||||||
# smtp = {
|
|
||||||
# host = config.repo.secrets.local.ente.mail.host;
|
|
||||||
# port = 465;
|
|
||||||
# email = config.repo.secrets.local.ente.mail.from;
|
|
||||||
# username = config.repo.secrets.local.ente.mail.user;
|
|
||||||
# password._secret = config.age.secrets.ente-smtp-password.path;
|
|
||||||
# };
|
|
||||||
|
|
||||||
s3 = {
|
|
||||||
use_path_style_urls = true;
|
|
||||||
b2-eu-cen = {
|
|
||||||
endpoint = "https://${s3Domain}";
|
|
||||||
region = "us-east-1";
|
|
||||||
bucket = "ente";
|
|
||||||
key._secret = config.age.secrets.minio-access-key.path;
|
|
||||||
secret._secret = config.age.secrets.minio-secret-key.path;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
jwt.secret._secret = config.age.secrets.ente-jwt.path;
|
|
||||||
key = {
|
|
||||||
encryption._secret = config.age.secrets.ente-encryption-key.path;
|
|
||||||
hash._secret = config.age.secrets.ente-hash-key.path;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# NOTE: services.ente.web is configured separately on both proxy servers!
|
|
||||||
nodes.sentinel.services.nginx =
|
|
||||||
proxyConfig globals.wireguard.proxy-sentinel.hosts.${config.node.name}.ipv4
|
|
||||||
"";
|
|
||||||
nodes.ward-web-proxy.services.nginx =
|
|
||||||
proxyConfig globals.wireguard.proxy-home.hosts.${config.node.name}.ipv4
|
|
||||||
''
|
|
||||||
allow ${globals.net.home-lan.vlans.home.cidrv4};
|
|
||||||
allow ${globals.net.home-lan.vlans.home.cidrv6};
|
|
||||||
# Firezone traffic
|
|
||||||
allow ${globals.net.home-lan.vlans.services.hosts.ward.ipv4};
|
|
||||||
allow ${globals.net.home-lan.vlans.services.hosts.ward.ipv6};
|
|
||||||
deny all;
|
|
||||||
'';
|
|
||||||
}
|
|
|
@ -13,13 +13,6 @@ let
|
||||||
# FIXME: new entry here? make new firezone entry too.
|
# FIXME: new entry here? make new firezone entry too.
|
||||||
homeDomains = [
|
homeDomains = [
|
||||||
globals.services.grafana.domain
|
globals.services.grafana.domain
|
||||||
# TODO: allow multiple domains per global service.
|
|
||||||
"accounts.photos.${globals.domains.me}"
|
|
||||||
"albums.photos.${globals.domains.me}"
|
|
||||||
"api.photos.${globals.domains.me}"
|
|
||||||
"cast.photos.${globals.domains.me}"
|
|
||||||
"photos.${globals.domains.me}"
|
|
||||||
"s3.photos.${globals.domains.me}"
|
|
||||||
globals.services.mealie.domain
|
globals.services.mealie.domain
|
||||||
globals.services.immich.domain
|
globals.services.immich.domain
|
||||||
globals.services.influxdb.domain
|
globals.services.influxdb.domain
|
||||||
|
|
|
@ -113,12 +113,6 @@ in
|
||||||
# FIXME: new entry here? make new firezone entry too.
|
# FIXME: new entry here? make new firezone entry too.
|
||||||
# FIXME: new entry here? make new firezone gateway on ward entry too.
|
# FIXME: new entry here? make new firezone gateway on ward entry too.
|
||||||
globals.services.grafana.domain
|
globals.services.grafana.domain
|
||||||
"accounts.photos.${globals.domains.me}"
|
|
||||||
"albums.photos.${globals.domains.me}"
|
|
||||||
"api.photos.${globals.domains.me}"
|
|
||||||
"cast.photos.${globals.domains.me}"
|
|
||||||
"photos.${globals.domains.me}"
|
|
||||||
"s3.photos.${globals.domains.me}"
|
|
||||||
globals.services.mealie.domain
|
globals.services.mealie.domain
|
||||||
globals.services.immich.domain
|
globals.services.immich.domain
|
||||||
globals.services.influxdb.domain
|
globals.services.influxdb.domain
|
||||||
|
|
|
@ -84,15 +84,4 @@ in
|
||||||
users.groups.acme.members = [ "nginx" ];
|
users.groups.acme.members = [ "nginx" ];
|
||||||
services.nginx.enable = true;
|
services.nginx.enable = true;
|
||||||
services.nginx.recommendedSetup = true;
|
services.nginx.recommendedSetup = true;
|
||||||
|
|
||||||
services.ente.web = {
|
|
||||||
enable = true;
|
|
||||||
domains = {
|
|
||||||
api = "api.photos.${globals.domains.me}";
|
|
||||||
accounts = "accounts.photos.${globals.domains.me}";
|
|
||||||
albums = "albums.photos.${globals.domains.me}";
|
|
||||||
cast = "cast.photos.${globals.domains.me}";
|
|
||||||
photos = "photos.${globals.domains.me}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
./backups.nix
|
./backups.nix
|
||||||
./deterministic-ids.nix
|
./deterministic-ids.nix
|
||||||
./distributed-config.nix
|
./distributed-config.nix
|
||||||
./ente.nix
|
|
||||||
./globals.nix
|
./globals.nix
|
||||||
./mealie.nix
|
./mealie.nix
|
||||||
./immich.nix
|
./immich.nix
|
||||||
|
|
363
modules/ente.nix
363
modules/ente.nix
|
@ -1,363 +0,0 @@
|
||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
utils,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
getExe
|
|
||||||
mkDefault
|
|
||||||
mkEnableOption
|
|
||||||
mkIf
|
|
||||||
mkMerge
|
|
||||||
mkOption
|
|
||||||
mkPackageOption
|
|
||||||
optional
|
|
||||||
types
|
|
||||||
;
|
|
||||||
|
|
||||||
cfgApi = config.services.ente.api;
|
|
||||||
cfgWeb = config.services.ente.web;
|
|
||||||
|
|
||||||
webPackage =
|
|
||||||
enteApp:
|
|
||||||
cfgWeb.package.override {
|
|
||||||
inherit enteApp;
|
|
||||||
enteMainUrl = "https://${cfgWeb.domains.photos}";
|
|
||||||
extraBuildEnv = {
|
|
||||||
NEXT_PUBLIC_ENTE_ENDPOINT = "https://${cfgWeb.domains.api}";
|
|
||||||
NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT = "https://${cfgWeb.domains.albums}";
|
|
||||||
NEXT_TELEMETRY_DISABLED = "1";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultUser = "ente";
|
|
||||||
defaultGroup = "ente";
|
|
||||||
dataDir = "/var/lib/ente";
|
|
||||||
|
|
||||||
yamlFormat = pkgs.formats.yaml { };
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.services.ente = {
|
|
||||||
web = {
|
|
||||||
enable = mkEnableOption "Ente web frontend (Photos, Albums)";
|
|
||||||
package = mkPackageOption pkgs "ente-web" { };
|
|
||||||
|
|
||||||
domains = {
|
|
||||||
api = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "api.ente.example.com";
|
|
||||||
description = ''
|
|
||||||
The domain under which the api is served. This will NOT serve the api itself,
|
|
||||||
but is a required setting to host the frontends! This will automatically be set
|
|
||||||
for you if you enable both the api server and web frontends.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
accounts = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "accounts.ente.example.com";
|
|
||||||
description = "The domain under which the accounts frontend will be served.";
|
|
||||||
};
|
|
||||||
|
|
||||||
cast = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "cast.ente.example.com";
|
|
||||||
description = "The domain under which the cast frontend will be served.";
|
|
||||||
};
|
|
||||||
|
|
||||||
albums = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "albums.ente.example.com";
|
|
||||||
description = "The domain under which the albums frontend will be served.";
|
|
||||||
};
|
|
||||||
|
|
||||||
photos = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "photos.ente.example.com";
|
|
||||||
description = "The domain under which the photos frontend will be served.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
api = {
|
|
||||||
enable = mkEnableOption "Museum (API server for ente.io)";
|
|
||||||
package = mkPackageOption pkgs "museum" { };
|
|
||||||
nginx.enable = mkEnableOption "nginx proxy for the API server";
|
|
||||||
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = defaultUser;
|
|
||||||
description = "User under which museum runs. If you set this option you must make sure the user exists.";
|
|
||||||
};
|
|
||||||
|
|
||||||
group = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = defaultGroup;
|
|
||||||
description = "Group under which museum runs. If you set this option you must make sure the group exists.";
|
|
||||||
};
|
|
||||||
|
|
||||||
domain = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "api.ente.example.com";
|
|
||||||
description = "The domain under which the api will be served.";
|
|
||||||
};
|
|
||||||
|
|
||||||
enableLocalDB = mkEnableOption "the automatic creation of a local postgres database for museum.";
|
|
||||||
|
|
||||||
settings = mkOption {
|
|
||||||
description = ''
|
|
||||||
Museum yaml configuration. Refer to upstream [local.yaml](https://github.com/ente-io/ente/blob/main/server/configurations/local.yaml) for more information.
|
|
||||||
You can specify secret values in this configuration by setting `somevalue._secret = "/path/to/file"` instead of setting `somevalue` directly.
|
|
||||||
'';
|
|
||||||
default = { };
|
|
||||||
type = types.submodule {
|
|
||||||
freeformType = yamlFormat.type;
|
|
||||||
options = {
|
|
||||||
apps = {
|
|
||||||
public-albums = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "https://albums.ente.io";
|
|
||||||
description = ''
|
|
||||||
If you're running a self hosted instance and wish to serve public links,
|
|
||||||
set this to the URL where your albums web app is running.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
cast = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "https://cast.ente.io";
|
|
||||||
description = ''
|
|
||||||
Set this to the URL where your cast page is running.
|
|
||||||
This is for browser and chromecast casting support.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
accounts = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "https://accounts.ente.io";
|
|
||||||
description = ''
|
|
||||||
Set this to the URL where your accounts page is running.
|
|
||||||
This is primarily for passkey support.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
db = {
|
|
||||||
host = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = "The database host";
|
|
||||||
};
|
|
||||||
|
|
||||||
port = mkOption {
|
|
||||||
type = types.port;
|
|
||||||
default = 5432;
|
|
||||||
description = "The database port";
|
|
||||||
};
|
|
||||||
|
|
||||||
name = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = "The database name";
|
|
||||||
};
|
|
||||||
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = "The database user";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = mkMerge [
|
|
||||||
(mkIf cfgApi.enable {
|
|
||||||
services.postgresql = mkIf cfgApi.enableLocalDB {
|
|
||||||
enable = true;
|
|
||||||
ensureUsers = [
|
|
||||||
{
|
|
||||||
name = "ente";
|
|
||||||
ensureDBOwnership = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
ensureDatabases = [ "ente" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
services.ente.web.domains.api = mkIf cfgWeb.enable cfgApi.domain;
|
|
||||||
services.ente.api.settings = {
|
|
||||||
# This will cause logs to be written to stdout/err, which then end up in the journal
|
|
||||||
log-file = mkDefault "";
|
|
||||||
db = mkIf cfgApi.enableLocalDB {
|
|
||||||
host = "/run/postgresql";
|
|
||||||
port = 5432;
|
|
||||||
name = "ente";
|
|
||||||
user = "ente";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.ente = {
|
|
||||||
description = "Ente.io Museum API Server";
|
|
||||||
after = [ "network.target" ] ++ optional cfgApi.enableLocalDB "postgresql.service";
|
|
||||||
requires = optional cfgApi.enableLocalDB "postgresql.service";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
|
|
||||||
preStart = ''
|
|
||||||
# Generate config including secret values. YAML is a superset of JSON, so we can use this here.
|
|
||||||
${utils.genJqSecretsReplacementSnippet cfgApi.settings "/run/ente/local.yaml"}
|
|
||||||
|
|
||||||
# Setup paths
|
|
||||||
mkdir -p ${dataDir}/configurations
|
|
||||||
ln -sTf /run/ente/local.yaml ${dataDir}/configurations/local.yaml
|
|
||||||
'';
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
ExecStart = getExe cfgApi.package;
|
|
||||||
Type = "simple";
|
|
||||||
Restart = "on-failure";
|
|
||||||
|
|
||||||
AmbientCapablities = [ ];
|
|
||||||
CapabilityBoundingSet = [ ];
|
|
||||||
LockPersonality = true;
|
|
||||||
MemoryDenyWriteExecute = true;
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
PrivateMounts = true;
|
|
||||||
PrivateTmp = true;
|
|
||||||
PrivateUsers = false;
|
|
||||||
ProcSubset = "pid";
|
|
||||||
ProtectClock = true;
|
|
||||||
ProtectControlGroups = true;
|
|
||||||
ProtectHome = true;
|
|
||||||
ProtectHostname = true;
|
|
||||||
ProtectKernelLogs = true;
|
|
||||||
ProtectKernelModules = true;
|
|
||||||
ProtectKernelTunables = true;
|
|
||||||
ProtectProc = "invisible";
|
|
||||||
ProtectSystem = "strict";
|
|
||||||
RestrictAddressFamilies = [
|
|
||||||
"AF_INET"
|
|
||||||
"AF_INET6"
|
|
||||||
"AF_NETLINK"
|
|
||||||
"AF_UNIX"
|
|
||||||
];
|
|
||||||
RestrictNamespaces = true;
|
|
||||||
RestrictRealtime = true;
|
|
||||||
RestrictSUIDSGID = true;
|
|
||||||
SystemCallArchitectures = "native";
|
|
||||||
SystemCallFilter = "@system-service";
|
|
||||||
UMask = "077";
|
|
||||||
|
|
||||||
BindReadOnlyPaths = [
|
|
||||||
"${cfgApi.package}/share/museum/migrations:${dataDir}/migrations"
|
|
||||||
"${cfgApi.package}/share/museum/mail-templates:${dataDir}/mail-templates"
|
|
||||||
"${cfgApi.package}/share/museum/web-templates:${dataDir}/web-templates"
|
|
||||||
];
|
|
||||||
|
|
||||||
User = cfgApi.user;
|
|
||||||
Group = cfgApi.group;
|
|
||||||
|
|
||||||
SyslogIdentifier = "ente";
|
|
||||||
StateDirectory = "ente";
|
|
||||||
WorkingDirectory = dataDir;
|
|
||||||
RuntimeDirectory = "ente";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Environment MUST be called local, otherwise we cannot log to stdout
|
|
||||||
environment = {
|
|
||||||
ENVIRONMENT = "local";
|
|
||||||
GIN_MODE = "release";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
users = {
|
|
||||||
users = mkIf (cfgApi.user == defaultUser) {
|
|
||||||
${defaultUser} = {
|
|
||||||
description = "ente.io museum service user";
|
|
||||||
inherit (cfgApi) group;
|
|
||||||
isSystemUser = true;
|
|
||||||
home = dataDir;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
groups = mkIf (cfgApi.group == defaultGroup) { ${defaultGroup} = { }; };
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = mkIf cfgApi.nginx.enable {
|
|
||||||
enable = true;
|
|
||||||
upstreams.museum = {
|
|
||||||
servers."localhost:8080" = { };
|
|
||||||
extraConfig = ''
|
|
||||||
zone museum 64k;
|
|
||||||
keepalive 20;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualHosts.${cfgApi.domain} = {
|
|
||||||
forceSSL = mkDefault true;
|
|
||||||
locations."/".proxyPass = "http://museum";
|
|
||||||
extraConfig = ''
|
|
||||||
client_max_body_size 4M;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
(mkIf cfgWeb.enable {
|
|
||||||
services.ente.api.settings = mkIf cfgApi.enable {
|
|
||||||
apps = {
|
|
||||||
accounts = "https://${cfgWeb.domains.accounts}";
|
|
||||||
cast = "https://${cfgWeb.domains.cast}";
|
|
||||||
public-albums = "https://${cfgWeb.domains.albums}";
|
|
||||||
};
|
|
||||||
|
|
||||||
webauthn = {
|
|
||||||
rpid = cfgWeb.domains.accounts;
|
|
||||||
rporigins = [ "https://${cfgWeb.domains.accounts}" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx =
|
|
||||||
let
|
|
||||||
domainFor = app: cfgWeb.domains.${app};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
enable = true;
|
|
||||||
virtualHosts.${domainFor "accounts"} = {
|
|
||||||
forceSSL = mkDefault true;
|
|
||||||
locations."/" = {
|
|
||||||
root = webPackage "accounts";
|
|
||||||
tryFiles = "$uri $uri.html /index.html";
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Access-Control-Allow-Origin 'https://${cfgWeb.domains.api}';
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
virtualHosts.${domainFor "cast"} = {
|
|
||||||
forceSSL = mkDefault true;
|
|
||||||
locations."/" = {
|
|
||||||
root = webPackage "cast";
|
|
||||||
tryFiles = "$uri $uri.html /index.html";
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Access-Control-Allow-Origin 'https://${cfgWeb.domains.api}';
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
virtualHosts.${domainFor "photos"} = {
|
|
||||||
serverAliases = [
|
|
||||||
(domainFor "albums") # the albums app is shared with the photos frontend
|
|
||||||
];
|
|
||||||
forceSSL = mkDefault true;
|
|
||||||
locations."/" = {
|
|
||||||
root = webPackage "photos";
|
|
||||||
tryFiles = "$uri $uri.html /index.html";
|
|
||||||
extraConfig = ''
|
|
||||||
add_header Access-Control-Allow-Origin 'https://${cfgWeb.domains.api}';
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
meta.maintainers = with lib.maintainers; [ oddlama ];
|
|
||||||
}
|
|
|
@ -18,7 +18,5 @@ _inputs: [
|
||||||
# xy = pythonPrev.xy.overrideAttrs { };
|
# xy = pythonPrev.xy.overrideAttrs { };
|
||||||
# })
|
# })
|
||||||
# ];
|
# ];
|
||||||
|
|
||||||
ente-web = prev.callPackage ./ente-web.nix { };
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
stdenv,
|
|
||||||
fetchFromGitHub,
|
|
||||||
fetchYarnDeps,
|
|
||||||
nodejs,
|
|
||||||
yarnConfigHook,
|
|
||||||
yarnBuildHook,
|
|
||||||
nix-update-script,
|
|
||||||
extraBuildEnv ? { },
|
|
||||||
# This package contains serveral sub-applications. This specifies which of them you want to build.
|
|
||||||
enteApp ? "photos",
|
|
||||||
# Accessing some apps (such as account) directly will result in a hardcoded redirect to ente.io.
|
|
||||||
# To prevent users from accidentally logging in to ente.io instead of the selfhosted instance, you
|
|
||||||
# can set this parameter to override these occurrences with your own url. Must include the schema.
|
|
||||||
# Example: https://my-ente.example.com
|
|
||||||
enteMainUrl ? null,
|
|
||||||
}:
|
|
||||||
|
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
|
||||||
pname = "ente-web-${enteApp}";
|
|
||||||
version = "1.0.4";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "ente-io";
|
|
||||||
repo = "ente";
|
|
||||||
sparseCheckout = [ "web" ];
|
|
||||||
tag = "photos-v${finalAttrs.version}";
|
|
||||||
fetchSubmodules = true;
|
|
||||||
hash = "sha256-M1kAZgqjbWNn6LqymtWRmAk/v0vWEGbyS50lVrsr85o=";
|
|
||||||
};
|
|
||||||
sourceRoot = "${finalAttrs.src.name}/web";
|
|
||||||
|
|
||||||
offlineCache = fetchYarnDeps {
|
|
||||||
yarnLock = "${finalAttrs.src}/web/yarn.lock";
|
|
||||||
hash = "sha256-EYhYwy6+7bgWckU/7SfL1PREWw9JUgKxWadSVtoZwXs=";
|
|
||||||
};
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
yarnConfigHook
|
|
||||||
yarnBuildHook
|
|
||||||
nodejs
|
|
||||||
];
|
|
||||||
|
|
||||||
# See: https://github.com/ente-io/ente/blob/main/web/apps/photos/.env
|
|
||||||
env = extraBuildEnv;
|
|
||||||
|
|
||||||
# Replace hardcoded ente.io urls if desired
|
|
||||||
postPatch = lib.optionalString (enteMainUrl != null) ''
|
|
||||||
substituteInPlace \
|
|
||||||
apps/payments/src/services/billing.ts \
|
|
||||||
apps/photos/src/pages/shared-albums.tsx \
|
|
||||||
--replace-fail "https://ente.io" ${lib.escapeShellArg enteMainUrl}
|
|
||||||
|
|
||||||
substituteInPlace \
|
|
||||||
apps/accounts/src/pages/index.tsx \
|
|
||||||
--replace-fail "https://web.ente.io" ${lib.escapeShellArg enteMainUrl}
|
|
||||||
'';
|
|
||||||
|
|
||||||
yarnBuildScript = "build:${enteApp}";
|
|
||||||
installPhase =
|
|
||||||
let
|
|
||||||
distName = if enteApp == "payments" then "dist" else "out";
|
|
||||||
in
|
|
||||||
''
|
|
||||||
runHook preInstall
|
|
||||||
|
|
||||||
cp -r apps/${enteApp}/${distName} $out
|
|
||||||
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
|
|
||||||
passthru.updateScript = nix-update-script {
|
|
||||||
extraArgs = [
|
|
||||||
"--version-regex"
|
|
||||||
"photos-v(.*)"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "Ente application web frontends";
|
|
||||||
homepage = "https://ente.io/";
|
|
||||||
changelog = "https://github.com/ente-io/ente/releases";
|
|
||||||
license = lib.licenses.agpl3Only;
|
|
||||||
maintainers = with lib.maintainers; [
|
|
||||||
pinpox
|
|
||||||
oddlama
|
|
||||||
];
|
|
||||||
platforms = lib.platforms.all;
|
|
||||||
};
|
|
||||||
})
|
|
|
@ -47,7 +47,6 @@
|
||||||
pkgs.gpu-screen-recorder
|
pkgs.gpu-screen-recorder
|
||||||
pkgs.gpu-screen-recorder-gtk
|
pkgs.gpu-screen-recorder-gtk
|
||||||
pkgs.spotify
|
pkgs.spotify
|
||||||
pkgs.ente-desktop
|
|
||||||
];
|
];
|
||||||
|
|
||||||
# TODO wrap thunderbird bin and set LC_ALL=de_DE.UTF-8 because thunderbird uses wrong date and time formatting with C.UTF-8
|
# TODO wrap thunderbird bin and set LC_ALL=de_DE.UTF-8 because thunderbird uses wrong date and time formatting with C.UTF-8
|
||||||
|
@ -81,7 +80,6 @@
|
||||||
".config/gpu-screen-recorder"
|
".config/gpu-screen-recorder"
|
||||||
".config/obsidian"
|
".config/obsidian"
|
||||||
".config/spotify"
|
".config/spotify"
|
||||||
".config/ente"
|
|
||||||
".factorio" # XDG spec? nah, apprently overrated.
|
".factorio" # XDG spec? nah, apprently overrated.
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue