materia/flake.nix

681 lines
20 KiB
Nix

{
description = "Materia";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
dream2nix = {
url = "github:nix-community/dream2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
bonfire.url = "github:L-Nafaryus/bonfire";
nix-std.url = "github:chessai/nix-std";
};
outputs = {
self,
nixpkgs,
dream2nix,
bonfire,
nix-std,
...
}: let
system = "x86_64-linux";
pkgs = import nixpkgs {inherit system;};
bonLib = bonfire.lib;
dreamBuildPackage = {
module,
meta ? {},
extraModules ? [],
extraArgs ? {},
}:
(
nixpkgs.lib.evalModules {
modules = [module] ++ extraModules;
specialArgs =
{
inherit dream2nix;
packageSets.nixpkgs = pkgs;
}
// extraArgs;
}
)
.config
.public
// {inherit meta;};
in {
packages.x86_64-linux = {
materia-frontend-vue = dreamBuildPackage {
module = {
lib,
config,
dream2nix,
...
}: {
name = "materia-frontend-vue";
version = "0.1.1";
imports = [
dream2nix.modules.dream2nix.WIP-nodejs-builder-v3
];
mkDerivation = {
src = ./packages/frontend;
};
deps = {nixpkgs, ...}: {
inherit
(nixpkgs)
fetchFromGitHub
stdenv
;
};
WIP-nodejs-builder-v3 = {
packageLockFile = "${config.mkDerivation.src}/package-lock.json";
};
};
meta = with nixpkgs.lib; {
description = "Materia is a simple and fast cloud storage (vue)";
homepage = "https://materia.elnafo.ru";
license = licenses.mit;
maintainers = with bonLib.maintainers; [L-Nafaryus];
broken = false;
};
};
materia-frontend = dreamBuildPackage {
extraArgs = {
inherit (self.packages.x86_64-linux) materia-frontend-vue;
};
module = {
config,
lib,
dream2nix,
materia-frontend-vue,
...
}: {
imports = [dream2nix.modules.dream2nix.WIP-python-pdm];
pdm.lockfile = ./pdm.lock;
pdm.pyproject = ./packages/frontend/pyproject.toml;
deps = _: {
python = pkgs.python312;
};
mkDerivation = {
src = ./packages/frontend;
buildInputs = [
pkgs.python312.pkgs.pdm-backend
];
configurePhase = ''
mkdir -p target/materia_frontend
cp -rv ${materia-frontend-vue}/dist/* ./target/materia_frontend/
cp -rv templates ./target/materia_frontend/
touch target/materia_frontend/__init__.py
'';
};
};
meta = with nixpkgs.lib; {
description = "Materia is a simple and fast cloud storage (frontend)";
homepage = "https://materia.elnafo.ru";
license = licenses.mit;
maintainers = with bonLib.maintainers; [L-Nafaryus];
broken = false;
};
};
materia-server = pkgs.callPackage ({
withFrontend ? true,
withDocs ? true,
...
}:
dreamBuildPackage {
extraArgs = {
inherit (self.packages.x86_64-linux) materia-frontend materia-docs;
};
module = {
config,
lib,
dream2nix,
materia-frontend,
materia-docs,
...
}: {
imports = [dream2nix.modules.dream2nix.WIP-python-pdm];
pdm.lockfile = ./pdm.lock;
pdm.pyproject = ./packages/server/pyproject.toml;
deps = _: {
python = pkgs.python312;
};
mkDerivation = {
src = ./packages/server;
buildInputs = [
pkgs.python312.pkgs.pdm-backend
];
nativeBuildInputs = [
pkgs.python312.pkgs.wrapPython
];
propagatedBuildInputs =
lib.optionals withFrontend [
materia-frontend
]
++ lib.optionals withDocs [materia-docs];
};
};
meta = with nixpkgs.lib; {
description = "Materia is a simple and fast cloud storage";
homepage = "https://materia.elnafo.ru";
license = licenses.mit;
maintainers = with bonLib.maintainers; [L-Nafaryus];
broken = false;
mainProgram = "materia";
};
}) {};
materia-docs = dreamBuildPackage {
extraArgs = {
materia-server = self.packages.x86_64-linux.materia-server.override {
withFrontend = false;
withDocs = false;
};
};
module = {
config,
lib,
dream2nix,
materia-server,
...
}: {
imports = [dream2nix.modules.dream2nix.WIP-python-pdm];
pdm.lockfile = ./pdm.lock;
pdm.pyproject = ./packages/docs/pyproject.toml;
deps = _: {
python = pkgs.python312;
};
mkDerivation = {
src = ./packages/docs;
buildInputs = [
pkgs.python312.pkgs.pdm-backend
];
nativeBuildInputs = [pkgs.mkdocs materia-server];
configurePhase = ''
mkdir -p target/materia_docs
mkdocs build
touch target/materia_docs/__init__.py
'';
};
};
meta = with nixpkgs.lib; {
description = "Materia is a simple and fast cloud storage (docs)";
homepage = "https://materia.elnafo.ru";
license = licenses.mit;
maintainers = with bonLib.maintainers; [L-Nafaryus];
broken = false;
};
};
postgresql-devel = bonfire.packages.x86_64-linux.postgresql;
redis-devel = bonfire.packages.x86_64-linux.redis;
materia-devel = let
user = "materia";
dataDir = "/var/lib/materia";
entryPoint = pkgs.writeTextDir "entrypoint.sh" ''
materia start
'';
in
pkgs.dockerTools.buildImage {
name = "materia";
tag = "latest";
copyToRoot = pkgs.buildEnv {
name = "image-root";
pathsToLink = ["/bin" "/etc" "/"];
paths = with pkgs; [
bash
self.packages.x86_64-linux.materia-server
entryPoint
];
};
runAsRoot = with pkgs; ''
#!${runtimeShell}
${dockerTools.shadowSetup}
groupadd -r ${user}
useradd -r -g ${user} --home-dir=${dataDir} ${user}
mkdir -p ${dataDir}
chown -R ${user}:${user} ${dataDir}
'';
config = {
Entrypoint = ["bash" "/entrypoint.sh"];
StopSignal = "SIGINT";
User = "${user}:${user}";
WorkingDir = dataDir;
ExposedPorts = {
"54601/tcp" = {};
};
Env = [
"MATERIA_APPLICATION__WORKING_DIRECTORY=${dataDir}"
];
};
};
};
devShells.x86_64-linux.default = pkgs.mkShell {
buildInputs = with pkgs; [postgresql redis pdm nodejs python312];
# greenlet requires libstdc++
LD_LIBRARY_PATH = nixpkgs.lib.makeLibraryPath [pkgs.stdenv.cc.cc];
};
nixosModules = rec {
materia = {
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.services.materia;
in {
options.services.materia = {
enable = mkEnableOption "Enables the Materia service";
package = mkOption {
type = types.package;
default = self.packages.x86_64-linux.materia-server;
description = "The package to use.";
};
application = mkOption {
type = types.submodule {
options = {
user = mkOption {
type = types.str;
};
group = mkOption {
type = types.str;
};
mode = mkOption {
type = types.str;
};
working_directory = mkOption {
type = types.path;
default = "/var/lib/materia";
};
};
};
default = {
user = "materia";
group = "materia";
mode = "production";
working_directory = "/var/lib/materia";
};
};
server = mkOption {
type = types.submodule {
options = {
scheme = mkOption {
type = types.str;
};
address = mkOption {
type = types.str;
};
port = mkOption {
type = types.port;
};
domain = mkOption {
type = types.str;
};
};
};
default = {
scheme = "http";
address = "127.0.0.1";
port = 54601;
domain = "localhost";
};
};
database = mkOption {
type = types.submodule {
options = {
backend = mkOption {
type = types.str;
default = "postgresql";
};
scheme = mkOption {
type = types.str;
default = "postgresql+asyncpg";
};
address = mkOption {
type = types.str;
default = "127.0.0.1";
};
port = mkOption {
type = types.port;
default = 5432;
};
name = mkOption {
type = types.nullOr types.str;
default = "materia";
};
user = mkOption {
type = types.str;
default = "materia";
};
password = mkOption {
type = types.nullOr (types.oneOf [types.str types.path]);
default = null;
};
};
};
default = {
backend = "postgresql";
scheme = "postgresql+asyncpg";
address = "127.0.0.1";
port = 5432;
name = "materia";
user = "materia";
password = null;
};
};
cache = mkOption {
type = types.submodule {
options = {
backend = mkOption {
type = types.str;
default = "redis";
};
scheme = mkOption {
type = types.str;
default = "redis";
};
address = mkOption {
type = types.str;
default = "127.0.0.1";
};
port = mkOption {
type = types.port;
default = 6379;
};
database = mkOption {
type = types.nullOr types.int;
default = 0;
};
user = mkOption {
type = types.str;
default = "materia";
};
password = mkOption {
type = types.nullOr (types.oneOf [types.str types.path]);
default = null;
};
};
};
default = {
backed = "redis";
scheme = "redis";
address = "127.0.0.1";
port = 6379;
database = 0;
user = "materia";
password = null;
};
};
security = mkOption {
type = types.submodule {
options = {
secret_key = mkOption {
type = types.nullOr (types.oneOf [types.str types.path]);
};
password_min_length = mkOption {
type = types.int;
};
password_hash_algo = mkOption {
type = types.nullOr types.str;
};
cookie_http_only = mkOption {
type = types.bool;
};
cookie_access_token_name = mkOption {
type = types.str;
};
cookie_refresh_token_name = mkOption {
type = types.str;
};
};
};
default = {
secret_key = null;
password_min_length = 8;
password_hash_algo = "bcrypt";
cookie_http_only = true;
cookie_access_token_name = "materia_at";
cookie_refresh_token_name = "materia_rt";
};
};
oauth2 = mkOption {
type = types.submodule {
options = {
enabled = mkOption {
type = types.bool;
};
jwt_signing_algo = mkOption {
type = types.str;
};
jwt_singing_key = mkOption {
type = types.nullOr (types.oneOf [types.str types.path]);
};
jwt_secret = mkOption {
type = types.nullOr (types.oneOf [types.str types.path]);
};
access_token_lifetime = mkOption {
type = types.int;
};
refresh_token_lifetime = mkOption {
type = types.int;
};
};
};
default = {
enabled = true;
jwt_signing_algo = "HS256";
jwt_singing_key = null;
jwt_secret = "changeme";
access_token_lifetime = 3600;
refresh_token_lifetime = 730 * 60;
};
};
mailer = mkOption {
type = types.submodule {
options = {
enabled = mkOption {
type = types.bool;
default = false;
};
scheme = mkOption {
type = types.nullOr types.str;
default = null;
};
address = mkOption {
type = types.nullOr types.str;
default = null;
};
port = mkOption {
type = types.nullOr types.int;
default = null;
};
helo = mkOption {
type = types.bool;
default = true;
};
cert_file = mkOption {
type = types.nullOr types.path;
default = null;
};
key_file = mkOption {
type = types.nullOr types.path;
default = null;
};
sender = mkOption {
type = types.nullOr types.str;
default = null;
};
user = mkOption {
type = types.nullOr types.str;
default = null;
};
password = mkOption {
type = types.nullOr (types.oneOf [types.str types.path]);
default = null;
};
plain_text = mkOption {
type = types.bool;
default = false;
};
};
};
default = {};
};
cron = mkOption {
type = types.submodule {
options = {
workers_count = mkOption {
type = types.int;
};
};
};
default = {
workers_count = 1;
};
};
repository = mkOption {
type = types.submodule {
options = {
capacity = mkOption {
type = types.int;
};
};
};
default = {capacity = 5368709120;};
};
misc = mkOption {
type = types.submodule {
options = {
enable_client = mkOption {
type = types.bool;
};
enable_docs = mkOption {
type = types.bool;
};
enable_api_docs = mkOption {
type = types.bool;
};
};
};
default = {
enable_client = true;
enable_docs = true;
enable_api_docs = false;
};
};
};
config = mkIf cfg.enable {
users.users.materia = {
description = "Materia service user";
home = cfg.application.working_directory;
createHome = true;
isSystemUser = true;
group = "materia";
};
users.groups.materia = {};
systemd.services.materia = {
description = "Materia service";
wantedBy = ["multi-user.target"];
after = ["network.target"];
serviceConfig = {
Restart = "always";
ExecStart = "${lib.getExe cfg.package} start";
User = "materia";
WorkingDirectory = cfg.application.working_directory;
};
preStart = let
toTOML = nix-std.lib.serde.toTOML;
configFile = pkgs.writeText "config.toml" ''
${toTOML {inherit (cfg) application server database cache security oauth2 mailer cron repository misc;}}
'';
in ''
ln -sf ${configFile} ${cfg.application.working_directory}/config.toml
'';
};
};
};
};
nixosConfigurations.materia = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.materia
({
pkgs,
config,
...
}: {
boot.isContainer = true;
networking.hostName = "materia";
networking.useDHCP = false;
services.redis.servers.materia = {
enable = true;
port = 6379;
databases = 1;
};
services.postgresql = {
enable = true;
enableTCPIP = true;
authentication = ''
host materia all 127.0.0.1/32 trust
'';
initialScript = pkgs.writeText "init" ''
CREATE ROLE materia WITH LOGIN PASSWORD 'test';
CREATE DATABASE materia OWNER materia;
'';
ensureDatabases = ["materia"];
};
services.materia = {
enable = true;
cache.port = config.services.redis.servers.materia.port;
database.password = "test";
};
system.stateVersion = "24.05";
})
];
};
};
}