Add ryot and moonrepo

main
Carsten Kragelund 2023-06-04 23:05:10 +02:00
parent 098ca9feff
commit fc7f47c56c
Signed by: nyx
GPG Key ID: CADDADEEC9F753C0
10 changed files with 497 additions and 21 deletions

@ -1,8 +1,32 @@
{
"nodes": {
"emacs-overlay": {
"crane": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs-unstable"
],
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1676846788,
"narHash": "sha256-XLsHLgXyMdliMeAXuzdGP+TXBaV44kG1RPTUHNOs6Jk=",
"owner": "ipetkov",
"repo": "crane",
"rev": "953b70da2813fb882c39890f2514e7db76fc8843",
"type": "github"
},
"original": {
"owner": "ipetkov",
"ref": "v0.11.3",
"repo": "crane",
"type": "github"
}
},
"emacs-overlay": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
]
@ -37,7 +61,38 @@
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1676283394,
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
@ -52,7 +107,7 @@
"type": "github"
}
},
"flake-utils_2": {
"flake-utils_3": {
"inputs": {
"systems": "systems"
},
@ -114,8 +169,8 @@
},
"nixos-wsl": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils_2",
"flake-compat": "flake-compat_2",
"flake-utils": "flake-utils_3",
"nixpkgs": [
"nixpkgs"
]
@ -182,6 +237,7 @@
},
"root": {
"inputs": {
"crane": "crane",
"emacs-overlay": "emacs-overlay",
"home-manager": "home-manager",
"nekowinston-nur": "nekowinston-nur",
@ -193,6 +249,31 @@
"sops-nix": "sops-nix"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": [
"crane",
"flake-utils"
],
"nixpkgs": [
"crane",
"nixpkgs"
]
},
"locked": {
"lastModified": 1676437770,
"narHash": "sha256-mhJye91Bn0jJIE7NnEywGty/U5qdELfsT8S+FBjTdG4=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "a619538647bd03e3ee1d7b947f7c11ff289b376e",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"sanctureplicum-nur": {
"inputs": {
"nixpkgs": [

@ -26,6 +26,10 @@
url = "github:nix-community/nixos-wsl";
inputs.nixpkgs.follows = "nixpkgs";
};
crane = {
url = "github:ipetkov/crane/v0.11.3";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
emacs-overlay = {
url = "github:nix-community/emacs-overlay";
inputs.nixpkgs.follows = "nixpkgs";
@ -42,6 +46,7 @@
emacs-overlay,
nixos-wsl,
sops-nix,
crane,
home-manager,
...
} @ inputs: let
@ -137,6 +142,7 @@
};
nixosConfigurations.gitea = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {craneLib = crane.lib."x86_64-linux";};
modules = [
({
config,

@ -1,14 +1,20 @@
{ config
, modulesPath
, pkgs
, ...
{
config,
modulesPath,
pkgs,
...
}: {
imports = [
./hardware.nix
(import ../common/network.nix { hostName = "gitea"; macAddresses = [ "00:50:50:00:00:01" ]; ipv4Addresses = [ "192.168.1.7" ]; })
(import ../common/network.nix {
hostName = "gitea";
macAddresses = ["00:50:50:00:00:01"];
ipv4Addresses = ["192.168.1.7"];
})
../common
./services
./modules
];
# networking = builtins.trace ((pkgs.callPackage ../common/network.nix {}) { hostName = "gitea"; macAddresses = [ "00:50:50:00:00:01" ]; ipv4Addresses = [ "192.168.1.7" ]; }) {};

@ -0,0 +1 @@
{...}: {imports = [./ryot.nix];}

@ -0,0 +1,368 @@
{
config,
lib,
options,
pkgs,
craneLib,
...
}:
with lib;
with builtins; let
pgHost = {
host,
socket,
name,
port,
user,
password,
...
}: "postgres://${user}${
if password != ""
then ":${password}"
else ""
}@${
if socket == null
then "${host}:${port}"
else replaceStrings ["/"] ["%%2F"] socket
}/${name}";
cfg = config.services.ryot;
pg = config.services.postgresql;
in {
options.services.ryot = {
enable = mkEnableOption "ryot";
package = mkOption {
default = pkgs.unstable.callPackage ../pkgs/ryot {inherit craneLib;};
type = types.package;
defaultText = literalExpression "pkgs.callPackage ../pkgs/ryot {}";
description = mdDoc "ryot derivation to use";
};
database = {
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = mdDoc "Database host address.";
};
port = mkOption {
type = types.port;
default = pg.port or 5432;
defaultText = literalExpression "config.postgresql.port or 5432";
description = mdDoc "Database host port.";
};
socket = mkOption {
type = types.nullOr types.path;
default = null;
example = "/var/run/postgresql";
description = mdDoc "Path to the unix socket file to use for authentication.";
};
name = mkOption {
type = types.str;
default = "ryot";
description = mdDoc "Database name.";
};
user = mkOption {
type = types.str;
default = "ryot";
description = mdDoc "Database user.";
};
password = mkOption {
type = types.str;
default = "";
description = mdDoc ''
The password corresponding to {option}`database.user`.
Warning: this is stored in cleartext in the Nix store!
'';
};
createDatabase = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc "Whether to create a local database automatically.";
};
};
user = mkOption {
type = types.str;
default = "ryot";
description = lib.mdDoc "User account under which ryot runs.";
};
group = mkOption {
type = types.str;
default = "ryot";
description = lib.mdDoc "Group under which ryot runs.";
};
port = mkOption {
type = types.port;
default = 8000;
description = mdDoc "The port the web interface should listen on";
};
https = mkOption {
type = types.bool;
default = false;
description = mdDoc "Whether the site is served over https";
};
cors_urls = mkOption {
type = types.listOf types.str;
default = [];
description = mdDoc ''A list of URLs for CORS.'';
};
settings = {
audio_books = {
audible = {
url = mkOption {
type = types.str;
default = "https://api.audible.com/1.0/catalog/products/";
description = mdDoc "The url to make requests for getting metadata from Audible.";
};
};
};
books = {
openlibrary = {
url = mkOption {
type = types.str;
default = "https://openlibrary.org";
description = mdDoc "The url to make requests for getting metadata from Openlibrary.";
};
cover_image_url = mkOption {
type = types.str;
default = "https://covers.openlibrary.org/b";
description = mdDoc "The url for getting images from Openlibrary.";
};
cover_image_size = mkOption {
type = types.enum ["S" "M" "L"];
default = "M";
description = mdDoc "The image sizes to fetch from Openlibrary.";
};
};
};
movies = {
tmdb = {
url = mkOption {
type = types.str;
default = "https://api.themoviedb.org/3/";
description = mdDoc "The url to make requests for getting metadata about shows/movies.";
};
access_token = mkOption {
type = types.str;
default = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI4ZGVlOTZjMjc0OGVhY2U0NzU2MGJkMWU4YzE5NTljMCIsInN1YiI6IjY0NDRiYmE4MmM2YjdiMDRiZTdlZDJmNSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.ZZZNJMXStvAOPJlT0hOBVPSTppFAK3mcUpmbJsExIq4";
description = mdDoc "The access token for the TMDB API.";
};
};
};
shows = {
tmdb = {
url = mkOption {
type = types.str;
default = "https://api.themoviedb.org/3/";
description = mdDoc "The url to make requests for getting metadata about shows/movies.";
};
access_token = mkOption {
type = types.str;
default = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI4ZGVlOTZjMjc0OGVhY2U0NzU2MGJkMWU4YzE5NTljMCIsInN1YiI6IjY0NDRiYmE4MmM2YjdiMDRiZTdlZDJmNSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.ZZZNJMXStvAOPJlT0hOBVPSTppFAK3mcUpmbJsExIq4";
description = mdDoc "The access token for the TMDB API.";
};
};
};
podcasts = {
listennotes = {
url = mkOption {
type = types.str;
default = "https://listen-api.listennotes.com/api/v2/";
description = mdDoc "The url to make requests for getting metadata about podcasts.";
};
api_token = mkOption {
type = types.str;
default = "";
description = mdDoc "The access token for the Listennotes API.";
};
user_agent = mkOption {
type = types.str;
default = "";
description = mdDoc "The user agent used for the Listennotes API.";
};
};
};
scheduler = {
user_cleanup_every = mkOption {
type = types.int;
default = 10;
description = mdDoc "Deploy a job every x minutes that performs user cleanup and summary calculation.";
};
rate_limit_num = mkOption {
type = types.int;
default = 5;
description = mdDoc "The number of jobs to process every 5 seconds when updating metadata in the background.";
};
};
video_games = {
twitch = {
client_id = mkOption {
type = types.str;
default = "";
description = mdDoc "The client ID issues by Twitch. Required to enable video games tracking. [More information](https://github.com/IgnisDa/ryot/blob/main/docs/guides/video-games.md)";
};
client_secret = mkOption {
type = types.str;
default = "";
description = mdDoc "The client secret issued by Twitch.";
};
access_token_url = mkOption {
type = types.str;
default = "https://id.twitch.tv/oauth2/token";
description = mdDoc "The endpoint that issues access keys for IGDB.";
};
};
igdb = {
url = mkOption {
type = types.str;
default = "https://api.igdb.com/v4/";
description = mdDoc "The url to make requests for getting metadata about video games.";
};
image_url = mkOption {
type = types.str;
default = "https://images.igdb.com/igdb/image/upload/";
description = mdDoc "The url for getting images from IGDB.";
};
image_size = mkOption {
type = types.enum ["t_original"];
default = "t_original";
description = mdDoc "The image sizes to fetch from IGDB.";
};
};
};
users = {
allow_changing_username = mkOption {
type = types.bool;
default = true;
description = mdDoc "Whether users will be allowed to change their username in their profile settings.";
};
token_valid_for_days = mkOption {
type = types.int;
default = 90;
description = mdDoc "The number of days till login auth token is valid.";
};
};
};
};
config = mkIf cfg.enable {
systemd.services.ryot = rec {
description = "ryot";
after = ["network.target" "postgresql.service"];
requires = ["postgresql.service"];
wantedBy = ["multi-user.target"];
path = [cfg.package];
preStart = ''mkdir -p /usr/share/ryot && chown ${cfg.user}:${cfg.group} /usr/share/ryot'';
serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
ExecStart = "${getExe cfg.package}";
Restart = "always";
WorkingDirectory = "/usr/share/ryot";
ReadWritePaths = ["/usr/share/ryot"];
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
RestrictAddressFamilies = ["AF_UNIX AF_INET AF_INET6"];
LockPersonality = true;
MemoryDenyWriteExecute = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
PrivateMounts = true;
# System Call Filtering
SystemCallArchitectures = "native";
SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @setuid @swap";
PassEnvironment = concatStringsSep " " (attrNames environment);
};
environment = {
AUDIO_BOOKS_AUDIBLE_URL = cfg.settings.audio_books.audible.url;
BOOKS_OPENLIBRARY_URL = cfg.settings.books.openlibrary.url;
BOOKS_OPENLIBRARY_COVER_IMAGE_URL = cfg.settings.books.openlibrary.cover_image_url;
BOOKS_OPENLIBRARY_COVER_IMAGE_SIZE = cfg.settings.books.openlibrary.cover_image_size;
MOVIES_TMDB_URL = cfg.settings.movies.tmdb.url;
MOVIES_TMDB_ACCESS_TOKEN = cfg.settings.movies.tmdb.access_token;
SHOWS_TMDB_URL = cfg.settings.shows.tmdb.url;
SHOWS_TMDB_ACCESS_TOKEN = cfg.settings.shows.tmdb.access_token;
PODCASTS_LISTENNOTES_URL = cfg.settings.podcasts.listennotes.url;
PODCASTS_LISTENNOTES_API_TOKEN = cfg.settings.podcasts.listennotes.api_token;
PODCASTS_LISTENNOTES_USER_AGENT = cfg.settings.podcasts.listennotes.user_agent;
SCHEDULER_USER_CLEANUP_EVERY = toString cfg.settings.scheduler.user_cleanup_every;
SCHEDULER_RATE_LIMIT_NUM = toString cfg.settings.scheduler.rate_limit_num;
VIDEO_GAMES_TWITCH_CLIENT_ID = cfg.settings.video_games.twitch.client_id;
VIDEO_GAMES_TWITCH_CLIENT_SECRET = cfg.settings.video_games.twitch.client_secret;
VIDEO_GAMES_TWITCH_ACCESS_TOKEN_URL = cfg.settings.video_games.twitch.access_token_url;
VIDEO_GAMES_IGDB_URL = cfg.settings.video_games.igdb.url;
VIDEO_GAMES_IGDB_IMAGE_URL = cfg.settings.video_games.igdb.image_url;
VIDEO_GAMES_IGDB_IMAGE_SIZE = cfg.settings.video_games.igdb.image_size;
USERS_ALLOW_CHANGING_USERNAME =
if cfg.settings.users.allow_changing_username
then "true"
else null;
USERS_TOKEN_VALID_FOR_DAYS = toString cfg.settings.users.token_valid_for_days;
WEB_CORS_ORIGINS = concatStringsSep "," cfg.cors_urls;
DATABASE_SCDB_URL = "/usr/share/ryot";
DATABASE_URL = pgHost cfg.database;
WEB_INSECURE_COOKIE =
if cfg.https
then null
else "true";
PORT = toString cfg.port;
};
};
services.postgresql = optionalAttrs cfg.database.createDatabase {
enable = mkDefault true;
ensureDatabases = [cfg.database.name];
ensureUsers = [
{
name = cfg.database.user;
ensurePermissions = {"DATABASE ${cfg.database.name}" = "ALL PRIVILEGES";};
}
];
};
users.users = mkIf (cfg.user == "ryot") {
ryot = {
description = "Ryot Service";
useDefaultShell = true;
group = cfg.group;
isSystemUser = true;
};
};
users.groups = mkIf (cfg.group == "ryot") {
ryot = {};
};
};
meta.maintainers = with maintainers; [nyxkrage];
}

@ -1,8 +1,8 @@
{
pkgs ? import <unstable> {},
lib ? pkgs.lib,
fetchFromGitHub ? pkgs.fetchFromGitHub,
craneLib ? import (builtins.fetchTarball "https://github.com/ipetkov/crane/archive/refs/tags/v0.12.1.tar.gz") {inherit pkgs;},
pkgs,
lib,
fetchFromGitHub,
craneLib,
}:
craneLib.buildPackage rec {
pname = "moonrepo";

@ -1,8 +1,7 @@
{
pkgs ? import <unstable> {},
stdenv ? pkgs.stdenv,
moon ? pkgs.callPackage ../moonrepo {},
craneLib ? import (builtins.fetchTarball "https://github.com/ipetkov/crane/archive/refs/tags/v0.12.1.tar.gz") {inherit pkgs;},
pkgs,
stdenv,
craneLib,
}: let
pname = "ryot";
version = "v1.0.0-beta.52";
@ -21,7 +20,7 @@ in
inherit pname version src;
nativeBuildInputs = [
moon
(pkgs.callPackage ../moonrepo {inherit craneLib;})
pkgs.nodejs
pkgs.rustc
pkgs.cargo

@ -1,8 +1,9 @@
{ ...} : {
{...}: {
imports = [
./gitea.nix
./postgres.nix
./nginx.nix
./sshd.nix
./ryot.nix
];
}

@ -18,5 +18,5 @@
}
];
};
networking.firewall.allowedTCPPorts = [5432 8001];
networking.firewall.allowedTCPPorts = [5432];
}

@ -0,0 +1,14 @@
{
pkgs,
config,
...
}: {
services.ryot = rec {
enable = true;
database.socket = "/var/run/postgresql";
cors_urls = ["https://ryot.pid1.sh" "https://ryot.nyx" "http://192.168.1.7:8001"];
port = 8001;
https = false;
};
networking.firewall.allowedTCPPorts = [config.services.ryot.port];
}