pict-rs: add initial module

+318
+1
nixos/modules/module-list.nix
··· 991 991 ./services/web-apps/nextcloud.nix 992 992 ./services/web-apps/nexus.nix 993 993 ./services/web-apps/node-red.nix 994 + ./services/web-apps/pict-rs.nix 994 995 ./services/web-apps/plantuml-server.nix 995 996 ./services/web-apps/plausible.nix 996 997 ./services/web-apps/pgpkeyserver-lite.nix
+88
nixos/modules/services/web-apps/pict-rs.md
··· 1 + # Pict-rs {#module-services-pict-rs} 2 + 3 + pict-rs is a a simple image hosting service. 4 + 5 + ## Quickstart {#module-services-pict-rs-quickstart} 6 + 7 + the minimum to start pict-rs is 8 + 9 + ```nix 10 + services.pict-rs.enable = true; 11 + ``` 12 + 13 + this will start the http server on port 8080 by default. 14 + 15 + ## Usage {#module-services-pict-rs-usage} 16 + 17 + pict-rs offers the following endpoints: 18 + - `POST /image` for uploading an image. Uploaded content must be valid multipart/form-data with an 19 + image array located within the `images[]` key 20 + 21 + This endpoint returns the following JSON structure on success with a 201 Created status 22 + ```json 23 + { 24 + "files": [ 25 + { 26 + "delete_token": "JFvFhqJA98", 27 + "file": "lkWZDRvugm.jpg" 28 + }, 29 + { 30 + "delete_token": "kAYy9nk2WK", 31 + "file": "8qFS0QooAn.jpg" 32 + }, 33 + { 34 + "delete_token": "OxRpM3sf0Y", 35 + "file": "1hJaYfGE01.jpg" 36 + } 37 + ], 38 + "msg": "ok" 39 + } 40 + ``` 41 + - `GET /image/download?url=...` Download an image from a remote server, returning the same JSON 42 + payload as the `POST` endpoint 43 + - `GET /image/original/{file}` for getting a full-resolution image. `file` here is the `file` key from the 44 + `/image` endpoint's JSON 45 + - `GET /image/details/original/{file}` for getting the details of a full-resolution image. 46 + The returned JSON is structured like so: 47 + ```json 48 + { 49 + "width": 800, 50 + "height": 537, 51 + "content_type": "image/webp", 52 + "created_at": [ 53 + 2020, 54 + 345, 55 + 67376, 56 + 394363487 57 + ] 58 + } 59 + ``` 60 + - `GET /image/process.{ext}?src={file}&...` get a file with transformations applied. 61 + existing transformations include 62 + - `identity=true`: apply no changes 63 + - `blur={float}`: apply a gaussian blur to the file 64 + - `thumbnail={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` 65 + square using raw pixel sampling 66 + - `resize={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` square 67 + using a Lanczos2 filter. This is slower than sampling but looks a bit better in some cases 68 + - `crop={int-w}x{int-h}`: produce a cropped version of the image with an `{int-w}` by `{int-h}` 69 + aspect ratio. The resulting crop will be centered on the image. Either the width or height 70 + of the image will remain full-size, depending on the image's aspect ratio and the requested 71 + aspect ratio. For example, a 1600x900 image cropped with a 1x1 aspect ratio will become 900x900. A 72 + 1600x1100 image cropped with a 16x9 aspect ratio will become 1600x900. 73 + 74 + Supported `ext` file extensions include `png`, `jpg`, and `webp` 75 + 76 + An example of usage could be 77 + ``` 78 + GET /image/process.jpg?src=asdf.png&thumbnail=256&blur=3.0 79 + ``` 80 + which would create a 256x256px JPEG thumbnail and blur it 81 + - `GET /image/details/process.{ext}?src={file}&...` for getting the details of a processed image. 82 + The returned JSON is the same format as listed for the full-resolution details endpoint. 83 + - `DELETE /image/delete/{delete_token}/{file}` or `GET /image/delete/{delete_token}/{file}` to 84 + delete a file, where `delete_token` and `file` are from the `/image` endpoint's JSON 85 + 86 + ## Missing {#module-services-pict-rs-missing} 87 + 88 + - Configuring the secure-api-key is not included yet. The envisioned basic use case is consumption on localhost by other services without exposing the service to the internet.
+50
nixos/modules/services/web-apps/pict-rs.nix
··· 1 + { lib, pkgs, config, ... }: 2 + with lib; 3 + let 4 + cfg = config.services.pict-rs; 5 + in 6 + { 7 + meta.maintainers = with maintainers; [ happysalada ]; 8 + # Don't edit the docbook xml directly, edit the md and generate it: 9 + # `pandoc pict-rs.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > pict-rs.xml` 10 + meta.doc = ./pict-rs.xml; 11 + 12 + options.services.pict-rs = { 13 + enable = mkEnableOption "pict-rs server"; 14 + dataDir = mkOption { 15 + type = types.path; 16 + default = "/var/lib/pict-rs"; 17 + description = '' 18 + The directory where to store the uploaded images. 19 + ''; 20 + }; 21 + address = mkOption { 22 + type = types.str; 23 + default = "127.0.0.1"; 24 + description = '' 25 + The IPv4 address to deploy the service to. 26 + ''; 27 + }; 28 + port = mkOption { 29 + type = types.port; 30 + default = 8080; 31 + description = '' 32 + The port which to bind the service to. 33 + ''; 34 + }; 35 + }; 36 + config = lib.mkIf cfg.enable { 37 + systemd.services.pict-rs = { 38 + environment = { 39 + PICTRS_PATH = cfg.dataDir; 40 + PICTRS_ADDR = "${cfg.address}:${toString cfg.port}"; 41 + }; 42 + wantedBy = [ "multi-user.target" ]; 43 + serviceConfig = { 44 + DynamicUser = true; 45 + StateDirectory = "pict-rs"; 46 + ExecStart = "${pkgs.pict-rs}/bin/pict-rs"; 47 + }; 48 + }; 49 + }; 50 + }
+162
nixos/modules/services/web-apps/pict-rs.xml
··· 1 + <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-pict-rs"> 2 + <title>Pict-rs</title> 3 + <para> 4 + pict-rs is a a simple image hosting service. 5 + </para> 6 + <section xml:id="module-services-pict-rs-quickstart"> 7 + <title>Quickstart</title> 8 + <para> 9 + the minimum to start pict-rs is 10 + </para> 11 + <programlisting language="bash"> 12 + services.pict-rs.enable = true; 13 + </programlisting> 14 + <para> 15 + this will start the http server on port 8080 by default. 16 + </para> 17 + </section> 18 + <section xml:id="module-services-pict-rs-usage"> 19 + <title>Usage</title> 20 + <para> 21 + pict-rs offers the following endpoints: - 22 + <literal>POST /image</literal> for uploading an image. Uploaded 23 + content must be valid multipart/form-data with an image array 24 + located within the <literal>images[]</literal> key 25 + </para> 26 + <programlisting> 27 + This endpoint returns the following JSON structure on success with a 201 Created status 28 + ```json 29 + { 30 + &quot;files&quot;: [ 31 + { 32 + &quot;delete_token&quot;: &quot;JFvFhqJA98&quot;, 33 + &quot;file&quot;: &quot;lkWZDRvugm.jpg&quot; 34 + }, 35 + { 36 + &quot;delete_token&quot;: &quot;kAYy9nk2WK&quot;, 37 + &quot;file&quot;: &quot;8qFS0QooAn.jpg&quot; 38 + }, 39 + { 40 + &quot;delete_token&quot;: &quot;OxRpM3sf0Y&quot;, 41 + &quot;file&quot;: &quot;1hJaYfGE01.jpg&quot; 42 + } 43 + ], 44 + &quot;msg&quot;: &quot;ok&quot; 45 + } 46 + ``` 47 + </programlisting> 48 + <itemizedlist> 49 + <listitem> 50 + <para> 51 + <literal>GET /image/download?url=...</literal> Download an 52 + image from a remote server, returning the same JSON payload as 53 + the <literal>POST</literal> endpoint 54 + </para> 55 + </listitem> 56 + <listitem> 57 + <para> 58 + <literal>GET /image/original/{file}</literal> for getting a 59 + full-resolution image. <literal>file</literal> here is the 60 + <literal>file</literal> key from the <literal>/image</literal> 61 + endpoint’s JSON 62 + </para> 63 + </listitem> 64 + <listitem> 65 + <para> 66 + <literal>GET /image/details/original/{file}</literal> for 67 + getting the details of a full-resolution image. The returned 68 + JSON is structured like so: 69 + <literal>json { &quot;width&quot;: 800, &quot;height&quot;: 537, &quot;content_type&quot;: &quot;image/webp&quot;, &quot;created_at&quot;: [ 2020, 345, 67376, 394363487 ] }</literal> 70 + </para> 71 + </listitem> 72 + <listitem> 73 + <para> 74 + <literal>GET /image/process.{ext}?src={file}&amp;...</literal> 75 + get a file with transformations applied. existing 76 + transformations include 77 + </para> 78 + <itemizedlist spacing="compact"> 79 + <listitem> 80 + <para> 81 + <literal>identity=true</literal>: apply no changes 82 + </para> 83 + </listitem> 84 + <listitem> 85 + <para> 86 + <literal>blur={float}</literal>: apply a gaussian blur to 87 + the file 88 + </para> 89 + </listitem> 90 + <listitem> 91 + <para> 92 + <literal>thumbnail={int}</literal>: produce a thumbnail of 93 + the image fitting inside an <literal>{int}</literal> by 94 + <literal>{int}</literal> square using raw pixel sampling 95 + </para> 96 + </listitem> 97 + <listitem> 98 + <para> 99 + <literal>resize={int}</literal>: produce a thumbnail of 100 + the image fitting inside an <literal>{int}</literal> by 101 + <literal>{int}</literal> square using a Lanczos2 filter. 102 + This is slower than sampling but looks a bit better in 103 + some cases 104 + </para> 105 + </listitem> 106 + <listitem> 107 + <para> 108 + <literal>crop={int-w}x{int-h}</literal>: produce a cropped 109 + version of the image with an <literal>{int-w}</literal> by 110 + <literal>{int-h}</literal> aspect ratio. The resulting 111 + crop will be centered on the image. Either the width or 112 + height of the image will remain full-size, depending on 113 + the image’s aspect ratio and the requested aspect ratio. 114 + For example, a 1600x900 image cropped with a 1x1 aspect 115 + ratio will become 900x900. A 1600x1100 image cropped with 116 + a 16x9 aspect ratio will become 1600x900. 117 + </para> 118 + </listitem> 119 + </itemizedlist> 120 + <para> 121 + Supported <literal>ext</literal> file extensions include 122 + <literal>png</literal>, <literal>jpg</literal>, and 123 + <literal>webp</literal> 124 + </para> 125 + <para> 126 + An example of usage could be 127 + <literal>GET /image/process.jpg?src=asdf.png&amp;thumbnail=256&amp;blur=3.0</literal> 128 + which would create a 256x256px JPEG thumbnail and blur it 129 + </para> 130 + </listitem> 131 + <listitem> 132 + <para> 133 + <literal>GET /image/details/process.{ext}?src={file}&amp;...</literal> 134 + for getting the details of a processed image. The returned 135 + JSON is the same format as listed for the full-resolution 136 + details endpoint. 137 + </para> 138 + </listitem> 139 + <listitem> 140 + <para> 141 + <literal>DELETE /image/delete/{delete_token}/{file}</literal> 142 + or <literal>GET /image/delete/{delete_token}/{file}</literal> 143 + to delete a file, where <literal>delete_token</literal> and 144 + <literal>file</literal> are from the <literal>/image</literal> 145 + endpoint’s JSON 146 + </para> 147 + </listitem> 148 + </itemizedlist> 149 + </section> 150 + <section xml:id="module-services-pict-rs-missing"> 151 + <title>Missing</title> 152 + <itemizedlist spacing="compact"> 153 + <listitem> 154 + <para> 155 + Configuring the secure-api-key is not included yet. The 156 + envisioned basic use case is consumption on localhost by other 157 + services without exposing the service to the internet. 158 + </para> 159 + </listitem> 160 + </itemizedlist> 161 + </section> 162 + </chapter>
+17
nixos/tests/pict-rs.nix
··· 1 + import ./make-test-python.nix ({ pkgs, lib, ... }: 2 + { 3 + name = "pict-rs"; 4 + meta.maintainers = with lib.maintainers; [ happysalada ]; 5 + 6 + machine = { ... }: { 7 + environment.systemPackages = with pkgs; [ curl jq ]; 8 + services.pict-rs.enable = true; 9 + }; 10 + 11 + testScript = '' 12 + start_all() 13 + 14 + machine.wait_for_unit("pict-rs") 15 + machine.wait_for_open_port("8080") 16 + ''; 17 + })