···11+# Pict-rs {#module-services-pict-rs}
22+33+pict-rs is a a simple image hosting service.
44+55+## Quickstart {#module-services-pict-rs-quickstart}
66+77+the minimum to start pict-rs is
88+99+```nix
1010+services.pict-rs.enable = true;
1111+```
1212+1313+this will start the http server on port 8080 by default.
1414+1515+## Usage {#module-services-pict-rs-usage}
1616+1717+pict-rs offers the following endpoints:
1818+- `POST /image` for uploading an image. Uploaded content must be valid multipart/form-data with an
1919+ image array located within the `images[]` key
2020+2121+ This endpoint returns the following JSON structure on success with a 201 Created status
2222+ ```json
2323+ {
2424+ "files": [
2525+ {
2626+ "delete_token": "JFvFhqJA98",
2727+ "file": "lkWZDRvugm.jpg"
2828+ },
2929+ {
3030+ "delete_token": "kAYy9nk2WK",
3131+ "file": "8qFS0QooAn.jpg"
3232+ },
3333+ {
3434+ "delete_token": "OxRpM3sf0Y",
3535+ "file": "1hJaYfGE01.jpg"
3636+ }
3737+ ],
3838+ "msg": "ok"
3939+ }
4040+ ```
4141+- `GET /image/download?url=...` Download an image from a remote server, returning the same JSON
4242+ payload as the `POST` endpoint
4343+- `GET /image/original/{file}` for getting a full-resolution image. `file` here is the `file` key from the
4444+ `/image` endpoint's JSON
4545+- `GET /image/details/original/{file}` for getting the details of a full-resolution image.
4646+ The returned JSON is structured like so:
4747+ ```json
4848+ {
4949+ "width": 800,
5050+ "height": 537,
5151+ "content_type": "image/webp",
5252+ "created_at": [
5353+ 2020,
5454+ 345,
5555+ 67376,
5656+ 394363487
5757+ ]
5858+ }
5959+ ```
6060+- `GET /image/process.{ext}?src={file}&...` get a file with transformations applied.
6161+ existing transformations include
6262+ - `identity=true`: apply no changes
6363+ - `blur={float}`: apply a gaussian blur to the file
6464+ - `thumbnail={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}`
6565+ square using raw pixel sampling
6666+ - `resize={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` square
6767+ using a Lanczos2 filter. This is slower than sampling but looks a bit better in some cases
6868+ - `crop={int-w}x{int-h}`: produce a cropped version of the image with an `{int-w}` by `{int-h}`
6969+ aspect ratio. The resulting crop will be centered on the image. Either the width or height
7070+ of the image will remain full-size, depending on the image's aspect ratio and the requested
7171+ aspect ratio. For example, a 1600x900 image cropped with a 1x1 aspect ratio will become 900x900. A
7272+ 1600x1100 image cropped with a 16x9 aspect ratio will become 1600x900.
7373+7474+ Supported `ext` file extensions include `png`, `jpg`, and `webp`
7575+7676+ An example of usage could be
7777+ ```
7878+ GET /image/process.jpg?src=asdf.png&thumbnail=256&blur=3.0
7979+ ```
8080+ which would create a 256x256px JPEG thumbnail and blur it
8181+- `GET /image/details/process.{ext}?src={file}&...` for getting the details of a processed image.
8282+ The returned JSON is the same format as listed for the full-resolution details endpoint.
8383+- `DELETE /image/delete/{delete_token}/{file}` or `GET /image/delete/{delete_token}/{file}` to
8484+ delete a file, where `delete_token` and `file` are from the `/image` endpoint's JSON
8585+8686+## Missing {#module-services-pict-rs-missing}
8787+8888+- 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
···11+{ lib, pkgs, config, ... }:
22+with lib;
33+let
44+ cfg = config.services.pict-rs;
55+in
66+{
77+ meta.maintainers = with maintainers; [ happysalada ];
88+ # Don't edit the docbook xml directly, edit the md and generate it:
99+ # `pandoc pict-rs.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > pict-rs.xml`
1010+ meta.doc = ./pict-rs.xml;
1111+1212+ options.services.pict-rs = {
1313+ enable = mkEnableOption "pict-rs server";
1414+ dataDir = mkOption {
1515+ type = types.path;
1616+ default = "/var/lib/pict-rs";
1717+ description = ''
1818+ The directory where to store the uploaded images.
1919+ '';
2020+ };
2121+ address = mkOption {
2222+ type = types.str;
2323+ default = "127.0.0.1";
2424+ description = ''
2525+ The IPv4 address to deploy the service to.
2626+ '';
2727+ };
2828+ port = mkOption {
2929+ type = types.port;
3030+ default = 8080;
3131+ description = ''
3232+ The port which to bind the service to.
3333+ '';
3434+ };
3535+ };
3636+ config = lib.mkIf cfg.enable {
3737+ systemd.services.pict-rs = {
3838+ environment = {
3939+ PICTRS_PATH = cfg.dataDir;
4040+ PICTRS_ADDR = "${cfg.address}:${toString cfg.port}";
4141+ };
4242+ wantedBy = [ "multi-user.target" ];
4343+ serviceConfig = {
4444+ DynamicUser = true;
4545+ StateDirectory = "pict-rs";
4646+ ExecStart = "${pkgs.pict-rs}/bin/pict-rs";
4747+ };
4848+ };
4949+ };
5050+}
+162
nixos/modules/services/web-apps/pict-rs.xml
···11+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-pict-rs">
22+ <title>Pict-rs</title>
33+ <para>
44+ pict-rs is a a simple image hosting service.
55+ </para>
66+ <section xml:id="module-services-pict-rs-quickstart">
77+ <title>Quickstart</title>
88+ <para>
99+ the minimum to start pict-rs is
1010+ </para>
1111+ <programlisting language="bash">
1212+services.pict-rs.enable = true;
1313+</programlisting>
1414+ <para>
1515+ this will start the http server on port 8080 by default.
1616+ </para>
1717+ </section>
1818+ <section xml:id="module-services-pict-rs-usage">
1919+ <title>Usage</title>
2020+ <para>
2121+ pict-rs offers the following endpoints: -
2222+ <literal>POST /image</literal> for uploading an image. Uploaded
2323+ content must be valid multipart/form-data with an image array
2424+ located within the <literal>images[]</literal> key
2525+ </para>
2626+ <programlisting>
2727+This endpoint returns the following JSON structure on success with a 201 Created status
2828+```json
2929+{
3030+ "files": [
3131+ {
3232+ "delete_token": "JFvFhqJA98",
3333+ "file": "lkWZDRvugm.jpg"
3434+ },
3535+ {
3636+ "delete_token": "kAYy9nk2WK",
3737+ "file": "8qFS0QooAn.jpg"
3838+ },
3939+ {
4040+ "delete_token": "OxRpM3sf0Y",
4141+ "file": "1hJaYfGE01.jpg"
4242+ }
4343+ ],
4444+ "msg": "ok"
4545+}
4646+```
4747+</programlisting>
4848+ <itemizedlist>
4949+ <listitem>
5050+ <para>
5151+ <literal>GET /image/download?url=...</literal> Download an
5252+ image from a remote server, returning the same JSON payload as
5353+ the <literal>POST</literal> endpoint
5454+ </para>
5555+ </listitem>
5656+ <listitem>
5757+ <para>
5858+ <literal>GET /image/original/{file}</literal> for getting a
5959+ full-resolution image. <literal>file</literal> here is the
6060+ <literal>file</literal> key from the <literal>/image</literal>
6161+ endpoint’s JSON
6262+ </para>
6363+ </listitem>
6464+ <listitem>
6565+ <para>
6666+ <literal>GET /image/details/original/{file}</literal> for
6767+ getting the details of a full-resolution image. The returned
6868+ JSON is structured like so:
6969+ <literal>json { "width": 800, "height": 537, "content_type": "image/webp", "created_at": [ 2020, 345, 67376, 394363487 ] }</literal>
7070+ </para>
7171+ </listitem>
7272+ <listitem>
7373+ <para>
7474+ <literal>GET /image/process.{ext}?src={file}&...</literal>
7575+ get a file with transformations applied. existing
7676+ transformations include
7777+ </para>
7878+ <itemizedlist spacing="compact">
7979+ <listitem>
8080+ <para>
8181+ <literal>identity=true</literal>: apply no changes
8282+ </para>
8383+ </listitem>
8484+ <listitem>
8585+ <para>
8686+ <literal>blur={float}</literal>: apply a gaussian blur to
8787+ the file
8888+ </para>
8989+ </listitem>
9090+ <listitem>
9191+ <para>
9292+ <literal>thumbnail={int}</literal>: produce a thumbnail of
9393+ the image fitting inside an <literal>{int}</literal> by
9494+ <literal>{int}</literal> square using raw pixel sampling
9595+ </para>
9696+ </listitem>
9797+ <listitem>
9898+ <para>
9999+ <literal>resize={int}</literal>: produce a thumbnail of
100100+ the image fitting inside an <literal>{int}</literal> by
101101+ <literal>{int}</literal> square using a Lanczos2 filter.
102102+ This is slower than sampling but looks a bit better in
103103+ some cases
104104+ </para>
105105+ </listitem>
106106+ <listitem>
107107+ <para>
108108+ <literal>crop={int-w}x{int-h}</literal>: produce a cropped
109109+ version of the image with an <literal>{int-w}</literal> by
110110+ <literal>{int-h}</literal> aspect ratio. The resulting
111111+ crop will be centered on the image. Either the width or
112112+ height of the image will remain full-size, depending on
113113+ the image’s aspect ratio and the requested aspect ratio.
114114+ For example, a 1600x900 image cropped with a 1x1 aspect
115115+ ratio will become 900x900. A 1600x1100 image cropped with
116116+ a 16x9 aspect ratio will become 1600x900.
117117+ </para>
118118+ </listitem>
119119+ </itemizedlist>
120120+ <para>
121121+ Supported <literal>ext</literal> file extensions include
122122+ <literal>png</literal>, <literal>jpg</literal>, and
123123+ <literal>webp</literal>
124124+ </para>
125125+ <para>
126126+ An example of usage could be
127127+ <literal>GET /image/process.jpg?src=asdf.png&thumbnail=256&blur=3.0</literal>
128128+ which would create a 256x256px JPEG thumbnail and blur it
129129+ </para>
130130+ </listitem>
131131+ <listitem>
132132+ <para>
133133+ <literal>GET /image/details/process.{ext}?src={file}&...</literal>
134134+ for getting the details of a processed image. The returned
135135+ JSON is the same format as listed for the full-resolution
136136+ details endpoint.
137137+ </para>
138138+ </listitem>
139139+ <listitem>
140140+ <para>
141141+ <literal>DELETE /image/delete/{delete_token}/{file}</literal>
142142+ or <literal>GET /image/delete/{delete_token}/{file}</literal>
143143+ to delete a file, where <literal>delete_token</literal> and
144144+ <literal>file</literal> are from the <literal>/image</literal>
145145+ endpoint’s JSON
146146+ </para>
147147+ </listitem>
148148+ </itemizedlist>
149149+ </section>
150150+ <section xml:id="module-services-pict-rs-missing">
151151+ <title>Missing</title>
152152+ <itemizedlist spacing="compact">
153153+ <listitem>
154154+ <para>
155155+ Configuring the secure-api-key is not included yet. The
156156+ envisioned basic use case is consumption on localhost by other
157157+ services without exposing the service to the internet.
158158+ </para>
159159+ </listitem>
160160+ </itemizedlist>
161161+ </section>
162162+</chapter>