summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doctrine/lib/mk-module.nix69
-rw-r--r--modules/socialpredict/default.nix11
-rw-r--r--modules/socialpredict/options.nix78
-rw-r--r--modules/socialpredict/sys.nix102
-rw-r--r--pkgs/athena-bccr/firmador.nix6
-rw-r--r--pkgs/default.nix1
-rw-r--r--pkgs/socialpredict/0001-Support-POSTGRES_URL-for-overriding-the-PG-DSN.patch30
-rw-r--r--pkgs/socialpredict/0002-Fix-API_URL-api-v0-.-API_URL-v0.patch47
-rw-r--r--pkgs/socialpredict/default.nix66
9 files changed, 381 insertions, 29 deletions
diff --git a/doctrine/lib/mk-module.nix b/doctrine/lib/mk-module.nix
index ffbe6bc..7a1de39 100644
--- a/doctrine/lib/mk-module.nix
+++ b/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/modules/socialpredict/default.nix b/modules/socialpredict/default.nix
new file mode 100644
index 0000000..f821bf4
--- /dev/null
+++ b/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/modules/socialpredict/options.nix b/modules/socialpredict/options.nix
new file mode 100644
index 0000000..bb2ad5e
--- /dev/null
+++ b/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/modules/socialpredict/sys.nix b/modules/socialpredict/sys.nix
new file mode 100644
index 0000000..36e5272
--- /dev/null
+++ b/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/pkgs/athena-bccr/firmador.nix b/pkgs/athena-bccr/firmador.nix
index d280b56..8ae8750 100644
--- a/pkgs/athena-bccr/firmador.nix
+++ b/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/pkgs/default.nix b/pkgs/default.nix
index 4a275a3..1b11af9 100644
--- a/pkgs/default.nix
+++ b/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/pkgs/socialpredict/0001-Support-POSTGRES_URL-for-overriding-the-PG-DSN.patch b/pkgs/socialpredict/0001-Support-POSTGRES_URL-for-overriding-the-PG-DSN.patch
new file mode 100644
index 0000000..69dc660
--- /dev/null
+++ b/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 <alejandro@34project.org>
+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/pkgs/socialpredict/0002-Fix-API_URL-api-v0-.-API_URL-v0.patch b/pkgs/socialpredict/0002-Fix-API_URL-api-v0-.-API_URL-v0.patch
new file mode 100644
index 0000000..09b0d70
--- /dev/null
+++ b/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 <alejandro@34project.org>
+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/pkgs/socialpredict/default.nix b/pkgs/socialpredict/default.nix
new file mode 100644
index 0000000..22dc6a9
--- /dev/null
+++ b/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
+ '';
+ };
+}