feat: HedgeDoc module(#168)

Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-config/pulls/168
Reviewed-by: Inex Code <inex.code@selfprivacy.org>
Co-authored-by: Thary <thary@riseup.net>
Co-committed-by: Thary <thary@riseup.net>
This commit is contained in:
2025-09-05 15:33:30 +03:00
committed by Inex Code
parent 73cbdf994e
commit 45c96d3472
5 changed files with 255 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
[
[ "selfprivacy", "domain" ],
[ "selfprivacy", "modules", "auth", "enable" ],
[ "selfprivacy", "modules", "hedgedoc" ],
[ "selfprivacy", "passthru", "auth", "mkOAuth2ClientSecretFP" ],
[ "selfprivacy", "passthru", "auth", "oauth2-discovery-url" ],
[ "selfprivacy", "passthru", "auth", "oauth2-provider-name" ],
[ "selfprivacy", "sso", "enable" ],
[ "selfprivacy", "useBinds" ],
[ "services", "kanidm", "serverSettings", "origin" ]
]

View File

@@ -0,0 +1,39 @@
{
description = "HedgeDoc module";
outputs = { ... }:
{
nixosModules.default = import ./module.nix;
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
meta =
{ lib, ... }:
{
spModuleSchemaVersion = 1;
id = "hedgedoc";
name = "HedgeDoc";
description = "HedgeDoc is an open-source, web-based, self-hosted, collaborative markdown editor.";
svgIcon = builtins.readFile ./icon.svg;
isMovable = true;
isRequired = false;
backupDescription = "Notes and attachments.";
systemdServices = [
"hedgedoc.service"
];
folders = [
"/var/lib/hedgedoc"
];
postgreDatabases = [
"hedgedoc"
];
license = [
lib.licenses.agpl3Only
];
homepage = "https://hedgedoc.org";
sourcePage = "https://github.com/hedgedoc/hedgedoc";
supportLevel = "normal";
sso = {
userGroup = "sp.hedgedoc.users";
};
};
};
}

View File

@@ -0,0 +1,13 @@
diff --git a/lib/web/auth/oauth2/index.js b/lib/web/auth/oauth2/index.js
index b0ffa5e8a..01e16e152 100644
--- a/lib/web/auth/oauth2/index.js
+++ b/lib/web/auth/oauth2/index.js
@@ -134,7 +134,7 @@ passport.use(new OAuth2CustomStrategy({
authorizationURL: config.oauth2.authorizationURL,
tokenURL: config.oauth2.tokenURL,
clientID: config.oauth2.clientID,
- clientSecret: config.oauth2.clientSecret,
+ clientSecret: require("fs").readFileSync(config.oauth2.clientSecret, 'utf8').split("\n")[0],
callbackURL: config.serverURL + '/auth/oauth2/callback',
userProfileURL: config.oauth2.userProfileURL,
scope: config.oauth2.scope,

View File

@@ -0,0 +1,6 @@
<svg width="1487" height="1486" viewBox="0 0 1487 1486" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M819.042 1411.03C818.967 1410.95 818.893 1410.88 818.819 1410.8C799.582 1391.61 773.078 1379.75 743.828 1379.72C743.791 1379.72 743.754 1379.72 743.717 1379.72C714.466 1379.72 687.972 1391.54 668.716 1410.7C668.606 1410.81 668.497 1410.92 668.388 1411.03C668.696 1452.37 702.296 1485.79 743.713 1485.79C785.129 1485.79 818.733 1452.36 819.042 1411.03Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M947.681 955.658C947.681 987.458 921.911 1013.23 890.111 1013.23C858.321 1013.23 832.544 987.458 832.544 955.658C832.544 923.858 858.321 898.088 890.111 898.088C921.911 898.088 947.681 923.858 947.681 955.658ZM911.981 962.968C920.781 962.968 927.931 955.848 927.931 947.028C927.931 938.218 920.781 931.068 911.981 931.068C903.171 931.068 896.021 938.218 896.021 947.028C896.021 955.848 903.171 962.968 911.981 962.968Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1411.69 818.521L1486.94 743.263L1411.69 668.017L1468.3 577.921L1378.2 521.305L1413.35 420.859L1312.91 385.717L1324.82 279.971L1219.08 268.059L1207.16 162.309L1101.42 174.221L1066.27 73.776L965.841 108.921L909.211 18.821L819.104 75.438L743.862 0.192017L668.62 75.442L578.516 18.83L521.904 108.926L421.462 73.776L386.32 174.217L280.57 162.301L268.658 268.042L162.916 279.955L174.833 385.696L74.3788 420.846L109.524 521.284L19.4238 577.896L76.0448 668.013L0.786743 743.263L76.0367 818.521L19.4238 908.628L109.524 965.238L74.3788 1065.68L174.829 1100.83L162.916 1206.56L268.658 1218.48L280.57 1324.22L386.32 1312.31L421.462 1412.74L521.899 1377.61L578.516 1467.71L668.612 1411.09L724.711 1466.67L743.864 1485.81L760.707 1468.95L819.112 1411.11L909.211 1467.72L965.821 1377.61L1066.27 1412.76L1101.41 1312.32L1207.16 1324.23L1219.07 1218.48L1324.82 1206.57L1312.9 1100.83L1413.35 1065.68L1378.2 965.228L1468.3 908.618L1411.69 818.521ZM688.748 687.306L743.61 742.168L810.706 675.068L810.739 675.102C858.311 627.989 923.751 598.877 995.991 598.877C1141.43 598.877 1259.33 716.781 1259.33 862.218C1259.33 942.318 1219.96 1012.55 1167.08 1062.32L818.819 1410.8C799.582 1391.61 773.078 1379.75 743.828 1379.72H743.717C714.466 1379.72 687.972 1391.54 668.716 1410.7L311.085 1053.26C259.594 1005.2 227.364 936.768 227.364 860.768C227.364 715.331 345.264 597.427 490.706 597.427C569.677 597.427 640.481 632.239 688.748 687.306ZM655.738 955.658C655.738 987.458 629.971 1013.23 598.167 1013.23C566.38 1013.23 540.609 987.458 540.609 955.658C540.609 923.858 566.38 898.088 598.167 898.088C629.971 898.088 655.738 923.858 655.738 955.658ZM620.043 962.968C628.847 962.968 635.993 955.848 635.993 947.028C635.993 938.218 628.847 931.068 620.043 931.068C611.235 931.068 604.085 938.218 604.085 947.028C604.085 955.848 611.235 962.968 620.043 962.968Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,186 @@
{
config,
lib,
pkgs,
...
}:
let
sp = config.selfprivacy;
cfg = sp.modules.hedgedoc;
oauthClientID = "hedgedoc";
auth-passthru = config.selfprivacy.passthru.auth;
oauth2-provider-origin = config.services.kanidm.serverSettings.origin;
usersGroup = "sp.hedgedoc.users";
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP oauthClientID;
in
{
options.selfprivacy.modules.hedgedoc = {
enable =
(lib.mkOption {
default = false;
type = lib.types.bool;
description = "Enable HedgeDoc";
})
// {
meta = {
type = "enable";
};
};
location =
(lib.mkOption {
type = lib.types.str;
description = "HedgeDoc location";
})
// {
meta = {
type = "location";
};
};
subdomain =
(lib.mkOption {
default = "hedgedoc";
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
description = "Subdomain";
})
// {
meta = {
widget = "subdomain";
type = "string";
regex = "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
weight = 0;
};
};
allowAnonymous =
(lib.mkOption {
default = false;
type = lib.types.bool;
description = "Allow usage (e.g., notes creation) without an account";
})
// {
meta = {
type = "bool";
weight = 1;
};
};
allowLibravatar =
(lib.mkOption {
default = false;
type = lib.types.bool;
description = "Use Libravatar as profile picture source";
})
// {
meta = {
type = "bool";
weight = 2;
};
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = sp.sso.enable;
message = "HedgeDoc cannot be enabled when SSO is disabled.";
}
];
fileSystems = lib.mkIf sp.useBinds {
"/var/lib/hedgedoc" = {
device = "/volumes/${cfg.location}/hedgedoc";
options = [ "bind" ];
};
};
services.postgresql = {
ensureDatabases = [ "hedgedoc" ];
ensureUsers = [
{
name = "hedgedoc";
ensureDBOwnership = true;
}
];
};
services.hedgedoc = {
enable = true;
package = pkgs.hedgedoc.overrideAttrs (old: {
doCheck = true;
patches = (old.patches or [ ]) ++ [
./hedgedocClientSecretAsFile.patch
];
});
settings = {
port = 3001;
protocolUseSSL = true;
useSSL = false;
domain = "${cfg.subdomain}.${sp.domain}";
host = "127.0.0.1";
# HedgeDoc saves the old option name for compatibility, even though it fetches avatars from Libravatar
allowGravatar = cfg.allowLibravatar;
db = {
username = "hedgedoc";
database = "hedgedoc";
host = "/run/postgresql";
dialect = "postgresql";
};
uploadsPath = "/var/lib/hedgedoc/uploads";
allowEmailRegister = false;
email = false;
allowAnonymous = cfg.allowAnonymous;
oauth2 = {
baseURL = oauth2-provider-origin;
clientID = "hedgedoc";
clientSecret = oauthClientSecretFP;
authorizationURL = "${oauth2-provider-origin}/ui/oauth2";
tokenURL = "${oauth2-provider-origin}/oauth2/token";
userProfileURL = "${oauth2-provider-origin}/oauth2/openid/${oauthClientID}/userinfo";
scope = "openid email profile";
userProfileUsernameAttr = "sub";
userProfileDisplayNameAttr = "name";
userProfileEmailAttr = "email";
};
loglevel = "error";
};
};
services.nginx.virtualHosts."${cfg.subdomain}.${sp.domain}" = {
useACMEHost = sp.domain;
forceSSL = true;
locations = {
"/" = {
proxyPass = "http://127.0.0.1:3001";
proxyWebsockets = true;
};
};
};
systemd = {
services.hedgedoc = {
unitConfig.RequiresMountsFor = lib.mkIf sp.useBinds "/volumes/${cfg.location}/hedgedoc";
serviceConfig.Slice = "hedgedoc.slice";
};
slices.hedgedoc = {
description = "HedgeDoc service slice";
};
};
selfprivacy.auth.clients.${oauthClientID} = {
inherit usersGroup;
subdomain = cfg.subdomain;
originLanding = "https://${cfg.subdomain}.${sp.domain}/";
originUrl = "https://${cfg.subdomain}.${sp.domain}/auth/oauth2/callback";
clientSystemdUnits = [ "hedgedoc.service" ];
enablePkce = false;
linuxUserOfClient = "hedgedoc";
linuxGroupOfClient = "hedgedoc";
};
};
}