Zapaste is a pastebin-like service built using Zig and the Zap web framework.
This project was my first experience with the Zig programming language. As I was learning the syntax and coding styles throughout the development process, please excuse any inconsistencies regarding Zig's idiomatic practices or coding standards.
- Paginated retrieval of public pastes.
- Password-protected paste access via POST.
- Support for
rawtext output via query parameters. - Random pastebin name.
- File upload and download.
- The same file is stored only once.
- Download file by CURL directly.
- Automatic clean outdate pastes.
- Automatic clean useless files.
- RESTful API.
- Self-hosted.
- Q: Does Zapaste work on Windows?
- Can not directly work on Windows, but it can work on wsl2 or a docker container.
- Q: Does support upload/download files?
- Basic support now, you can view swagger api.
- Q: Does Zapaste only provided HTTP API?
- Yes, Zapaste only provides an HTTP API, it does not include any visual interface. If you need a visual interface, you can use Zapaste UI.
The Zapaste project itself does not include any graphical user interface, because, according to my personal vision for it, Zapaste should be a backend software server, only providing HTTP API interfaces.
Of course, if you need a graphical user interface, you have the following options.
Zapaste UI is a Material Design 3-style web front-end visual interface built with Vite and React, and it also supports mobile device layouts.
When building the Zapaste Docker image, an additional Docker image with the tag embedded-ui is built. This image integrates Zapaste UI and automatically starts Zapaste's embedded web server.
You only need to run the following command to run it, and then you can access the visual interface directly through your browser at http://localhost:3000:
docker pull ghcr.io/smileyik/zapaste:embedded-ui
docker run -p 3000:3000 -it --rm ghcr.io/smileyik/zapaste:embedded-uiMake sure you are installed zig at first.
git clone https://github.com/SmileYik/zapaste.git
cd zapaste
zig build -Doptimize=ReleaseFastYou can pick one from github action. If file all outdate in github action, then you can clone this repository then the github action will be restart.
You can simplify use ./zapaste command to run service with default configuration.
if you wanna use your custom configuration, you can use:
./zapaste /path/to/your/config.jsonIf zapaste cannot access file /path/to/your/config.json then it will fallback to use default configuration.
You can also pull docker image and run it.
This is a quick start example, will using default configuration to launch zapaste service.
docker pull ghcr.io/smileyik/zapaste:latest
docker run -p 3000:3000 -it --rm ghcr.io/smileyik/zapaste:latestThere is another example about load your custom configuration:
docker pull ghcr.io/smileyik/zapaste:latest
docker run -it --rm \
-p 3000:3000 \
-v $(pwd)/config.json:/app/config.json \
ghcr.io/smileyik/zapaste:latestIf you wanna mount data folder to your local machine, then you can modify your config.json:
{
"work_dir": "/data/",
}then launch docker container:
docker pull ghcr.io/smileyik/zapaste:latest
docker run -it --rm \
-p 3000:3000 \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/data:/data \
ghcr.io/smileyik/zapaste:latestYou can find swagger configuration in resources folder.
Or you can dirictly goto online swagger editor!
Example configuration file: default configuration
-
dao_type: currently only supportSqlite. -
sqlite_options: only enabled whendao_typeisSqlite-
memory_mode: enable memory mode, the all data will store in memory, and will lose after shutdown service. default:false -
pool_size: sqlite pool size, if you are usingmemory_mode, it must be1. default2 -
shared_cache: share cache, defaultfalse, -
pragma: a key-value map, as same as run sql when sqlite connection be created:SET PRAGMA KEY = VALUE, default isnull
-
-
swagger: swagger config-
enable: enable swagger or not, if you turn it on, you can accesshttp://yourhostname:port/swaggerto access swagger page, defaultfalse. -
swagger_config_path: swagger config file path (Relative path, relative towork_dir, if you set/openapi.yml, then will access file${work_dir}/openapi.yml), if file is not found then will return embed api config, default/openapi.yml. -
swagger_index_path: swagger html file path, likeswagger_config_path, defaultnull
-
-
web: static web config-
enable: enable static web server, defaultfalse -
default_file: lookup default file when access folder url(like/or/admin/) , defaultindex.html -
prefix: static web request path prefix, default/ -
static_path: static resources path (Relative path, relative towork_dir, if you setstatic, then will access directory${work_dir}/static; if you setweb/staticthen${work_dir}/web/static). defaultstatic -
compression_static: automatic send compressed static file if it's exists, will try to find brotli at first (file name suffix is .br), then gzip (file name suffix is .gz), orignal file is the end. defaultfalse
-
-
auth: Global login authentication configuration-
auth_type: authentication type, There has two auth type:NoneandBasic.Nonemeans no authenticator protect;Basicprovides basic protection. defaultNone -
skip_auth_path: this is a key-value map, to control which url path could skip auth verify. The key is url path, you can use:xxxto match one variable path, e.g.,/api/paste/:namecan matches/api/paste/abcor/api/paste/defbut not matches/api/paste/abc/delete; the value is allow which HTTP method skip auth verify, you cann special multiple HTTP methods by delimiters,. e.g.:"/api/paste/:name": "GET,POST"means allow everyone access url/api/paste/xxxxxby HTTP methodGETorPOST. defaultnull -
basic: Configuration forBasicauth_typeusers: a key-value map, the key is username, the value is password, defaultnull
-
-
work_dir: the directory data stored. default./. -
upload_dir: the uploaded file stored (Relative path, relative towork_dir, if you setuploads, then will access directory${work_dir}/uploads). defaultuploads -
bind_port: bind service port, default3000 -
max_clients: max clients, default1000000, -
enable_log: enable log, defaulttrue, -
threads: how many threads handle http request. default2, -
workers: how many workers, cannot share memory between workers. default1 -
paste_clean_frequency: the frequency of clean outdate pastes (The number of views reaches the set value or the content expires.), time unit is milliseconds, default3600000(every 1 hour) -
file_clean_frequency: the frequency of clean useless files (Files that were not referenced by the pastes), time unit is milliseconds, default3600000(every 1 hour) -
max_body_size: the max http request body payload, unit byte, default52428800(50MB) -
custom_headers: a key-value map, it's will set custom headers for every http request. defaultnull -
cors_headers: a key-value map. it's will set headers for everyOPTIONShttp requset, defaultnull
you can simply turn it on. then you can access http://your-host-name:your-port/swagger/.
{
"swagger": {
"enable": true
}
}By the way, the swagger configuration, you can find it in resources folder.
Assume your current directory is as follows:
tree .
.
├── config.json
├── static
│ ├── hello.txt
│ └── index.html
└── zapaste
1 directory, 4 files./config.jsoncontent
{
"work_dir": "./",
"web": {
"enable": true,
"default_file": "index.html",
"prefix": "/",
"static_path": "static"
}
}./static/hello.txtand./static/index.htmlhas same content
Hello Zapaste!
after that, you can simple run ./zapaste config.json in current directory, then you can access and see Hello Zapaste!:
http://your-host-name:your-port/http://your-host-name:your-port/http://your-host-name:your-port/hello.txt
You need add CROS headers to your http response, to achieve this, you can configure custom_headers and cors_headers.
For example, you can allow all CROS OPTIONS request by below configuration:
{
"custom_headers": {
"Access-Control-Allow-Origin": "*"
},
"cors_headers": {
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization"
}
}You can configure auth in config.json to let only authenticated users can perform full operations in Zapaste service.
For now only one authenticator is available, that is Basic authenticator.
Basic authenticator is a simple authenticator, it will verify authorization header in every http request, and get your username and password from authorization header, after then compare your password with your sets in configuration, if the verification is successful then service will continue to handle next step operation, else then will block the request.
authorization header format is a base64 encoded string: $username:$password. for example: if username abc, password 123456 then the authorization should be Basic YWJjOjEyMzQ1Ng==.
You can configure configuration to add users. there is a config.json example, in this example, we use Basic authenticator, and add 2 users: abc and tom. password of user abc is 123456; tom's is password. also i configured skip_auth_path to let everyone can access swagger, view public pastes list, view exists pastes (both locked and unlocked) and download files (as same as pastes, both include locked and unlocked).
"auth": {
"auth_type": "Basic",
"basic": {
"users": {
"abc": "123456",
"tom": "password"
}
},
"skip_auth_path": {
"/swagger": "GET",
"/swagger/:any": "GET",
"/api/paste": "GET",
"/api/paste/:name": "GET,POST",
"/api/paste/:name/file/name/:filename": "GET,POST"
}
}