{"id":20748175,"url":"https://github.com/topi314/gobin","last_synced_at":"2025-04-28T12:04:26.780Z","repository":{"id":65568019,"uuid":"594398101","full_name":"topi314/gobin","owner":"topi314","description":"A hastebin/pastebin compatible paste server","archived":false,"fork":false,"pushed_at":"2025-04-26T16:15:55.000Z","size":13294,"stargazers_count":27,"open_issues_count":1,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-28T12:04:14.480Z","etag":null,"topics":["golang","hacktoberfest","hastebin","hastebin-client","pastebin","pastebin-client","postgres","sqlite","ssr"],"latest_commit_sha":null,"homepage":"https://xgob.in","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/topi314.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-28T12:35:12.000Z","updated_at":"2025-04-26T16:15:58.000Z","dependencies_parsed_at":"2024-03-28T08:31:13.651Z","dependency_job_id":"d37f5834-4d40-4844-bb41-23f4daef7a49","html_url":"https://github.com/topi314/gobin","commit_stats":{"total_commits":161,"total_committers":4,"mean_commits":40.25,"dds":"0.024844720496894457","last_synced_commit":"3be84c7d87cfb93aeb91f42eed7ab5ba3cc4f140"},"previous_names":["topi314/gobin","topisenpai/gobin"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fgobin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fgobin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fgobin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/topi314%2Fgobin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/topi314","download_url":"https://codeload.github.com/topi314/gobin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251311330,"owners_count":21569009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","hacktoberfest","hastebin","hastebin-client","pastebin","pastebin-client","postgres","sqlite","ssr"],"created_at":"2024-11-17T08:15:51.446Z","updated_at":"2025-04-28T12:04:26.763Z","avatar_url":"https://github.com/topi314.png","language":"Go","readme":"[![Go Report](https://goreportcard.com/badge/github.com/topi314/gobin/v3)](https://goreportcard.com/report/github.com/topi314/gobin/v3)\n[![Go Version](https://img.shields.io/github/go-mod/go-version/topi314/gobin)](https://golang.org/doc/devel/release.html)\n[![KittyBot License](https://img.shields.io/github/license/topi314/gobin)](LICENSE)\n[![KittyBot Version](https://img.shields.io/github/v/tag/topi314/gobin?label=release)](https://github.com/topi314/gobin/releases/latest)\n[![Server](https://github.com/topi314/gobin/actions/workflows/server.yml/badge.svg)](https://github.com/topi314/gobin/actions/workflows/server.yml)\n[![CLI](https://github.com/topi314/gobin/actions/workflows/cli.yml/badge.svg)](https://github.com/topi314/gobin/actions/workflows/cli.yml)\n[![Discord](https://discordapp.com/api/guilds/608506410803658753/embed.png?style=shield)](https://discord.gg/sD3ABd5)\n\n# gobin\n\ngobin is a simple lightweight haste-server alternative written in Go, HTML, JS and CSS. It is aimed to be easy to use\nand deploy. You can find an instance running\nat [xgob.in](https://xgob.in).\n\n\u003cdetails\u003e\n\u003csummary\u003eTable of Contents\u003c/summary\u003e\n\n- [Features](#features)\n- [Installation](#installation)\n    - [Server](#server)\n        - [Docker](#docker)\n        - [Manual](#manual)\n            - [Requirements](#requirements)\n            - [Build](#build)\n            - [Run](#run)\n    - [CLI](#cli)\n        - [Release](#release)\n        - [Manual](#manual-1)\n            - [Requirements](#requirements-1)\n            - [Build](#build-1)\n            - [Run](#run-1)\n- [Configuration](#configuration)\n- [Custom Themes](#custom-themes)\n- [Rate Limit](#rate-limits)\n- [API](#api)\n    - [Errors](#errors)\n    - [Formatter Enum](#formatter-enum)\n    - [Language Enum](#language-enum)\n    - [Create a document](#create-a-document)\n        - [Single file](#single-file)\n        - [Multiple files](#multiple-files)\n    - [Get a document (version)](#get-a-document-version)\n    - [Get a document (version) file](#get-a-document-version-file)\n    - [Get a documents versions](#get-a-documents-versions)\n    - [Update a document](#update-a-document)\n        - [Single file](#single-file-1)\n        - [Multiple files](#multiple-files-1)\n    - [Delete a document (version)](#delete-a-document-version)\n    - [Share a document](#share-a-document)\n    - [Document webhooks](#document-webhooks)\n        - [Create a document webhook](#create-a-document-webhook)\n        - [Update a document webhook](#update-a-document-webhook)\n        - [Delete a document webhook](#delete-a-document-webhook)\n    - [Other endpoints](#other-endpoints)\n- [License](#license)\n- [Contributing](#contributing)\n- [Credits](#credits)\n- [Contact](#contact)\n\n\u003c/details\u003e\n\n## Features\n\n- Easy to deploy and use\n- Built-in rate-limiting\n- Create, update and delete documents\n- Document update/delete webhooks\n- Syntax highlighting\n- Social Media PNG previews\n- Document expiration\n- Supports [PostgreSQL](https://www.postgresql.org/) or [SQLite](https://sqlite.org/)\n- One binary and config file\n- Docker image available\n- ~~Metrics (to be implemented)~~\n- [base16](https://github.com/chriskempson/base16) \u0026 [chroma](https://github.com/topi314/chroma) custom themes\n\n## Installation\n\n### Server\n\n#### Docker\n\nThe easiest way to deploy gobin is using docker with [Docker Compose](https://docs.docker.com/compose/). You can find\nthe docker image\non [Packages](https://github.com/topi314/gobin/pkgs/container/gobin).\n\nCreate a new `docker-compose.yml` file with the following content:\n\n\u003e [!Note]\n\u003e You should change the password in the `docker-compose.yml` and `gobin.toml` file.\n\n```yaml\nversion: \"3.8\"\n\nservices:\n  gobin:\n    image: ghcr.io/topi314/gobin:latest\n    container_name: gobin\n    restart: unless-stopped\n    volumes:\n      - ./gobin.toml:/var/lib/gobin/gobin.toml\n      # use this for sqlite\n      - ./gobin.db:/var/lib/gobin/gobin.db\n    ports:\n      - 80:80\n\n  # or use this for postgres\n  postgres:\n    image: postgres:latest\n    container_name: postgres\n    restart: unless-stopped\n    volumes:\n      - ./data:/var/lib/postgresql/data\n    environment:\n      POSTGRES_DB: gobin\n      POSTGRES_USER: gobin\n      POSTGRES_PASSWORD: password\n```\n\nFor `gobin.toml`/database schema see [Configuration](#configuration).\n\n```bash\ndocker-compose up -d\n```\n\n---\n\n#### Manual\n\n##### Requirements\n\n- Go 1.24 or higher\n\n##### Build\n\n```bash\ngit clone https://github.com/topi314/gobin.git\ncd gobin\ngo build -o gobin github.com/topi314/gobin/v3\n```\n\nor\n\n```bash\ngo install github.com/topi314/gobin/v3@latest\n```\n\n##### Run\n\n```bash\ngobin --config=gobin.toml\n```\n\n---\n\n### CLI\n\n#### Release\n\nYou can find the latest release on [Releases](https://github.com/topi314/gobin/releases).\n\n#### Manual\n\n##### Requirements\n\n- Go 1.21 or higher\n\n##### Build\n\n```bash\ngit clone https://github.com/topi314/gobin.git\ncd gobin\ngo build -o gobin github.com/topi314/gobin/v3/cli\n```\n\nor\n\n```bash\ngo install github.com/topi314/gobin/v3/cli@latest\n# rename binary to gobin\nmv $(go env GOPATH)/bin/cli $(go env GOPATH)/bin/gobin\n# or move binary into /usr/local/bin\nmv $(go env GOPATH)/bin/cli /usr/local/bin/gobin\n# change file ownership to root \nchown 0:0 /usr/local/bin/gobin\n```\n\n##### Run\n\n```bash\ngobin help\n```\n\n---\n\n## Configuration\n\nThe database schema is automatically created or migrated when you start gobin.\n\nCreate a new `gobin.toml` file with the following content:\n\n\u003e [!Note]\n\u003e Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".\n\n```json5\n{\n  \"log\": {\n    // level can be -4 (debug), 0 (info), 4 (warn), 8 (error)\n    \"level\": 0,\n    // log format, either \"json\" or \"text\"\n    \"format\": \"text\",\n    // whether to add the source file and line to the log output\n    \"add_source\": false,\n    // whether to add color to the log output (only for text format)\n    \"no_color\": false\n  },\n  // enable or disable debug profiler endpoint\n  \"debug\": false,\n  // enable or disable hot reload of templates and assets\n  \"dev_mode\": false,\n  \"listen_addr\": \"0.0.0.0:80\",\n  // secret for jwt tokens, replace with a long random string\n  \"jwt_secret\": \"...\",\n  \"database\": {\n    // either \"postgres\" or \"sqlite\"\n    \"type\": \"postgres\",\n    \"debug\": false,\n    \"expire_after\": \"168h\",\n    \"cleanup_interval\": \"10m\",\n    // path to sqlite database\n    // if you run gobin with docker make sure to set it to \"/var/lib/gobin/gobin.db\"\n    \"path\": \"gobin.db\",\n    // postgres connection settings\n    \"host\": \"localhost\",\n    \"port\": 5432,\n    \"username\": \"gobin\",\n    \"password\": \"password\",\n    \"database\": \"gobin\",\n    \"ssl_mode\": \"disable\"\n  },\n  // max character count for all files in a document combined (0 to disable)\n  \"max_document_size\": 0,\n  // max_highlight_size is the max character count for a single file in a document to be highlighted (0 to disable)\n  \"max_highlight_size\": 0,\n  // omit or set values to 0 or \"0\" to disable rate limit\n  \"rate_limit\": {\n    // number of requests which can be done in the duration\n    \"requests\": 10,\n    // the duration of the requests\n    \"duration\": \"1m\",\n    // a list of ip addresses which are exempt from rate limiting\n    \"whitelist\": [\n      \"127.0.0.1\"\n    ],\n    // a list of ip addresses which are blocked from rate limited endpoints\n    \"blacklist\": [\n      \"123.456.789.0\"\n    ]\n  },\n  // settings for social media previews, omit to disable\n  \"preview\": {\n    // path to inkscape binary https://inkscape.org/\n    \"inkscape_path\": \"/usr/bin/inkscape\",\n    // how many lines should be shown in the preview\n    \"max_lines\": 10,\n    // how high the resolution of the preview should be, 96 is the default\n    \"dpi\": 96,\n    // how many previews should be maximally cached\n    \"cache_size\": 1024,\n    // how long should previews be cached\n    \"cache_duration\": \"1h\"\n  },\n  // open telemetry settings, omit to disable\n  \"otel\": {\n    // the instance id of the server\n    \"instance_id\": \"1\",\n    // otel trace settings, omit to disable\n    \"trace\": {\n      // the address of the tempo instance\n      \"endpoint\": \"tempo:4318\",\n      // whether to use an insecure connection\n      \"insecure\": true\n    },\n    // otel metrics settings, omit to disable\n    \"metrics\": {\n      // the address where the metrics should be exposed\n      \"listen_addr\": \":9100\"\n    }\n  },\n  // settings for webhooks, omit to disable\n  \"webhook\": {\n    // webhook reqauest timeout\n    \"timeout\": \"10s\",\n    // max number of tries to send a webhook\n    \"max_tries\": 3,\n    // how long to wait before retrying a webhook\n    \"backoff\": \"1s\",\n    // how much the backoff should be increased after each retry\n    \"backoff_factor\": 2,\n    // max backoff time\n    \"max_backoff\": \"5m\"\n  },\n  // load custom chroma xml or base16 yaml themes from this directory, omit to disable\n  \"custom_styles\": \"custom_styles\",\n  \"default_style\": \"snazzy\"\n}\n```\n\nAlternatively you can use environment variables to configure gobin. The environment variables are prefixed with `GOBIN_`\nand are in uppercase. For example `GOBIN_DATABASE_TYPE`\nor `GOBIN_RATE_LIMIT_REQUESTS`.\n\n\u003cdetails\u003e\n\u003csummary\u003eHere is a list of all environment variables\u003c/summary\u003e\n\n```env\nGOBIN_LOG_LEVEL=info\nGOBIN_LOG_FORMAT=text\nGOBIN_LOG_ADD_SOURCE=false\n\nGOBIN_DEBUG=false\nGOBIN_DEV_MODE=false\nGOBIN_LISTEN_ADDR=0.0.0.0:80\nGOBIN_JWT_SECRET=...\n\nGOBIN_DATABASE_TYPE=postgres\nGOBIN_DATABASE_DEBUG=false\nGOBIN_DATABASE_EXPIRE_AFTER=168h\nGOBIN_DATABASE_CLEANUP_INTERVAL=10m\n\nGOBIN_DATABASE_PATH=gobin.db\n\nGOBIN_DATABASE_HOST=localhost\nGOBIN_DATABASE_PORT=5432\nGOBIN_DATABASE_USERNAME=gobin\nGOBIN_DATABASE_PASSWORD=password\nGOBIN_DATABASE_DATABASE=gobin\nGOBIN_DATABASE_SSL_MODE=disable\n\nGOBIN_MAX_DOCUMENT_SIZE=0\nGOBIN_MAX_HIGHLIGHT_SIZE=0\n\nGOBIN_RATE_LIMIT_REQUESTS=10\nGOBIN_RATE_LIMIT_DURATION=1m\n\nGOBIN_PREVIEW_INKSCAPE_PATH=/usr/bin/inkscape\nGOBIN_PREVIEW_MAX_LINES=10\nGOBIN_PREVIEW_DPI=96\nGOBIN_PREVIEW_CACHE_SIZE=1024\nGOBIN_PREVIEW_CACHE_TTL=1h\n\nGOBIN_WEBHOOK_TIMEOUT=10s\nGOBIN_WEBHOOK_MAX_TRIES=3\nGOBIN_WEBHOOK_BACKOFF=1s\nGOBIN_WEBHOOK_BACKOFF_FACTOR=2\nGOBIN_WEBHOOK_MAX_BACKOFF=5m\n\nGOBIN_CUSTOM_STYLES=custom_styles\nGOBIN_DEFAULT_STYLE=snazzy\n```\n\n\u003c/details\u003e\n\n---\n\n## Custom Themes\n\nYou can add your own themes to gobin by adding the `custom_styles` directory to the config file and adding your themes\nto it.\n\nThe themes have to be in the following format:\n\n```yaml file=custom_styles/name.yaml\nscheme: \"name\"\nauthor: \"author\"\ntheme: \"dark\" # or \"light\"\nbase00: \"282a36\"\nbase01: \"34353e\"\nbase02: \"43454f\"\nbase03: \"78787e\"\nbase04: \"a5a5a9\"\nbase05: \"e2e4e5\"\nbase06: \"eff0eb\"\nbase07: \"f1f1f0\"\nbase08: \"ff5c57\"\nbase09: \"ff9f43\"\nbase0A: \"f3f99d\"\nbase0B: \"5af78e\"\nbase0C: \"9aedfe\"\nbase0D: \"57c7ff\"\nbase0E: \"ff6ac1\"\nbase0F: \"b2643c\"\n```\n\nSee [base16](https://github.com/chriskempson/base16) for more information.\n\nOr you can use the [chroma](https://github.com/topi314/chroma/tree/master/styles/embedded) XML themes.\n\n## Rate Limits\n\nAll `POST`, `PATCH` and `DELETE` endpoints are rate limited. The rate limit can be configured in the config file.\nThe bucket is based on the IP address and the path of the request. So each of these unique combinations has its own bucket/rate limit.\n\nIt's based on a sliding window algorithm, but instead of a fixed window the window will start at the first request and\nend after the duration. So if you set the duration to 1 minute and send 10 requests in the first 10 seconds you will be rate limited for 50 seconds. After that you can send 10 requests\nagain.\n\nGobin will return these headers to help clients keep track of the rate limit:\n\n| Header                | Description                                                                    |\n|-----------------------|--------------------------------------------------------------------------------|\n| X-RateLimit-Limit     | The maximum number of requests which can be done in the duration.              |\n| X-RateLimit-Remaining | The number of remaining requests which can be done in the duration.            |\n| X-RateLimit-Reset     | The time when the rate limit will be reset in unix timestamp.                  |\n| Retry-After           | The time when the rate limit will be reset in seconds. (only when hit a `429`) |\n\n---\n\n## API\n\nFields marked with `?` are optional and types marked with `?` are nullable.\n\n### Errors\n\nIn case of an error gobin will return the following JSON body with the corresponding HTTP status code:\n\n```json5\n{\n  \"message\": \"document not found\",\n  // error message\n  \"status\": 404,\n  // HTTP status code\n  \"path\": \"/documents/7df3vw\",\n  // request path\n  \"request_id\": \"fbe0a365387f/gVAMGuraLW-003490\"\n  // request id\n}\n```\n\n---\n\n### Formatter Enum\n\nDocument formatting is done using [chroma](https://github.com/topi314/chroma). The following formatters are\navailable:\n\n| Value           | Description             |\n|-----------------|-------------------------|\n| terminal8       | 8-bit terminal colors   |\n| terminal16      | 16-bit terminal colors  |\n| terminal256     | 256-bit terminal colors |\n| terminal16m     | true terminal colors    |\n| html            | HTML                    |\n| html-standalone | Standalone HTML         |\n| svg             | SVG                     |\n\n---\n\n### Language Enum\n\nThe following languages are available:\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to expand\u003c/summary\u003e\n\n| Prefix | Language                                                                                                                                                                                                          |\n|--------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| A      | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Arduino, Awk                                                                                                        |\n| B      | Ballerina, Bash, Batchfile, BibTeX, Bicep, BlitzBasic, BNF, Brainfuck, BQN                                                                                                                                        |\n| C      | C, C#, C++, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Crystal, CSS, Cython |\n| D      | D, Dart, Diff, Django/Jinja, Docker, DTD, Dylan                                                                                                                                                                   |\n| E      | EBNF, Elixir, Elm, EmacsLisp, Erlang                                                                                                                                                                              |\n| F      | Factor, Fish, Forth, Fortran, FSharp                                                                                                                                                                              |\n| G      | GAS, GDScript, Genshi, Genshi HTML, Genshi Text, Gherkin, GLSL, Gnuplot, Go, Go HTML Template, Go Text Template, GraphQL, Groff, Groovy                                                                           |\n| H      | Handlebars, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HTML, HTTP, Hy                                                                                                                                                |\n| I      | Idris, Igor, INI, Io                                                                                                                                                                                              |\n| J      | J, Java, JavaScript, JSON, Julia, Jungle                                                                                                                                                                          |\n| K      | Kotlin                                                                                                                                                                                                            |\n| L      | Lighttpd configuration file, LLVM, Lua                                                                                                                                                                            |\n| M      | Makefile, Mako, markdown, Mason, Mathematica, Matlab, MiniZinc, MLIR, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL                                                                                           |\n| N      | NASM, Newspeak, Nginx configuration file, Nim, Nix                                                                                                                                                                |\n| O      | Objective-C, OCaml, Octave, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode                                                                                                                                      |\n| P      | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, PromQL, Properties, Protocol Buffer, PSL, Puppet, Python 2, Python       |\n| Q      | QBasic                                                                                                                                                                                                            |\n| R      | R, Racket, Ragel, Raku, react, ReasonML, reg, reStructuredText, Rexx, Ruby, Rust                                                                                                                                  |\n| S      | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog                                        |\n| T      | TableGen, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData                        |\n| V      | VB.net, verilog, VHDL, VHS, VimL, vue                                                                                                                                                                             |\n| W      | WDTE                                                                                                                                                                                                              |\n| X      | XML, Xorg                                                                                                                                                                                                         |\n| Y      | YAML, YANG                                                                                                                                                                                                        |\n| Z      | Zig                                                                                                                                                                                                               |\n\n\u003c/details\u003e\n\n--- \n\n### Create a document\n\nYou can create a document with a single file or multiple files. When creating a document with a single file you can\nsimply `POST` the content to `/documents`.\nWhen creating a document with multiple files you have to `POST` the content to `/documents` as `multipart/form-data`.\nSee below for more information.\n\n#### Single file\n\nTo create a document with a single file you have to send a `POST` request to `/documents` with the `content` as body.\n\n| Header               | Type      | Description                                             |\n|----------------------|-----------|---------------------------------------------------------|\n| Content-Disposition? | string    | The file name of the document.                          |\n| Content-Type?        | string    | The content type of the document.                       |\n| Language?            | string    | The language of the document.                           |\n| Expires?             | Timestamp | When the document file should expire in RFC 3339 format |\n\n| Query Parameter | Type                         | Description                                             |\n|-----------------|------------------------------|---------------------------------------------------------|\n| language?       | [language](#language-enum)   | The language of the document.                           |\n| formatter?      | [formatter](#formatter-enum) | With which formatter to render the document.            |\n| style?          | style name                   | Which style to use for the formatter                    |\n| expires?        | Timestamp                    | When the document file should expire in RFC 3339 format |\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```go\npackage main\n\nfunc main() {\n\tprintln(\"Hello World!\")\n}\n```\n\n\u003c/details\u003e\n\n#### Multiple files\n\nTo create a document with multiple files you have to send a `POST` request to `/documents` with the `content`\nas `multipart/form-data` body.\nEach file has to be in its own part with the name `file-{index}`. The first file has to be named `file-0`, the\nsecond `file-1` and so on.\n\n| Query Parameter | Type                         | Description                                             |\n|-----------------|------------------------------|---------------------------------------------------------|\n| formatter?      | [formatter](#formatter-enum) | With which formatter to render the document.            |\n| style?          | style name                   | Which style to use for the formatter                    |\n| expires?        | Timestamp                    | When the document file should expire in RFC 3339 format |\n\n| Header   | Type      | Description                                             |\n|----------|-----------|---------------------------------------------------------|\n| Expires? | Timestamp | When the document file should expire in RFC 3339 format |\n\n| Part Header         | Type      | Description                                                                                  |\n|---------------------|-----------|----------------------------------------------------------------------------------------------|\n| Content-Disposition | string    | The form \u0026 file name of the document.                                                        |\n| Content-Type?       | string    | The content type/language of the document.                                                   |\n| Language?           | string    | The language of the document.                                                                |\n| Expires?            | Timestamp | When the document file should expire in RFC 3339 format, overwrites the query param \u0026 header |\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```multipart/form-data\n-----------------------------302370379826172687681786440755\nContent-Disposition: form-data; name=\"file-0\"; filename=\"main.go\"\nContent-Type: text/x-gosrc\nLanguage: Go\nExpires: 2023-10-10T10:10:10Z\n\npackage main\n\nfunc main() {\n\tprintln(\"Hello World!\")\n}\n-----------------------------302370379826172687681786440755\nContent-Disposition: form-data; name=\"file-1\"; filename=\"untitled1\"\nContent-Type: text/plain; charset=utf-8\n\nHello World!\n-----------------------------302370379826172687681786440755--\n```\n\n\u003c/details\u003e\n\nA successful request will return a `201 Created` response with a JSON body containing the document key and token to\nupdate the document.\n\n```json5\n{\n  \"key\": \"hocwr6i6\",\n  \"version\": 1,\n  \"files\": [\n    {\n      \"name\": \"main.go\",\n      \"content\": \"package main\\n\\nfunc main() {\\n    println(\\\"Hello World!\\\")\\n}\",\n      // only if formatter is set\n      \"formatted\": \"...\",\n      \"language\": \"Go\",\n      \"expires_at\": null\n    },\n    {\n      \"name\": \"untitled1\",\n      \"content\": \"Hello World!\",\n      // only if formatter is set\n      \"formatted\": \"...\",\n      \"language\": \"plaintext\",\n      \"expires_at\": null\n    }\n  ],\n  \"token\": \"kiczgez33j7qkvqdg9f7ksrd8jk88wba\"\n}\n```\n\n---\n\n### Get a document (version)\n\nTo get a document you have to send a `GET` request to `/documents/{key}` or `/documents/{key}/versions/{version}`.\n\n| Query Parameter | Type                         | Description                                                                                        |\n|-----------------|------------------------------|----------------------------------------------------------------------------------------------------|\n| formatter?      | [formatter](#formatter-enum) | With which formatter to render the document                                                        |\n| style?          | style name                   | Which style to use for the formatter                                                               |\n| file?           | file name                    | Which file to return                                                                               |\n| language?       | [language](#language-enum)   | In which language the document should be rendered. Only works in combination with the `file` param |\n\nThe response will be a `200 OK` with the document content as `application/json` body.\n\n```json5\n{\n  \"key\": \"hocwr6i6\",\n  \"version\": 1,\n  \"files\": [\n    {\n      \"name\": \"main.go\",\n      \"content\": \"package main\\n\\nfunc main() {\\n    println(\\\"Hello World!\\\")\\n}\",\n      // only if formatter is set\n      \"formatted\": \"...\",\n      \"language\": \"Go\",\n      \"expires_at\": null\n    },\n    {\n      \"name\": \"untitled1\",\n      \"content\": \"Hello World!\",\n      // only if formatter is set\n      \"formatted\": \"...\",\n      \"language\": \"plaintext\",\n      \"expires_at\": null\n    }\n  ]\n}\n```\n\nIn case you provide a `file` query param the response will be like from [Get a document file](#get-a-document-version-file)\n\n---\n\n### Get a document (version) file\n\nTo get a document (version) file you have to send a `GET` request to `/documents/{key}/files/{fileName}`or `/documents/{key}/versions/{version}/files/{fileName}`\n\n| Query Parameter | Type                         | Description                                  |\n|-----------------|------------------------------|----------------------------------------------|\n| formatter?      | [formatter](#formatter-enum) | With which formatter to render the document. |\n| style?          | style name                   | Which style to use for the formatter         |\n| language?       | language name                | Which language to use for the formatter      |\n\nThe response will be a `200 OK` with the document content as `application/json` body.\n\n```json5\n{\n  \"name\": \"main.go\",\n  // only if withContent is set\n  \"content\": \"package main\\n\\nfunc main() {\\n    println(\\\"Hello World!\\\")\\n}\",\n  // only if formatter is set\n  \"formatted\": \"...\",\n  \"language\": \"Go\",\n  \"expires_at\": null\n}\n```\n\n---\n\n### Get a documents versions\n\nTo get a documents versions you have to send a `GET` request to `/documents/{key}/versions`.\n\n| Query Parameter | Type                         | Description                                        |\n|-----------------|------------------------------|----------------------------------------------------|\n| formatter?      | [formatter](#formatter-enum) | With which formatter to render the document        |\n| style?          | style name                   | Which style to use for the formatter               |\n| withContent?    | bool                         | If the content should be included in the response. |\n\nThe response will be a `200 OK` with the document content as `application/json` body.\n\n```json5\n[\n  {\n    \"key\": \"hocwr6i6\",\n    \"version\": 2,\n    \"files\": [\n      {\n        \"name\": \"main.go\",\n        // only if withContent is set\n        \"content\": \"package main\\n\\nfunc main() {\\n    println(\\\"Hello World!\\\")\\n}\",\n        // only if formatter is set\n        \"formatted\": \"...\",\n        \"language\": \"Go\",\n        \"expires_at\": null\n      },\n      {\n        \"name\": \"untitled1\",\n        // only if withContent is set\n        \"content\": \"Hello World!\",\n        // only if formatter is set\n        \"formatted\": \"...\",\n        \"language\": \"plaintext\",\n        \"expires_at\": null\n      }\n    ]\n  },\n  {\n    \"key\": \"hocwr6i6\",\n    \"version\": 1,\n    \"files\": [\n      {\n        \"name\": \"main.go\",\n        \"content\": \"package main\\n\\nfunc main() {\\n    println(\\\"Hello!\\\")\\n}\",\n        // only if formatter is set\n        \"formatted\": \"...\",\n        \"language\": \"Go\",\n        \"expires_at\": null\n      },\n      {\n        \"name\": \"untitled1\",\n        \"content\": \"Hello!\",\n        // only if formatter is set\n        \"formatted\": \"...\",\n        \"language\": \"plaintext\",\n        \"expires_at\": null\n      }\n    ]\n  }\n]\n```\n\n---\n\n### Update a document\n\nYou can update a document with a single file or multiple files. When updating a document with a single file you can\nsimply `PATCH` the content to `/documents/{key}`.\nWhen updating a document with multiple files you have to `PATCH` the content to `/documents/{key}`\nas `multipart/form-data`. See below for more information.\n\n#### Single file\n\nTo create a document with a single file you have to send a `PATCH` request to `/documents/{key}` with the `content` as\nbody.\n\n| Header              | Type      | Description                                               |\n|---------------------|-----------|-----------------------------------------------------------|\n| Content-Disposition | string    | The form \u0026 file name of the document.                     |\n| Content-Type?       | string    | The content type of the document.                         |\n| Language?           | string    | The language of the document.                             |\n| Authorization?      | string    | The update token of the document. (prefix with `Bearer `) |\n| Expires?            | Timestamp | When the document file should expire in RFC 3339 format   |\n\n| Query Parameter | Type                         | Description                                             |\n|-----------------|------------------------------|---------------------------------------------------------|\n| language?       | [language](#language-enum)   | The language of the document.                           |\n| formatter?      | [formatter](#formatter-enum) | With which formatter to render the document.            |\n| style?          | style name                   | Which style to use for the formatter                    |\n| expires?        | Timestamp                    | When the document file should expire in RFC 3339 format |\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```go\npackage main\n\nfunc main() {\n\tprintln(\"Hello World Updated!\")\n}\n```\n\n\u003c/details\u003e\n\n#### Multiple files\n\nTo update a document with multiple files you have to send a `PATCH` request to `/documents/{key}` with the `content`\nas `multipart/form-data` body.\nEach file has to be in its own part with the name `file-{index}`. The first file has to be named `file-0`, the\nsecond `file-1` and so on.\n\n| Header         | Type      | Description                                               |\n|----------------|-----------|-----------------------------------------------------------|\n| Authorization? | string    | The update token of the document. (prefix with `Bearer `) |\n| Expires?       | Timestamp | When the document file should expire in RFC 3339 format   |\n\n| Query Parameter | Type                         | Description                                             |\n|-----------------|------------------------------|---------------------------------------------------------|\n| formatter?      | [formatter](#formatter-enum) | With which formatter to render the document.            |\n| style?          | style name                   | Which style to use for the formatter                    |\n| expires?        | Timestamp                    | When the document file should expire in RFC 3339 format |\n\n| Part Header         | Type      | Description                                                                                  |\n|---------------------|-----------|----------------------------------------------------------------------------------------------|\n| Content-Disposition | string    | The form \u0026 file name of the document.                                                        |\n| Content-Type?       | string    | The content type of the document.                                                            |\n| Language?           | string    | The language of the document.                                                                |\n| Expires?            | Timestamp | When the document file should expire in RFC 3339 format, overwrites the query param \u0026 header |\n\n\u003cdetails\u003e\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```\nAuthorization: kiczgez33j7qkvqdg9f7ksrd8jk88wba\n```\n\n```multipart/form-data\n-----------------------------302370379826172687681786440755\nContent-Disposition: form-data; name=\"file-0\"; filename=\"main.go\"\nContent-Type: text/x-gosrc\nLanguage: Go\nExpires: 2023-10-10T10:10:10Z\n\npackage main\n\nfunc main() {\n\tprintln(\"Hello World!\")\n}\n-----------------------------302370379826172687681786440755\nContent-Disposition: form-data; name=\"file-1\"; filename=\"untitled1\"\nContent-Type: text/plain; charset=utf-8\n\nHello World!\n-----------------------------302370379826172687681786440755--\n```\n\n\u003c/details\u003e\n\nA successful request will return a `201 Created` response with a JSON body containing the document key and token to\nupdate the document.\n\n\u003e [!Note]\n\u003e The update token will not change after updating the document. You can use the same token to update the document again.\n\n```json5\n{\n  \"key\": \"hocwr6i6\",\n  \"version\": 2,\n  \"files\": [\n    {\n      \"name\": \"main.go\",\n      \"content\": \"package main\\n\\nfunc main() {\\n    println(\\\"Hello World Updated!\\\")\\n}\",\n      // only if formatter is set\n      \"formatted\": \"...\",\n      \"language\": \"Go\",\n      \"expires_at\": null\n    },\n    {\n      \"name\": \"untitled1\",\n      \"content\": \"Hello World Updated!\",\n      // only if formatter is set\n      \"formatted\": \"...\",\n      \"language\": \"plaintext\",\n      \"expires_at\": null\n    }\n  ],\n  \"token\": \"kiczgez33j7qkvqdg9f7ksrd8jk88wba\"\n}\n```\n\n---\n\n### Delete a document (version)\n\nTo delete a document you have to send a `DELETE` request to `/documents/{key}` or `/documents/{key}/versions/{version}` with the `token` as `Authorization`\nheader.\n\n| Header         | Type   | Description                                               |\n|----------------|--------|-----------------------------------------------------------|\n| Authorization? | string | The update token of the document. (prefix with `Bearer `) |\n\nA successful request will return a `204 No Content` response with an empty body or a `200 OK` with a JSON body\ncontaining the count of remaining document versions:\n\n```json5\n{\n  \"versions\": 1\n}\n```\n\n---\n\n### Share a document\n\nTo share a document you have to send a `POST` request to `/documents/{key}/share`.\n\n| Header         | Type   | Description                                               |\n|----------------|--------|-----------------------------------------------------------|\n| Authorization? | string | The update token of the document. (prefix with `Bearer `) |\n\n```json5\n{\n  \"permissions\": [\n    \"write\",\n    \"delete\",\n    \"share\"\n  ]\n}\n```\n\nA successful request will return a `200 OK` response with a JSON body containing the share token.\nYou can append the token to URLs like this: `https://xgob.in/{key}?token={token}` to make the frontend auto import the\ntoken for editing/deleting/sharing the document.\n\n```json5\n{\n  \"token\": \"kiczgez33j7qkvqdg9f7ksrd8jk88wba\"\n}\n```\n\n---\n\n### Document webhooks\n\nYou can listen for document changes using webhooks. The webhook will send a `POST` request to the specified url with the\nfollowing JSON body:\n\n```json5\n{\n  // the id of the webhook\n  \"webhook_id\": \"hocwr6i6\",\n  // the event which triggered the webhook (update or delete)\n  \"event\": \"update\",\n  // when the event was created\n  \"created_at\": \"2021-08-01T12:00:00Z\",\n  // the updated or deleted document\n  \"document\": {\n    // the key of the document\n    \"key\": \"hocwr6i6\",\n    // the version of the document\n    \"version\": 2,\n    // the files of the document\n    \"files\": [\n      {\n        \"name\": \"main.go\",\n        \"content\": \"package main\\n\\nfunc main() {\\n    println(\\\"Hello World Updated!\\\")\\n}\",\n        \"language\": \"Go\",\n        \"expires_at\": null\n      }\n    ]\n  }\n}\n```\n\nGobin will include the webhook secret in the `Authorization` header in the following format: `Secret {secret}`.\n\nWhen sending an event to a webhook fails gobin will retry it up to x times with an exponential backoff. The retry\nsettings can be configured in the config file.\nWhen an event fails to be sent after x retries, the webhook will be dropped.\n\n\u003e [!Important]\n\u003e Authorizing for the following webhook endpoints is done using the `Authorization` header in the following\n\u003e format: `Secret {secret}`.\n\n#### Create a document webhook\n\nTo create a webhook you have to send a `POST` request to `/documents/{key}/webhooks` with the following JSON body:\n\n```json5\n{\n  // the url to send a request to\n  \"url\": \"https://example.com/webhook\",\n  // the secret to include in the request\n  \"secret\": \"secret\",\n  // the events you want to receive\n  \"events\": [\n    // update event is sent when a document is updated. This includes content and language changes\n    \"update\",\n    // delete event is sent when a document is deleted\n    \"delete\"\n  ]\n}\n```\n\nA successful request will return a `200 OK` response with a JSON body containing the webhook.\n\n```json5\n{\n  // the id of the webhook\n  \"id\": 1,\n  // the url to send a request to\n  \"url\": \"https://example.com/webhook\",\n  // the secret to include in the request\n  \"secret\": \"secret\",\n  // the events you want to receive\n  \"events\": [\n    // update event is sent when a document is updated. This includes content and language changes\n    \"update\",\n    // delete event is sent when a document is deleted\n    \"delete\"\n  ]\n}\n```\n\n---\n\n#### Get a document webhook\n\nTo get a webhook you have to send a `GET` request to `/documents/{key}/webhooks/{id}` with the `Authorization` header.\n\nA successful request will return a `200 OK` response with a JSON body containing the webhook.\n\n```json5\n{\n  // the id of the webhook\n  \"id\": 1,\n  // the url to send a request to\n  \"url\": \"https://example.com/webhook\",\n  // the secret to include in the request\n  \"secret\": \"secret\",\n  // the events you want to receive\n  \"events\": [\n    // update event is sent when a document is updated. This includes content and language changes\n    \"update\",\n    // delete event is sent when a document is deleted\n    \"delete\"\n  ]\n}\n```\n\n---\n\n#### Update a document webhook\n\nTo update a webhook you have to send a `PATCH` request to `/documents/{key}/webhooks/{id}` with the `Authorization`\nheader and the following JSON body:\n\n\u003e [!Note]\n\u003e All fields are optional, but at least one field is required.\n\n```json5\n{\n  // the url to send a request to\n  \"url\": \"https://example.com/webhook\",\n  // the secret to include in the request\n  \"secret\": \"secret\",\n  // the events you want to receive\n  \"events\": [\n    // update event is sent when a document is updated. This includes content and language changes\n    \"update\",\n    // delete event is sent when a document is deleted\n    \"delete\"\n  ]\n}\n```\n\nA successful request will return a `200 OK` response with a JSON body containing the webhook.\n\n```json5\n{\n  // the id of the webhook\n  \"id\": 1,\n  // the url to send a request to\n  \"url\": \"https://example.com/webhook\",\n  // the secret to include in the request\n  \"secret\": \"secret\",\n  // the events you want to receive\n  \"events\": [\n    // update event is sent when a document is updated. This includes content and language changes\n    \"update\",\n    // delete event is sent when a document is deleted\n    \"delete\"\n  ]\n}\n```\n\n---\n\n#### Delete a document webhook\n\nTo delete a webhook you have to send a `DELETE` request to `/documents/{key}/webhooks/{id}` with the `Authorization`\nheader.\n\nA successful request will return a `204 No Content` response with an empty body.\n\n---\n\n### Other endpoints\n\n- `GET`/`HEAD` `/{key}/files/{filename}` - Get the content of a file in a document, query parameters are the same as\n  for `GET /documents/{key}`.\n- `GET`/`HEAD` `/{key}/versions/{version}/files/{filename}` - Get the content of a file in a document with a specific\n  version, query parameters are the same as for `GET /documents/{key}`.\n- `GET`/`HEAD` `/assets/theme.css?style={style}` - Get the css of a style, this is used for the syntax highlighting in\n  the frontend.\n- `GET`/`HEAD` `/{key}/preview` - Get the preview of a document, query parameters are the same as\n  for `GET /documents/{key}`.\n- `GET`/`HEAD` `/{key}/{version}/preview` - Get the preview of a document version, query parameters are the same as\n  for `GET /documents/{key}/versions/{version}`.\n- `GET`/`HEAD` `/raw/{key}` - Get the raw content of a document, query parameters are the same as\n  for `GET /documents/{key}`.\n- `GET`/`HEAD` `/raw/{key}/files/{filename}` - Get the raw content of a document file, query parameters are the same as\n  for `GET /documents/{key}`.\n- `GET`/`HEAD` `/raw/{key}/versions/{version}` - Get the raw content of a document version, query parameters are the\n  same as for `GET /documents/{key}/versions/{version}`.\n- `GET`/`HEAD` `/raw/{key}/versions/{version}/files/{filename}` - Get the raw content of a document version file, query\n  parameters are the same as for `GET /documents/{key}/versions/{version}`.\n- `GET` `/ping` - Get the status of the server.\n- `GET` `/debug` - Proof debug endpoint (only available in debug mode).\n- `GET` `/version` - Get the version of the server.\n\n---\n\n## License\n\ngobin is licensed under the [Apache License 2.0](/LICENSE).\n\n---\n\n## Contributing\n\nContributions are always welcome! Just open a pull request or discussion and I will take a look at it.\n\n---\n\n## Credits\n\n- [@Damon](https://github.com/day-mon) for helping me.\n\n---\n\n## Contact\n\n- [Discord](https://discord.gg/sD3ABd5)\n- [Twitter](https://twitter.com/topi314)\n- [Email](mailto:git@topi.wtf)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftopi314%2Fgobin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftopi314%2Fgobin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftopi314%2Fgobin/lists"}