nixos/archtika: init at 1.0.1 (#365218)

Sandro 32d2826f d1c535f6

Changed files
+378
maintainers
nixos
doc
manual
release-notes
modules
services
web-apps
pkgs
by-name
ar
archtika
+6
maintainers/maintainer-list.nix
···
github = "thilobillerbeck";
githubId = 7442383;
};
thled = {
name = "Thomas Le Duc";
email = "dev@tleduc.de";
···
github = "thilobillerbeck";
githubId = 7442383;
};
+
thiloho = {
+
name = "Thilo Hohlt";
+
email = "thilo.hohlt@tutanota.com";
+
github = "thiloho";
+
githubId = 123883702;
+
};
thled = {
name = "Thomas Le Duc";
email = "dev@tleduc.de";
+2
nixos/doc/manual/release-notes/rl-2505.section.md
···
- [Bonsai](https://git.sr.ht/~stacyharper/bonsai), a general-purpose event mapper/state machine primarily used to create complex key shortcuts, and as part of the [SXMO](https://sxmo.org/) desktop environment. Available as [services.bonsaid](#opt-services.bonsaid.enable).
- [scanservjs](https://github.com/sbs20/scanservjs/), a web UI for SANE scanners. Available at [services.scanservjs](#opt-services.scanservjs.enable).
- [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](options.html#opt-services.kimai).
···
- [Bonsai](https://git.sr.ht/~stacyharper/bonsai), a general-purpose event mapper/state machine primarily used to create complex key shortcuts, and as part of the [SXMO](https://sxmo.org/) desktop environment. Available as [services.bonsaid](#opt-services.bonsaid.enable).
+
- [archtika](https://github.com/archtika/archtika), a FLOSS, modern, performant, lightweight and self‑hosted CMS. Available as [services.archtika](#opt-services.archtika.enable).
+
- [scanservjs](https://github.com/sbs20/scanservjs/), a web UI for SANE scanners. Available at [services.scanservjs](#opt-services.scanservjs.enable).
- [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](options.html#opt-services.kimai).
+1
nixos/modules/module-list.nix
···
./services/web-apps/agorakit.nix
./services/web-apps/alps.nix
./services/web-apps/anuko-time-tracker.nix
./services/web-apps/artalk.nix
./services/web-apps/audiobookshelf.nix
./services/web-apps/bluemap.nix
···
./services/web-apps/agorakit.nix
./services/web-apps/alps.nix
./services/web-apps/anuko-time-tracker.nix
+
./services/web-apps/archtika.nix
./services/web-apps/artalk.nix
./services/web-apps/audiobookshelf.nix
./services/web-apps/bluemap.nix
+307
nixos/modules/services/web-apps/archtika.nix
···
···
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
+
+
let
+
inherit (lib)
+
mkEnableOption
+
mkOption
+
mkIf
+
mkPackageOption
+
types
+
;
+
cfg = config.services.archtika;
+
in
+
{
+
options.services.archtika = {
+
enable = mkEnableOption "Whether to enable the archtika service";
+
+
package = mkPackageOption pkgs "archtika" { };
+
+
user = mkOption {
+
type = types.str;
+
default = "archtika";
+
description = "User account under which archtika runs.";
+
};
+
+
group = mkOption {
+
type = types.str;
+
default = "archtika";
+
description = "Group under which archtika runs.";
+
};
+
+
databaseName = mkOption {
+
type = types.str;
+
default = "archtika";
+
description = "Name of the PostgreSQL database for archtika.";
+
};
+
+
apiPort = mkOption {
+
type = types.port;
+
default = 5000;
+
description = "Port on which the API runs.";
+
};
+
+
apiAdminPort = mkOption {
+
type = types.port;
+
default = 7500;
+
description = "Port on which the API admin server runs.";
+
};
+
+
webAppPort = mkOption {
+
type = types.port;
+
default = 10000;
+
description = "Port on which the web application runs.";
+
};
+
+
domain = mkOption {
+
type = types.str;
+
description = "Domain to use for the application.";
+
};
+
+
settings = mkOption {
+
description = "Settings for the running archtika application.";
+
type = types.submodule {
+
options = {
+
disableRegistration = mkOption {
+
type = types.bool;
+
default = false;
+
description = "By default any user can create an account. That behavior can be disabled with this option.";
+
};
+
maxUserWebsites = mkOption {
+
type = types.ints.positive;
+
default = 2;
+
description = "Maximum number of websites allowed per user by default.";
+
};
+
maxWebsiteStorageSize = mkOption {
+
type = types.ints.positive;
+
default = 50;
+
description = "Maximum amount of disk space in MB allowed per user website by default.";
+
};
+
};
+
};
+
};
+
};
+
+
config = mkIf cfg.enable (
+
let
+
baseHardenedSystemdOptions = {
+
CapabilityBoundingSet = "";
+
LockPersonality = true;
+
NoNewPrivileges = true;
+
PrivateDevices = true;
+
PrivateTmp = true;
+
ProtectClock = true;
+
ProtectControlGroups = true;
+
ProtectHome = true;
+
ProtectHostname = true;
+
ProtectKernelLogs = true;
+
ProtectKernelModules = true;
+
ProtectKernelTunables = true;
+
ProtectSystem = "strict";
+
RemoveIPC = true;
+
RestrictNamespaces = true;
+
RestrictRealtime = true;
+
RestrictSUIDSGID = true;
+
SystemCallArchitectures = "native";
+
SystemCallFilter = [
+
"@system-service"
+
"~@privileged"
+
"~@resources"
+
];
+
ReadWritePaths = [ "/var/www/archtika-websites" ];
+
};
+
in
+
{
+
users.users.${cfg.user} = {
+
isSystemUser = true;
+
group = cfg.group;
+
};
+
+
users.groups.${cfg.group} = {
+
members = [
+
"nginx"
+
"postgres"
+
];
+
};
+
+
systemd.tmpfiles.settings."10-archtika" = {
+
"/var/www" = {
+
d = {
+
mode = "0755";
+
user = "root";
+
group = "root";
+
};
+
};
+
"/var/www/archtika-websites" = {
+
d = {
+
mode = "0770";
+
user = cfg.user;
+
group = cfg.group;
+
};
+
};
+
};
+
+
systemd.services.archtika-api = {
+
description = "archtika API service";
+
wantedBy = [ "multi-user.target" ];
+
after = [
+
"network.target"
+
"postgresql.service"
+
];
+
+
path = [ config.services.postgresql.package ];
+
+
serviceConfig = baseHardenedSystemdOptions // {
+
User = cfg.user;
+
Group = cfg.group;
+
Restart = "always";
+
WorkingDirectory = "${cfg.package}/rest-api";
+
RestrictAddressFamilies = [
+
"AF_INET"
+
"AF_INET6"
+
"AF_UNIX"
+
];
+
};
+
+
script =
+
let
+
dbUrl = user: "postgres://${user}@/${cfg.databaseName}?host=/var/run/postgresql";
+
in
+
''
+
JWT_SECRET=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c64)
+
+
psql ${dbUrl "postgres"} \
+
-c "ALTER DATABASE ${cfg.databaseName} SET \"app.jwt_secret\" TO '$JWT_SECRET'" \
+
-c "ALTER DATABASE ${cfg.databaseName} SET \"app.website_max_storage_size\" TO ${toString cfg.settings.maxWebsiteStorageSize}" \
+
-c "ALTER DATABASE ${cfg.databaseName} SET \"app.website_max_number_user\" TO ${toString cfg.settings.maxUserWebsites}"
+
+
${lib.getExe pkgs.dbmate} --url "${dbUrl "postgres"}&sslmode=disable" --migrations-dir ${cfg.package}/rest-api/db/migrations up
+
+
PGRST_SERVER_CORS_ALLOWED_ORIGINS="https://${cfg.domain}" \
+
PGRST_ADMIN_SERVER_PORT=${toString cfg.apiAdminPort} \
+
PGRST_SERVER_PORT=${toString cfg.apiPort} \
+
PGRST_DB_SCHEMAS="api" \
+
PGRST_DB_ANON_ROLE="anon" \
+
PGRST_OPENAPI_MODE="ignore-privileges" \
+
PGRST_DB_URI=${dbUrl "authenticator"} \
+
PGRST_JWT_SECRET="$JWT_SECRET" \
+
${lib.getExe pkgs.postgrest}
+
'';
+
};
+
+
systemd.services.archtika-web = {
+
description = "archtika Web App service";
+
wantedBy = [ "multi-user.target" ];
+
after = [ "network.target" ];
+
+
serviceConfig = baseHardenedSystemdOptions // {
+
User = cfg.user;
+
Group = cfg.group;
+
Restart = "always";
+
WorkingDirectory = "${cfg.package}/web-app";
+
RestrictAddressFamilies = [
+
"AF_INET"
+
"AF_INET6"
+
];
+
};
+
+
environment = {
+
REGISTRATION_IS_DISABLED = toString cfg.settings.disableRegistration;
+
BODY_SIZE_LIMIT = "10M";
+
ORIGIN = "https://${cfg.domain}";
+
PORT = toString cfg.webAppPort;
+
};
+
+
script = "${lib.getExe pkgs.nodejs} ${cfg.package}/web-app";
+
};
+
+
services.postgresql = {
+
enable = true;
+
ensureDatabases = [ cfg.databaseName ];
+
extensions = ps: with ps; [ pgjwt ];
+
authentication = lib.mkOverride 11 ''
+
local postgres postgres trust
+
local ${cfg.databaseName} all trust
+
'';
+
};
+
+
systemd.services.postgresql = {
+
path = with pkgs; [
+
gnutar
+
gzip
+
];
+
serviceConfig = {
+
ReadWritePaths = [ "/var/www/archtika-websites" ];
+
SystemCallFilter = [ "@system-service" ];
+
};
+
};
+
+
services.nginx = {
+
enable = true;
+
recommendedProxySettings = true;
+
recommendedTlsSettings = true;
+
recommendedZstdSettings = true;
+
recommendedOptimisation = true;
+
+
appendHttpConfig = ''
+
map $http_cookie $archtika_auth_header {
+
default "";
+
"~*session_token=([^;]+)" "Bearer $1";
+
}
+
'';
+
+
virtualHosts = {
+
"${cfg.domain}" = {
+
useACMEHost = cfg.domain;
+
forceSSL = true;
+
locations = {
+
"/" = {
+
proxyPass = "http://127.0.0.1:${toString cfg.webAppPort}";
+
};
+
"/previews/" = {
+
alias = "/var/www/archtika-websites/previews/";
+
index = "index.html";
+
tryFiles = "$uri $uri/ $uri.html =404";
+
};
+
"/api/rpc/export_articles_zip" = {
+
proxyPass = "http://127.0.0.1:${toString cfg.apiPort}/rpc/export_articles_zip";
+
extraConfig = ''
+
default_type application/json;
+
proxy_set_header Authorization $archtika_auth_header;
+
'';
+
};
+
"/api/" = {
+
proxyPass = "http://127.0.0.1:${toString cfg.apiPort}/";
+
extraConfig = ''
+
default_type application/json;
+
'';
+
};
+
"/api/rpc/register" = mkIf cfg.settings.disableRegistration {
+
extraConfig = ''
+
deny all;
+
'';
+
};
+
};
+
};
+
"~^(?<subdomain>.+)\\.${cfg.domain}$" = {
+
useACMEHost = cfg.domain;
+
forceSSL = true;
+
locations = {
+
"/" = {
+
root = "/var/www/archtika-websites/$subdomain";
+
index = "index.html";
+
tryFiles = "$uri $uri/ $uri.html =404";
+
};
+
};
+
};
+
};
+
};
+
}
+
);
+
+
meta.maintainers = [ lib.maintainers.thiloho ];
+
}
+62
pkgs/by-name/ar/archtika/package.nix
···
···
+
{
+
lib,
+
stdenv,
+
buildNpmPackage,
+
importNpmLock,
+
symlinkJoin,
+
fetchFromGitHub,
+
nix-update-script,
+
}:
+
+
let
+
version = "1.2.0";
+
+
src = fetchFromGitHub {
+
owner = "archtika";
+
repo = "archtika";
+
tag = "v${version}";
+
hash = "sha256-ba9da7LqCE/e2lhRVHD7GOhwOj1fNTBbN/pARPMzIg4=";
+
};
+
+
web = buildNpmPackage {
+
name = "web-app";
+
src = "${src}/web-app";
+
npmDepsHash = "sha256-RTyo7K/Hr1hBGtcBKynrziUInl91JqZl84NkJg16ufA=";
+
npmFlags = [ "--legacy-peer-deps" ];
+
installPhase = ''
+
mkdir -p $out/web-app
+
cp package.json $out/web-app
+
cp -r node_modules $out/web-app
+
cp -r build/* $out/web-app
+
cp -r template-styles $out/web-app
+
'';
+
};
+
+
api = stdenv.mkDerivation {
+
name = "api";
+
src = "${src}/rest-api";
+
installPhase = ''
+
mkdir -p $out/rest-api/db/migrations
+
cp -r db/migrations/* $out/rest-api/db/migrations
+
'';
+
};
+
in
+
symlinkJoin {
+
pname = "archtika";
+
inherit version;
+
+
paths = [
+
web
+
api
+
];
+
+
passthru.updateScript = nix-update-script { };
+
+
meta = {
+
description = "Modern, performant and lightweight CMS";
+
homepage = "https://archtika.com";
+
license = lib.licenses.gpl3;
+
maintainers = [ lib.maintainers.thiloho ];
+
platforms = lib.platforms.unix;
+
};
+
}