Initial commit

This commit is contained in:
2025-11-07 15:16:45 +03:00
commit 6bf1d003fe
4 changed files with 227 additions and 0 deletions

11
config-paths-needed.json Normal file
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" ]
]

36
flake.nix Normal file
View File

@@ -0,0 +1,36 @@
{
description = "HedgeDoc module";
outputs = { ... }:
{
nixosModules.default = import ./module.nix;
configPathsNeeded = builtins.fromJSON (builtins.readFile ./config-paths-needed.json);
meta =
{ lib, ... }:
{
spModuleSchemaVersion = 1;
id = "writefreely";
name = "WriteFreely";
description = "An open source platform for building a writing space on the web.";
svgIcon = builtins.readFile ./icon.svg;
isMovable = true;
isRequired = false;
backupDescription = "Your articles and attachments.";
systemdServices = [
"writefreely.service"
];
folders = [
"/var/lib/writefreely"
];
license = [
lib.licenses.agpl3Only
];
homepage = "https://writefreely.org";
sourcePage = "https://github.com/writefreely/writefreely";
supportLevel = "normal";
sso = {
userGroup = "sp.writefreely.users";
};
};
};
}

20
icon.svg Normal file
View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="8.5162764mm" height="5.8980036mm" viewBox="0 0 30.175782 20.898438" id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="logo.svg">
<defs id="defs4"/>
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="3.959798" inkscape:cx="-36.448977" inkscape:cy="-13.073394" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:window-width="1280" inkscape:window-height="722" inkscape:window-x="0" inkscape:window-y="26" inkscape:window-maximized="1"/>
<metadata id="metadata7">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-191.62389,-331.91299)">
<g style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#292929;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="text4136">
<path d="m 220.8231,344.31534 q -0.66406,1.79687 -1.67968,3.33984 -1.01563,1.54297 -2.32422,2.69531 -1.3086,1.15235 -2.85156,1.81641 -1.52344,0.64453 -3.22266,0.64453 -0.70313,0 -1.44531,-0.21484 -0.72266,-0.21485 -1.32813,-0.76172 -0.58594,-0.56641 -0.95703,-1.50391 -0.37109,-0.95703 -0.37109,-2.40234 0,-0.52734 0.0586,-1.15234 l -0.0781,0 q -0.41016,1.17187 -1.13282,2.26562 -0.70312,1.09375 -1.62109,1.93359 -0.91797,0.83985 -1.99219,1.34766 -1.05469,0.48828 -2.14844,0.48828 -1.17187,0 -1.97265,-0.41015 -0.78125,-0.41016 -1.26953,-1.11329 -0.48828,-0.72265 -0.70313,-1.66015 -0.21484,-0.95703 -0.21484,-2.03125 0,-1.58203 0.15625,-3.30078 0.17578,-1.71875 0.37109,-3.32032 0.19531,-1.60156 0.35156,-2.94921 0.17579,-1.36719 0.17579,-2.22657 0,-0.37109 -0.0391,-0.70312 -0.0195,-0.35156 -0.13672,-0.60547 -0.11718,-0.25391 -0.33203,-0.41016 -0.21484,-0.15625 -0.58593,-0.15625 -0.33204,0 -0.70313,0.21485 -0.37109,0.19531 -0.74219,0.66406 -0.35156,0.44922 -0.70312,1.19141 -0.35156,0.74218 -0.64453,1.79687 l -1.11328,-0.23437 q 0.13671,-0.70313 0.48828,-1.66016 0.37109,-0.97656 0.97656,-1.85547 0.625,-0.89844 1.50391,-1.50391 0.89843,-0.625 2.10937,-0.625 1.58203,0 2.40234,0.9961 0.83985,0.99609 0.83985,2.89062 0,0.87891 -0.0781,1.99219 -0.0781,1.09375 -0.21484,2.28516 -0.11719,1.1914 -0.25391,2.42187 -0.13672,1.21094 -0.27343,2.32422 -0.11719,1.09375 -0.19532,1.99219 -0.0781,0.89844 -0.0781,1.46484 0,0.44922 0.0781,0.87891 0.0781,0.42969 0.27344,0.76172 0.19531,0.33203 0.52734,0.54687 0.33204,0.21485 0.83985,0.21485 1.28906,0 2.53906,-0.9375 1.25,-0.9375 2.22656,-2.71485 0.9961,-1.77734 1.60157,-4.35547 0.625,-2.59765 0.625,-5.89843 0,-1.03516 -0.0586,-2.12891 -0.0586,-1.11328 -0.21484,-2.28516 l 3.41797,0 q 0.0391,0.17578 0.0586,0.42969 0.0391,0.23438 0.0586,0.48828 0.0195,0.25391 0.0195,0.50781 0.0195,0.23438 0.0195,0.3711 0,2.01172 -0.21485,3.86719 -0.19531,1.83593 -0.44922,3.51562 -0.2539,1.66016 -0.46875,3.18359 -0.19531,1.52344 -0.19531,2.89063 0,0.58594 0.0781,1.15234 0.0977,0.56641 0.33203,0.9961 0.2539,0.42968 0.68359,0.70312 0.44922,0.25391 1.15235,0.25391 0.97656,0 1.95312,-0.41016 0.99609,-0.42969 1.89453,-1.1914 0.89844,-0.76172 1.66016,-1.81641 0.78125,-1.05469 1.34765,-2.32422 0.58594,-1.28906 0.89844,-2.73437 0.33203,-1.46485 0.33203,-3.02735 0,-0.76172 -0.0977,-1.48437 -0.0977,-0.72266 -0.3125,-1.34766 -0.21485,-0.625 -0.60547,-1.07422 -0.3711,-0.46875 -0.91797,-0.70312 -0.23438,0.52734 -0.68359,0.76172 -0.44922,0.23437 -0.95704,0.23437 -0.27343,0 -0.5664,-0.0977 -0.29297,-0.0976 -0.54688,-0.3125 -0.23437,-0.21484 -0.39062,-0.52734 -0.15625,-0.33203 -0.15625,-0.78125 0,-0.33203 0.11719,-0.68359 0.13671,-0.35157 0.41015,-0.625 0.27344,-0.29297 0.72266,-0.46875 0.46875,-0.19532 1.15234,-0.19532 1.21094,0 2.10938,0.56641 0.89843,0.56641 1.48437,1.52344 0.58594,0.95703 0.87891,2.22656 0.29297,1.26953 0.29297,2.69531 0,1.32813 -0.25391,2.71485 -0.23437,1.36718 -0.72266,2.67578 z" style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Lora;-inkscape-font-specification:'Lora Italic';fill:#292929;fill-opacity:1" id="path4140"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

160
module.nix Normal file
View File

@@ -0,0 +1,160 @@
{
config,
lib,
pkgs,
...
}:
let
sp = config.selfprivacy;
cfg = sp.modules.writefreely;
oauthClientID = "writefreely";
auth-passthru = config.selfprivacy.passthru.auth;
oauth2-provider-origin = config.services.kanidm.serverSettings.origin;
usersGroup = "sp.writefreely.users";
oauthClientSecretFP = auth-passthru.mkOAuth2ClientSecretFP oauthClientID;
in
{
options.selfprivacy.modules.writefreely = {
enable =
(lib.mkOption {
default = false;
type = lib.types.bool;
description = "Enable WriteFreely";
})
// {
meta = {
type = "enable";
};
};
location =
(lib.mkOption {
type = lib.types.str;
description = "WriteFreely location";
})
// {
meta = {
type = "location";
};
};
subdomain =
(lib.mkOption {
default = "writefreely";
type = lib.types.strMatching "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
description = "Subdomain (changing subdomain after setting up will cause breakage of the federation!)";
})
// {
meta = {
widget = "subdomain";
type = "string";
regex = "[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]";
weight = 0;
};
};
enableFederation =
(lib.mkOption {
default = false;
type = lib.types.bool;
description = "Enable the ActivityPub federation.";
})
// {
meta = {
type = "bool";
weight = 1;
};
};
title =
(lib.mkOption {
default = "WriteFreely";
type = lib.types.str;
description = "Name of the WriteFreely instance.";
})
// {
meta = {
type = "str";
weight = 2;
};
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = sp.sso.enable;
message = "WriteFreely cannot be enabled when SSO is disabled.";
}
];
fileSystems = lib.mkIf sp.useBinds {
"/var/lib/writefreely" = {
device = "/volumes/${cfg.location}/writefreely";
options = [ "bind" ];
};
};
services.writefreely = {
enable = true;
database.type = "sqlite3";
host = "${cfg.subdomain}.${sp.domain}";
settings = {
server.port = 8081;
app = {
site_name = cfg.title;
single_user = false;
federation = cfg.enableFederation;
disable_password_auth = true;
open_registration = false;
};
"oauth.generic" = {
client_id = oauthClientID;
host = oauth2-provider-origin;
display_name = "SSO";
token_endpoint = "/oauth2/token";
inspect_endpoint = "/oauth2/openid/${oauthClientID}/userinfo";
auth_endpoint = "/ui/oauth2";
};
};
};
systemd = {
services.writefreely = {
requeres = [ "writefreely-secrets.service" ];
unitConfig.RequiresMountsFor = lib.mkIf sp.useBinds "/volumes/${cfg.location}/writefreely";
serviceConfig.Slice = "writefreely.slice";
};
services.writefreely-secrets = let inherit (config.services.writefreely) stateDir;
in {
wantedBy = [ "multi-user.target" ];
requiredBy = [ "writefreely.service" ];
serviceConfig = {
Type = "oneshot";
Slice = "writefreely.slice";
};
script = let crudini = lib.getExe pkgs.crudini;
in ''
${crudini} --set ${stateDir}/config.ini oauth.generic client_secret '$(cat ${oauthClientSecretFP})'
'';
};
slices.writefreely = {
description = "WriteFreely 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 = [ "writefreely.service" ];
enablePkce = false;
linuxUserOfClient = "writefreely";
linuxGroupOfClient = "writefreely";
};
};
}