diff --git a/trivionomicon/doctrine/lib/mk-module.nix b/trivionomicon/doctrine/lib/mk-module.nix index ffbe6bc..7a1de39 100644 --- a/trivionomicon/doctrine/lib/mk-module.nix +++ b/trivionomicon/doctrine/lib/mk-module.nix @@ -13,39 +13,56 @@ namespace ? doctrine.namespace, passthru ? {}, }: let - optionsSet = import options (passthru - // { - inherit config lib pkgs cfg name doctrine; - }); + patchArgs = args: args // passthru // {inherit cfg name doctrine;}; - configSet = import configFiles.${namespace} (passthru - // { - inherit config lib pkgs doctrine cfg; - }); + optionsSet = args: import options (patchArgs args); + configSet = args: import configFiles.${namespace} (patchArgs args); configFiles = lib.filterAttrs (k: v: v != null) { inherit sys hm; }; cfg = config.${prefix}.${name}; + hasConfig = configFiles ? ${namespace}; in { - config = - lib.optionalAttrs (configFiles ? ${namespace}) - (lib.mkIf cfg.enable (lib.mkMerge [ - configSet - { - assertions = - map (dependency: { - assertion = cfg.enable -> config.${prefix}.${dependency}.enable; - message = "${prefix}.${name}.enable requires ${prefix}.${dependency}.enable"; - }) - requires; - } - ])); + imports = [ + (args @ { + config, + modulesPath ? null, + lib, + pkgs, + ... + }: + lib.optionalAttrs hasConfig { + config = lib.mkIf cfg.enable (lib.mkMerge [ + (configSet args) + { + assertions = + map (dependency: { + assertion = cfg.enable -> config.${prefix}.${dependency}.enable; + message = "${prefix}.${name}.enable requires ${prefix}.${dependency}.enable"; + }) + requires; + } + ]); + }) - options.${prefix}.${name} = - lib.optionalAttrs (options != null && optionsSet ? ${namespace}) optionsSet.${namespace} - // { - enable = lib.mkEnableOption name; - }; + (args @ { + config, + modulesPath ? null, + lib, + pkgs, + ... + }: let + hasOptions = options != null && optionsForArgs ? ${namespace}; + optionsForArgs = optionsSet args; + in + lib.optionalAttrs (hasOptions || hasConfig) { + options.${prefix}.${name} = + lib.optionalAttrs hasOptions optionsForArgs.${namespace} + // { + enable = lib.mkEnableOption name; + }; + }) + ]; } diff --git a/trivionomicon/modules/socialpredict/default.nix b/trivionomicon/modules/socialpredict/default.nix new file mode 100644 index 0000000..f821bf4 --- /dev/null +++ b/trivionomicon/modules/socialpredict/default.nix @@ -0,0 +1,11 @@ +{ + config, + doctrine, + ... +}: +doctrine.lib.mkModule { + inherit config; + name = "socialpredict"; + options = ./options.nix; + sys = ./sys.nix; +} diff --git a/trivionomicon/modules/socialpredict/options.nix b/trivionomicon/modules/socialpredict/options.nix new file mode 100644 index 0000000..bb2ad5e --- /dev/null +++ b/trivionomicon/modules/socialpredict/options.nix @@ -0,0 +1,78 @@ +{ + config, + doctrine, + lib, + modulesPath, + pkgs, + ... +}: +with lib.types; let + inherit (pkgs.${doctrine.prefix}) socialpredict; +in { + sys = { + frontend = lib.mkOption { + type = package; + default = socialpredict.frontend; + defaultText = "pkgs.\${doctrine.prefix}.frontend"; + description = "socialpredict frontend package"; + }; + + backend = lib.mkOption { + type = package; + default = socialpredict.backend; + defaultText = "pkgs.\${doctrine.prefix}.backend"; + description = "socialpredict backend package"; + }; + + package = lib.mkOption { + type = package; + default = pkgs.${doctrine.prefix}.socialpredict; + defaultText = "pkgs.\${doctrine.prefix}.socialpredict"; + description = "socialpredict package"; + }; + + database = lib.mkOption { + type = str; + default = "socialpredict"; + description = "database name"; + }; + + user = lib.mkOption { + type = str; + default = "socialpredict"; + description = "user that will run the backend"; + }; + + group = lib.mkOption { + type = str; + default = "socialpredict"; + description = "group that will run the backend"; + }; + + backendPort = lib.mkOption { + type = port; + description = "backend port"; + }; + + initialAdminPassword = lib.mkOption { + type = str; + default = "change-me"; + description = "initial password of the 'admin' user"; + }; + + domain = lib.mkOption { + type = nullOr str; + default = null; + description = "domain host"; + }; + + nginx = lib.mkOption { + type = submodule ( + lib.recursiveUpdate (import "${modulesPath}/services/web-servers/nginx/vhost-options.nix" {inherit config lib;}) {} + ); + + default = {}; + description = "extra nginx virtual host config"; + }; + }; +} diff --git a/trivionomicon/modules/socialpredict/sys.nix b/trivionomicon/modules/socialpredict/sys.nix new file mode 100644 index 0000000..36e5272 --- /dev/null +++ b/trivionomicon/modules/socialpredict/sys.nix @@ -0,0 +1,102 @@ +{ + cfg, + doctrine, + lib, + pkgs, + ... +}: { + services = { + nginx = lib.mkIf (cfg.domain != null) { + enable = true; + + virtualHosts.${cfg.domain} = lib.mkMerge [ + cfg.nginx + { + locations = { + "/" = { + root = "${cfg.frontend}"; + index = "index.html"; + tryFiles = "$uri $uri/ /index.html =404"; + }; + + "/api/" = { + proxyPass = "http://localhost:${toString cfg.backendPort}/"; + }; + + "= /env-config.js" = { + alias = "${pkgs.writeText "socialpredict-env-config.js" '' + window.__ENV__ = { + DOMAIN_URL: "https://${cfg.domain}", + API_URL: "https://${cfg.domain}/api" + }; + ''}"; + }; + }; + } + ]; + }; + + postgresql = { + enable = true; + + ensureUsers = [ + { + name = cfg.user; + ensureDBOwnership = cfg.user == cfg.database; + } + ]; + + ensureDatabases = [cfg.database]; + }; + }; + + systemd.services.socialpredict = { + after = ["postgresql.service"]; + wants = ["postgresql.service"]; + wantedBy = ["multi-user.target"]; + + environment = { + ADMIN_PASSWORD = cfg.initialAdminPassword; + BACKEND_PORT = toString cfg.backendPort; + POSTGRES_URL = "postgresql:///${cfg.database}?host=/var/run/postgresql"; + }; + + serviceConfig = { + Group = cfg.group; + User = cfg.user; + + ExecStart = lib.getExe cfg.backend; + + KeyringMode = "private"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateMounts = "yes"; + PrivateTmp = "yes"; + ProtectControlGroups = true; + ProtectHome = "yes"; + ProtectHostname = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + + ReadWritePaths = [ + "/var/run/postgresql" + ]; + }; + }; + + users = { + groups.${cfg.group} = {}; + users.${cfg.user} = { + inherit (cfg) group; + isSystemUser = true; + }; + }; +} diff --git a/trivionomicon/pkgs/athena-bccr/firmador.nix b/trivionomicon/pkgs/athena-bccr/firmador.nix index d280b56..8ae8750 100644 --- a/trivionomicon/pkgs/athena-bccr/firmador.nix +++ b/trivionomicon/pkgs/athena-bccr/firmador.nix @@ -4,7 +4,7 @@ makeWrapper, maven, openjdk, - wrapGAppsHook, + wrapGAppsHook3, libasep11 ? null, }: let jdk = openjdk.override { @@ -27,11 +27,11 @@ in ./0001-Remove-CheckUpdatePlugin-from-default-list.patch ]; - mvnHash = "sha256-h1zoStTgaE7toWWKq0Y0ahOORyltChwjmaMYjLgs1VE="; + mvnHash = "sha256-m3UaOLNyIlVAOI5tzxMlxg4KZ1N5gT2O2WSka+jBat4="; nativeBuildInputs = [ makeWrapper - wrapGAppsHook + wrapGAppsHook3 ]; postPatch = lib.optionalString (libasep11 != null) '' diff --git a/trivionomicon/pkgs/default.nix b/trivionomicon/pkgs/default.nix index 4a275a3..1b11af9 100644 --- a/trivionomicon/pkgs/default.nix +++ b/trivionomicon/pkgs/default.nix @@ -6,5 +6,6 @@ in { athena-bccr = callPackage ./athena-bccr {}; snapborg = final.python3Packages.callPackage ./snapborg {}; + socialpredict = callPackage ./socialpredict {}; spliit = callPackage ./spliit {}; } diff --git a/trivionomicon/pkgs/socialpredict/0001-Support-POSTGRES_URL-for-overriding-the-PG-DSN.patch b/trivionomicon/pkgs/socialpredict/0001-Support-POSTGRES_URL-for-overriding-the-PG-DSN.patch new file mode 100644 index 0000000..69dc660 --- /dev/null +++ b/trivionomicon/pkgs/socialpredict/0001-Support-POSTGRES_URL-for-overriding-the-PG-DSN.patch @@ -0,0 +1,30 @@ +From 67cf25f7cb397d37d807797b6013447b19c8f73b Mon Sep 17 00:00:00 2001 +From: Alejandro Soto +Date: Thu, 29 Jan 2026 23:21:21 -0600 +Subject: [PATCH] Support POSTGRES_URL for overriding the PG DSN + +--- + backend/util/postgres.go | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/util/postgres.go b/util/postgres.go +index ac69932..030f516 100644 +--- a/util/postgres.go ++++ b/util/postgres.go +@@ -49,8 +49,11 @@ func InitDB() { + dbPort = "5432" + } + +- dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=UTC", +- dbHost, dbUser, dbPassword, dbName, dbPort) ++ dsn := os.Getenv("POSTGRES_URL") ++ if dsn == "" { ++ dsn = fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=UTC", ++ dbHost, dbUser, dbPassword, dbName, dbPort) ++ } + + DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { +-- +2.51.2 + diff --git a/trivionomicon/pkgs/socialpredict/0002-Fix-API_URL-api-v0-.-API_URL-v0.patch b/trivionomicon/pkgs/socialpredict/0002-Fix-API_URL-api-v0-.-API_URL-v0.patch new file mode 100644 index 0000000..09b0d70 --- /dev/null +++ b/trivionomicon/pkgs/socialpredict/0002-Fix-API_URL-api-v0-.-API_URL-v0.patch @@ -0,0 +1,47 @@ +From 9ce9d4d3bdbd12dfb3dda98f3d4f72d3bbe724b7 Mon Sep 17 00:00:00 2001 +From: Alejandro Soto +Date: Sat, 31 Jan 2026 13:04:28 -0600 +Subject: [PATCH 2/2] Fix ${API_URL}/api/v0/... -> ${API_URL}/v0/... + +--- + src/hooks/usePortfolio.jsx | 2 +- + src/hooks/useUserData.jsx | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/hooks/usePortfolio.jsx b/src/hooks/usePortfolio.jsx +index 0f0774b..a233ea3 100644 +--- a/src/hooks/usePortfolio.jsx ++++ b/src/hooks/usePortfolio.jsx +@@ -17,7 +17,7 @@ const usePortfolio = (username) => { + headers['Content-Type'] = 'application/json'; + } + +- const response = await fetch(`${API_URL}/api/v0/portfolio/${username}`, { headers }); ++ const response = await fetch(`${API_URL}/v0/portfolio/${username}`, { headers }); + if (!response.ok) { + throw new Error('Failed to fetch portfolio'); + } +diff --git a/src/hooks/useUserData.jsx b/src/hooks/useUserData.jsx +index 593594f..960c5aa 100644 +--- a/src/hooks/useUserData.jsx ++++ b/src/hooks/useUserData.jsx +@@ -15,14 +15,14 @@ const useUserData = (username, usePrivateProfile = false) => { + + if (usePrivateProfile) { + // Use private profile endpoint for authenticated user's own profile +- url = `${API_URL}/api/v0/privateprofile`; ++ url = `${API_URL}/v0/privateprofile`; + headers = { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }; + } else { + // Use public user endpoint for viewing other users' profiles +- url = `${API_URL}/api/v0/userinfo/${username}`; ++ url = `${API_URL}/v0/userinfo/${username}`; + if (token) { + headers = { + 'Authorization': `Bearer ${token}`, +-- +2.51.2 + diff --git a/trivionomicon/pkgs/socialpredict/default.nix b/trivionomicon/pkgs/socialpredict/default.nix new file mode 100644 index 0000000..22dc6a9 --- /dev/null +++ b/trivionomicon/pkgs/socialpredict/default.nix @@ -0,0 +1,66 @@ +{ + buildGoModule, + buildNpmPackage, + fetchFromGitHub, + lib, +}: let + version = "2.1.0"; + + src = fetchFromGitHub { + owner = "openpredictionmarkets"; + repo = "socialpredict"; + tag = "v${version}"; + hash = "sha256-aV6Z7vsqV8zxyB+v7hSyOm/jzGqR8YnhG+xLKSC9Qoo="; + }; + + meta = { + description = "Easy to Deploy Prediction Market Platform "; + homepage = "https://github.com/openpredictionmarkets/socialpredict"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; []; # [ _3442 ]; + }; +in { + backend = buildGoModule { + pname = "socialpredict-backend"; + inherit src version; + + patches = [ + ./0001-Support-POSTGRES_URL-for-overriding-the-PG-DSN.patch + ]; + + sourceRoot = "source/backend"; + vendorHash = "sha256-ah2d+gHe7HULEsqMSUwGOL4D00aY0QtZvcD3pTQp/Q0="; + + meta = meta // {mainProgram = "socialpredict";}; + }; + + frontend = buildNpmPackage { + pname = "socialpredict-frontend"; + inherit src meta version; + + sourceRoot = "source/frontend"; + npmDepsHash = "sha256-zn1yPtvi8DaKESMGAtqnh/66xET+QaCa1TUlpbatI70="; + + patches = [ + ./0002-Fix-API_URL-api-v0-.-API_URL-v0.patch + ]; + + buildPhase = '' + runHook preBuild + + node --max_old_space_size=1024000 ./node_modules/vite/bin/vite.js build + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out + cp -r build/* $out + rm $out/env-config.js.template + + runHook postInstall + ''; + }; +}