Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a4d9291ee3 | ||
|
129101ac2e | ||
|
c3f133b211 | ||
|
4d3cac8851 | ||
|
574b7ce5f1 | ||
|
3873729ae7 | ||
|
4342fa4a56 | ||
|
aa1dff7015 | ||
|
69f19a1888 | ||
|
a5df50f43c | ||
|
9f8afe5054 | ||
|
edc99c3481 |
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@ -0,0 +1,4 @@
|
||||
__pycache__
|
||||
upload
|
||||
venv
|
||||
.git
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,7 +1,3 @@
|
||||
__pycache__
|
||||
upload
|
||||
env
|
||||
.env
|
||||
files
|
||||
/gomod2nix-template
|
||||
.direnv
|
||||
.pre-commit-config.yaml
|
||||
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -0,0 +1,13 @@
|
||||
FROM python:3.10-alpine
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY requirements.txt ./
|
||||
|
||||
RUN apk add --no-cache --update libmagic \
|
||||
&& pip install --no-cache-dir --upgrade pip \
|
||||
&& pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips='*'"]
|
131
README.md
131
README.md
@ -1,125 +1,62 @@
|
||||
# UpFast
|
||||
|
||||
The new and improwed version of upfast, now writen in GO!
|
||||
Simple tool for uploading and sharing files that you can self-host.
|
||||
|
||||
This one is gonna be a lot easier to deploy since all you need to do is download the upfast binary and run it in a directory.
|
||||
## How to host
|
||||
|
||||
To change the port or the ip adress to listen for you can use `-p` and `-a` options respectivelly.
|
||||
### Regular system
|
||||
|
||||
There is alse the `-d` option that will set what domain name upfast will return when returning the file path when uploaded.
|
||||
To run on a regular system I recommend to use a virtual environment to install the dependencies and run the project from there.
|
||||
|
||||
> example
|
||||
We will also be setting up an specific user to run the app as safe as possible with a systemd service file for startup
|
||||
|
||||
#### Installing
|
||||
```bash
|
||||
./upfast -p 8080 -a 0.0.0.0 -d https://upfast.cronyakatsuki.xyz
|
||||
./install.sh
|
||||
```
|
||||
|
||||
By default upfast listen's on 127.0.0.1 adress so if you wan't to access it outside your network you will either need to listen to 0.0.0.0 or use a reverse proxy. I recommend the usage of a reverse proxy.
|
||||
|
||||
## Note for the users of the python version
|
||||
|
||||
You will need to rename the `upload` folder to `files`, since I changed the naming a bit.
|
||||
|
||||
## Installation
|
||||
|
||||
This installation step's will also harden our installation and limit the size of how much can be uploaded at the same time so your system can't be flooded.
|
||||
|
||||
### Creating user
|
||||
#### Running
|
||||
```bash
|
||||
# Create upfast user with no login shell, no home directory and as a system user
|
||||
useradd --shell /usr/sbin/nologin --system -M upfast
|
||||
# Resctrict login to upfast user
|
||||
usermod -L upfast
|
||||
systemctl start upfast.service
|
||||
```
|
||||
|
||||
### Creating virtual filesystem
|
||||
|
||||
This is done in order to limit the amount of storage the upfast installation, so people can't storage dos you.
|
||||
|
||||
|
||||
Creating virtual filesystem to limit amount people can upload like chad's, using dd to create an empty file of size you choose.
|
||||
|
||||
Change the size output file to whatever you wan't and size to whatever you wan't.
|
||||
``` bash
|
||||
# make sure to first change into a directory where you wan't to save the virtual filesystem
|
||||
dd if=/dev/zero of=20gb bs=1M count=20480
|
||||
# make that file into a filesystem
|
||||
mkfs.ext4 20gb
|
||||
# create directory to mount the filesystem, I recommend /usr/local/upfast because that's where the included systemd service looks for upfast binary
|
||||
mkdir /usr/local/upfast
|
||||
# mounting the filesystem
|
||||
mount -o loop,rw /home/ivek/20gb /usr/local/upfast
|
||||
```
|
||||
Now to make it mount on system reboot we need to add this line to fstab.
|
||||
``` fstab
|
||||
/home/amir/mydatafile /usr/local/upfast ext4 loop,rw,usrquota,grpquota 0 0
|
||||
```
|
||||
### Downloading the binary
|
||||
#### Start on boot
|
||||
```bash
|
||||
# go to upfast's user's home and curl the upfast binary, make sure to run the next command's as root
|
||||
cd /usr/local/upfast
|
||||
curl -O https://code.cronyakatsuki.xyz/crony/UpFast/releases/download/v1.2/upfast
|
||||
chmod +x upfast
|
||||
systemctl enable upfast.service
|
||||
```
|
||||
|
||||
#### Updating
|
||||
To update, you only need to pull the changes
|
||||
```bash
|
||||
# Own the directory with upfast user and group
|
||||
chown upfast:upfast -R /usr/local/upfast
|
||||
chmod 700 /usr/local/upfast
|
||||
su upfast -c 'cd && git pull --no-rebase'
|
||||
```
|
||||
|
||||
## Updating
|
||||
### Docker
|
||||
|
||||
When new update of upfast come's out all you will need to change to upfast user and curl the new binary in it's home directory overwritting the old one like this.
|
||||
In the repo there is an included dockerfile to generate an image from the latest version of everything, to generate an image just run `docker build . -t upfast` (You need root privileges or to be in the docker group).
|
||||
|
||||
To run the docker container check the container id with `docker images` command.
|
||||
|
||||
> sample docker run command
|
||||
```bash
|
||||
su upfast
|
||||
cd /usr/local/upfast
|
||||
curl -O https://code.cronyakatsuki.xyz/crony/UpFast/releases/download/v1.2/upfast
|
||||
sudo docker run -p 8000:8000 -v ./upload:/usr/src/app/upload CONTAINER_ID
|
||||
```
|
||||
|
||||
## Startup automaticallly on system restart
|
||||
The sample command will need an upload directory, you can replace `./upload` with a different path to save uploaded stuff.
|
||||
|
||||
If you use the old version of upfast you just need to change values in the systemd service located `/etc/systemd/system/upfast.service`, otherwise create a file in that path with this content.
|
||||
### Nginx Proxy setup
|
||||
|
||||
> /etc/systemd/system/upfast.service
|
||||
|
||||
```systemd-service
|
||||
[Unit]
|
||||
Description=UpFast service
|
||||
Documentation=https://code.cronyakatsuki.xyz/crony/upfast
|
||||
|
||||
[Service]
|
||||
User=upfast
|
||||
Group=upfast
|
||||
WorkingDirectory=/usr/local/upfast/
|
||||
ExecStart=/usr/local/upfast/upfast -p 8000 -a 127.0.0.1 -d https://upfast.cronyakatsuki.xyz
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
This is an example nginx proxy config for http
|
||||
```bash
|
||||
cp ./upfast-nginx /etc/nginx/sites-available/upfast
|
||||
ln -sf /etc/nginx/sites-available/upfast /etc/nginx/sites-enabled/
|
||||
```
|
||||
|
||||
Remember to change the port if you need to, the listening adress and the domain name.
|
||||
|
||||
After that run `systemctl daemon-reload` as root or with sudo.
|
||||
|
||||
## Nginx proxy setup
|
||||
|
||||
To setup nginx as the reverse proxy you will need to add this server block to either the main nginx config file or in a separate file depending on your version of nginx.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name upfast.example.xyz;
|
||||
|
||||
location / {
|
||||
proxy_pass https://127.0.0.1:8000;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
> Load config
|
||||
```bash
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
# Contributions
|
||||
|
||||
Thanks [TLasT](https://craftmenners.men) on his help with testing and documentation.
|
||||
|
21
default.nix
21
default.nix
@ -1,21 +0,0 @@
|
||||
{ pkgs ? (
|
||||
let
|
||||
inherit (builtins) fetchTree fromJSON readFile;
|
||||
inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
|
||||
in
|
||||
import (fetchTree nixpkgs.locked) {
|
||||
overlays = [
|
||||
(import "${fetchTree gomod2nix.locked}/overlay.nix")
|
||||
];
|
||||
}
|
||||
)
|
||||
, buildGoApplication ? pkgs.buildGoApplication
|
||||
}:
|
||||
|
||||
buildGoApplication {
|
||||
pname = "upfast";
|
||||
version = "1.3";
|
||||
pwd = ./.;
|
||||
src = ./.;
|
||||
modules = ./gomod2nix.toml;
|
||||
}
|
210
flake.lock
210
flake.lock
@ -1,210 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709126324,
|
||||
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1703887061,
|
||||
"narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gomod2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705314449,
|
||||
"narHash": "sha256-yfQQ67dLejP0FLK76LKHbkzcQqNIrux6MFe32MMFGNQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "30e3c3a9ec4ac8453282ca7f67fca9e1da12c3e6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "gomod2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1709237383,
|
||||
"narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1704874635,
|
||||
"narHash": "sha256-YWuCrtsty5vVZvu+7BchAxmcYzTMfolSPP5io8+WYCg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3dc440faeee9e889fe2d1b4d25ad0f430d449356",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1704842529,
|
||||
"narHash": "sha256-OTeQA+F8d/Evad33JMfuXC89VMetQbsU4qcaePchGr4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "eabe8d3eface69f5bb16c18f8662a702f50c20d5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1708018599,
|
||||
"narHash": "sha256-M+Ng6+SePmA8g06CmUZWi1AjG2tFBX9WCXElBHEKnyM=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "5df5a70ad7575f6601d91f0efec95dd9bc619431",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"gomod2nix": "gomod2nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
31
flake.nix
31
flake.nix
@ -1,31 +0,0 @@
|
||||
{
|
||||
description = "A basic gomod2nix flake";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.gomod2nix.url = "github:nix-community/gomod2nix";
|
||||
inputs.gomod2nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.gomod2nix.inputs.flake-utils.follows = "flake-utils";
|
||||
inputs.pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, gomod2nix, pre-commit-hooks }:
|
||||
(flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
|
||||
# This has no effect on other platforms.
|
||||
callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage;
|
||||
in
|
||||
{
|
||||
packages.default = callPackage ./. {
|
||||
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
|
||||
};
|
||||
devShells.default = callPackage ./shell.nix {
|
||||
inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix;
|
||||
inherit pre-commit-hooks;
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
19
go.mod
19
go.mod
@ -1,19 +0,0 @@
|
||||
module cronyakatsuki/upfast
|
||||
|
||||
go 1.21.5
|
||||
|
||||
require github.com/labstack/echo/v4 v4.11.3
|
||||
|
||||
require (
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
)
|
47
go.sum
47
go.sum
@ -1,47 +0,0 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/labstack/echo/v4 v4.11.3 h1:Upyu3olaqSHkCjs1EJJwQ3WId8b8b1hxbogyommKktM=
|
||||
github.com/labstack/echo/v4 v4.11.3/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -1,39 +0,0 @@
|
||||
schema = 3
|
||||
|
||||
[mod]
|
||||
[mod."github.com/golang-jwt/jwt"]
|
||||
version = "v3.2.2+incompatible"
|
||||
hash = "sha256-LOkpuXhWrFayvVf1GOaOmZI5YKEsgqVSb22aF8LnCEM="
|
||||
[mod."github.com/labstack/echo/v4"]
|
||||
version = "v4.11.3"
|
||||
hash = "sha256-rdqH4HQB/vZyEsoymrEsQ8izjf0m7jhrIxbF6r5ZmBo="
|
||||
[mod."github.com/labstack/gommon"]
|
||||
version = "v0.4.0"
|
||||
hash = "sha256-xISAIJEu2xh0hoWsORbgjnz3rDK3ft3hrvmxt0wfHVw="
|
||||
[mod."github.com/mattn/go-colorable"]
|
||||
version = "v0.1.13"
|
||||
hash = "sha256-qb3Qbo0CELGRIzvw7NVM1g/aayaz4Tguppk9MD2/OI8="
|
||||
[mod."github.com/mattn/go-isatty"]
|
||||
version = "v0.0.19"
|
||||
hash = "sha256-wYQqGxeqV3Elkmn26Md8mKZ/viw598R4Ych3vtt72YE="
|
||||
[mod."github.com/valyala/bytebufferpool"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-I9FPZ3kCNRB+o0dpMwBnwZ35Fj9+ThvITn8a3Jr8mAY="
|
||||
[mod."github.com/valyala/fasttemplate"]
|
||||
version = "v1.2.2"
|
||||
hash = "sha256-gp+lNXE8zjO+qJDM/YbS6V43HFsYP6PKn4ux1qa5lZ0="
|
||||
[mod."golang.org/x/crypto"]
|
||||
version = "v0.14.0"
|
||||
hash = "sha256-UUSt3X/i34r1K0mU+Y5IzljX5HYy07JcHh39Pm1MU+o="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.17.0"
|
||||
hash = "sha256-qRawHWLSsJ06QNbLhUWPXGVSO1eaioeC9xZlUEWN8J8="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.13.0"
|
||||
hash = "sha256-/+RDZ0a0oEfJ0k304VqpJpdrl2ZXa3yFlOxy4mjW7w0="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.13.0"
|
||||
hash = "sha256-J34dbc8UNVIdRJUZP7jPt11oxuwG8VvrOOylxE7V3oA="
|
||||
[mod."golang.org/x/time"]
|
||||
version = "v0.3.0"
|
||||
hash = "sha256-/hmc9skIswMYbivxNS7R8A6vCTUF9k2/7tr/ACkcEaM="
|
39
install.sh
Executable file
39
install.sh
Executable file
@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
# quick install (and run) script for upfast using systemd
|
||||
if [ "$(id -u)" -ne 0 ]
|
||||
then
|
||||
echo "Please run as root"
|
||||
exit 1
|
||||
fi
|
||||
SIZE="5G"
|
||||
|
||||
UFDIR=${1:=/srv/upfast}
|
||||
USER=${1:=upfast}
|
||||
useradd --shell /bin/sh --system --home-dir $UFDIR $UFUSER ||
|
||||
exit 1
|
||||
mkdir -p "$UFDIR" || # dodge copying of skeletons
|
||||
exit 1
|
||||
|
||||
chown "$UFUSER:$UFUSER" "$UFDIR"
|
||||
chmod 700 "$UFDIR"
|
||||
|
||||
su "$UFUSER" -c"
|
||||
cd
|
||||
git clone https://code.cronyakatsuki.xyz/tlast/upfast . ||
|
||||
exit 1
|
||||
|
||||
python3 -m venv env
|
||||
. ./env/bin/activate ||
|
||||
exit1
|
||||
pip install -r requirements.txt ||
|
||||
exit 1
|
||||
|
||||
fallocate -l $SIZE storage
|
||||
mkfs.ext4 storage
|
||||
mount storage upload
|
||||
rm -fd /storage/*" ||
|
||||
exit 1
|
||||
|
||||
cp ./upfast.service /etc/systemd/system/upfast.service
|
||||
systemctl daemon-reload
|
247
main.go
247
main.go
@ -1,247 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"flag"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
)
|
||||
|
||||
//go:embed all:public/views/*.html
|
||||
var templates embed.FS
|
||||
|
||||
//go:embed all:static/*
|
||||
var static embed.FS
|
||||
|
||||
var domain string
|
||||
|
||||
func main() {
|
||||
t := &Template{
|
||||
templates: template.Must(template.ParseFS(templates, "public/views/*.html")),
|
||||
}
|
||||
|
||||
p := flag.Int("p", 1323, "upfast port to listen on.")
|
||||
a := flag.String("a", "127.0.0.1", "upfast ip to listen to")
|
||||
d := flag.String("d", "127.0.0.1", "upfast domain")
|
||||
flag.Parse()
|
||||
|
||||
host := *a + ":" + strconv.Itoa(*p)
|
||||
|
||||
domain = *d
|
||||
|
||||
e := echo.New()
|
||||
|
||||
e.Renderer = t
|
||||
e.Use(middleware.Logger())
|
||||
|
||||
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
||||
Root: "static",
|
||||
Browse: false,
|
||||
HTML5: true,
|
||||
Filesystem: http.FS(static),
|
||||
}))
|
||||
|
||||
files := "files"
|
||||
|
||||
if _, err := os.Stat(files); errors.Is(err, os.ErrNotExist) {
|
||||
err := os.Mkdir(files, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
e.Static("/files", files)
|
||||
|
||||
e.GET("/", Index)
|
||||
|
||||
e.POST("/", Upload)
|
||||
|
||||
e.GET("/files/", Files)
|
||||
|
||||
e.DELETE("/files/:file", Delete)
|
||||
|
||||
e.Logger.Fatal(e.Start(host))
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
templates *template.Template
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Name string
|
||||
FileType string
|
||||
Content string
|
||||
}
|
||||
|
||||
type FilesData struct {
|
||||
Files []File
|
||||
}
|
||||
|
||||
type IndexData struct {
|
||||
Host string
|
||||
}
|
||||
|
||||
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
return t.templates.ExecuteTemplate(w, name, data)
|
||||
}
|
||||
|
||||
func Index(c echo.Context) error {
|
||||
data := IndexData{
|
||||
Host: domain,
|
||||
}
|
||||
return c.Render(http.StatusOK, "index", data)
|
||||
}
|
||||
|
||||
func GetFileContentType(ouput *os.File) (string, error) {
|
||||
buf := make([]byte, 512)
|
||||
|
||||
_, err := ouput.Read(buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
contentType := http.DetectContentType(buf)
|
||||
|
||||
return contentType, nil
|
||||
}
|
||||
|
||||
func Files(c echo.Context) error {
|
||||
var files FilesData
|
||||
|
||||
filelist, err := os.ReadDir("./files/")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
UserAgent := c.Request().UserAgent()
|
||||
|
||||
log.Print(UserAgent)
|
||||
|
||||
match, err := regexp.MatchString("^curl/.*", UserAgent)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if match {
|
||||
out := ""
|
||||
for _, f := range filelist {
|
||||
out += f.Name() + "\n"
|
||||
}
|
||||
return c.String(http.StatusOK, out)
|
||||
}
|
||||
|
||||
var Type string
|
||||
var Content string
|
||||
|
||||
ImageMatch := regexp.MustCompile("^image/.*")
|
||||
VideoMatch := regexp.MustCompile("^video/.*")
|
||||
JsonMatch := regexp.MustCompile("application/json")
|
||||
TextMatch := regexp.MustCompile("^text/.*")
|
||||
|
||||
for _, f := range filelist {
|
||||
filePath := "files/" + f.Name()
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
contentType, err := GetFileContentType(file)
|
||||
|
||||
switch {
|
||||
case ImageMatch.MatchString(contentType):
|
||||
Type = "image"
|
||||
Content = ""
|
||||
case VideoMatch.MatchString(contentType):
|
||||
Type = "video"
|
||||
Content = ""
|
||||
case JsonMatch.MatchString(contentType):
|
||||
Type = "text"
|
||||
b, _ := os.ReadFile(filePath)
|
||||
Content = string(b)
|
||||
case TextMatch.MatchString(contentType):
|
||||
Type = "text"
|
||||
b, _ := os.ReadFile(filePath)
|
||||
Content = string(b)
|
||||
default:
|
||||
Type = "else"
|
||||
Content = ""
|
||||
}
|
||||
|
||||
log.Print(contentType)
|
||||
files.Files = append(
|
||||
files.Files,
|
||||
File{Name: f.Name(), FileType: Type, Content: Content},
|
||||
)
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "files", files)
|
||||
}
|
||||
|
||||
func Upload(c echo.Context) error {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat("files/" + file.Filename); err == nil {
|
||||
return c.String(http.StatusOK, "A file with the same name already exist's on the server.\n")
|
||||
}
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst, err := os.Create("files/" + file.Filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if _, err = io.Copy(dst, src); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileUrl := domain + "/files/" + file.Filename + "\n"
|
||||
|
||||
UserAgent := c.Request().UserAgent()
|
||||
|
||||
log.Print(UserAgent)
|
||||
|
||||
match, err := regexp.MatchString("^curl/.*", UserAgent)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if match {
|
||||
return c.String(http.StatusOK, fileUrl)
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, "File uploaded at url: <strong>"+fileUrl+"</strong>")
|
||||
}
|
||||
|
||||
func Delete(c echo.Context) error {
|
||||
file := c.Param("file")
|
||||
filePath := "files/" + file
|
||||
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return c.String(http.StatusOK, "That file doesn't exist on the server\n")
|
||||
}
|
||||
|
||||
err := os.Remove(filePath)
|
||||
if err != nil {
|
||||
return c.String(http.StatusOK, "Error while deleting "+file+"\n")
|
||||
}
|
||||
return c.String(http.StatusOK, "Deleted file from server\n")
|
||||
}
|
113
main.py
Normal file
113
main.py
Normal file
@ -0,0 +1,113 @@
|
||||
import shutil, magic, re
|
||||
from typing import Annotated, Union
|
||||
from os import listdir, remove
|
||||
from os.path import abspath, dirname, exists
|
||||
from fastapi import FastAPI, Request, UploadFile, File, Header
|
||||
from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
|
||||
# file class
|
||||
class _File:
|
||||
def __init__(self, name: str, fileType: str, content=None):
|
||||
self.name = name
|
||||
self.fileType = fileType
|
||||
self.content = content if content is not None else ''
|
||||
|
||||
|
||||
# File list generator with filetype assignemt and content reading for previews
|
||||
def file_list_generator(path: str, file_list: list[str]):
|
||||
files = []
|
||||
for file in file_list:
|
||||
file_type = magic.from_file(f"{path}{file}", mime=True)
|
||||
if re.search("^image/.*", file_type):
|
||||
_file = _File(file, "image")
|
||||
elif re.search("^video/.*", file_type):
|
||||
_file = _File(file, "video")
|
||||
elif re.search("^text/.*", file_type):
|
||||
with open(f"{path}{file}") as f:
|
||||
content = f.read()
|
||||
_file = _File(file, "text", content)
|
||||
elif file_type == 'application/json':
|
||||
with open(f"{path}{file}") as f:
|
||||
content = f.read()
|
||||
_file = _File(file, "text", content)
|
||||
else:
|
||||
_file = _File(file, "else")
|
||||
files.append(_file)
|
||||
return files
|
||||
|
||||
|
||||
# Load jinja2 for templating
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
# Create fastapi template
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
# Mount all uploaded files at the /files path
|
||||
app.mount("/files", StaticFiles(directory="upload"), name='upload')
|
||||
|
||||
|
||||
# Mount static files like css
|
||||
app.mount("/static", StaticFiles(directory="static"), name='static')
|
||||
|
||||
|
||||
# show the homepage when just getting the website
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request, user_agent: Annotated[Union[str, None], Header()] = None):
|
||||
if re.search("^curl/.*", str(user_agent)):
|
||||
return PlainTextResponse("It fucking works!\n")
|
||||
else:
|
||||
context = {'request': request, "user_agent": user_agent}
|
||||
return templates.TemplateResponse("index.html", context)
|
||||
|
||||
|
||||
# get the file user want's to upload and save it
|
||||
@app.post("/")
|
||||
async def upload( request: Request, file: UploadFile = File(...), user_agent: Annotated[Union[str, None], Header()] = None):
|
||||
file_location = f"upload/{file.filename}"
|
||||
if exists(file_location):
|
||||
context = f'''File: "{request.url._url}files/{file.filename}" already exists on the server!
|
||||
Please rename the file or delete the one on the server\n'''
|
||||
return PlainTextResponse(context)
|
||||
with open(file_location, "wb+") as file_object:
|
||||
shutil.copyfileobj(file.file, file_object)
|
||||
if re.search("^curl/.*", str(user_agent)):
|
||||
return PlainTextResponse(f"{request.url._url}files/{file.filename}\n")
|
||||
else:
|
||||
return {"filename": file.filename, "path": f"{request.url._url}files/{file.filename}"}
|
||||
|
||||
|
||||
# show the files page with the list of all files or if run with curl list all files in url format
|
||||
@app.get("/files")
|
||||
async def files( request: Request, user_agent: Annotated[Union[str, None], Header()] = None):
|
||||
path = f"{dirname(abspath(__file__))}/upload/"
|
||||
file_list = listdir(path)
|
||||
files = file_list_generator(path, file_list)
|
||||
context = { 'request': request, "files": files}
|
||||
if re.search("^curl/.*", str(user_agent)):
|
||||
context = ""
|
||||
for file in files:
|
||||
context += f"{request.url._url}/{file.name}\n"
|
||||
return PlainTextResponse(f"{context}")
|
||||
else:
|
||||
return templates.TemplateResponse("files.html", context)
|
||||
|
||||
|
||||
# delete specific file when getting this request
|
||||
@app.get("/delete/{file}")
|
||||
async def delete(request: Request, file: str, user_agent: Annotated[Union[str, None], Header()] = None):
|
||||
file_path = f"{dirname(abspath(__file__))}/upload/{file}"
|
||||
if exists(file_path):
|
||||
remove(file_path)
|
||||
if re.search("^curl/.*", str(user_agent)):
|
||||
return PlainTextResponse(f"file {file} deleted from the server\n")
|
||||
else:
|
||||
return RedirectResponse(request.url_for('files'))
|
||||
if re.search("^curl/.*", str(user_agent)):
|
||||
return PlainTextResponse(f"file {file} doesn't exist on the server\n")
|
||||
else:
|
||||
return RedirectResponse(request.url_for('files'))
|
@ -1,32 +0,0 @@
|
||||
{{define "files"}}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Files - UpFast</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="/css/files.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>File List</h1>
|
||||
{{range .Files}}
|
||||
<hr />
|
||||
<div class="file">
|
||||
{{if eq .FileType "image"}}
|
||||
<img src="/files/{{.Name}}" alt="{{.Name}}" />
|
||||
{{else if eq .FileType "video"}}
|
||||
<video controls src="/files/{{.Name}}" />
|
||||
{{else if eq .FileType "text"}}
|
||||
<pre>{{.Content}}</pre>
|
||||
{{end}}
|
||||
<div class="info">
|
||||
<a href="/files/{{.Name}}" download>{{.Name}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
@ -1,55 +0,0 @@
|
||||
{{define "index"}}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>UpFast</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="/css/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>UpFast: Minimal file upload and sharing tool</h1>
|
||||
|
||||
<p>
|
||||
<a href="https://0x0.st">0x0.st</a> Influenced file upload and sharing
|
||||
tool
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To upload files use curl:
|
||||
<code>curl -F "file=@/path/to/file" "{{.Host}}"</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Or for <a href="https://poggerer.xyz/">Tulg</a> on a windows pc ;)
|
||||
|
||||
<form action="/" method="post" enctype="multipart/form-data">
|
||||
File: <input type="file" name="file"> <input type="submit" value="submit">
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To delete a file using curl:
|
||||
<code>curl -X DELETE "{{.Host}}/files/:file"</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To get of all files hosted on the instance check
|
||||
<a href="/files">files</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Created and maintained by
|
||||
<a href="https://cronyakatsuki.xyz">Crony Akatsuki</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Code is hosted on
|
||||
<a href="https://code.cronyakatsuki.xyz/crony/upfast">https://code.cronyakatsuki.xyz/crony/upfast</a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
14
requirements.txt
Normal file
14
requirements.txt
Normal file
@ -0,0 +1,14 @@
|
||||
anyio==3.6.2
|
||||
click==8.1.3
|
||||
fastapi==0.95.0
|
||||
h11==0.14.0
|
||||
idna==3.4
|
||||
Jinja2==3.1.2
|
||||
MarkupSafe==2.1.2
|
||||
pydantic==1.10.7
|
||||
python-magic==0.4.27
|
||||
python-multipart==0.0.6
|
||||
sniffio==1.3.0
|
||||
starlette==0.26.1
|
||||
typing_extensions==4.5.0
|
||||
uvicorn==0.21.1
|
32
shell.nix
32
shell.nix
@ -1,32 +0,0 @@
|
||||
{ pkgs ? (
|
||||
let
|
||||
inherit (builtins) fetchTree fromJSON readFile;
|
||||
inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
|
||||
in
|
||||
import (fetchTree nixpkgs.locked) {
|
||||
overlays = [
|
||||
(import "${fetchTree gomod2nix.locked}/overlay.nix")
|
||||
];
|
||||
}
|
||||
)
|
||||
, mkGoEnv ? pkgs.mkGoEnv
|
||||
, gomod2nix ? pkgs.gomod2nix
|
||||
, pre-commit-hooks
|
||||
}:
|
||||
|
||||
let
|
||||
goEnv = mkGoEnv { pwd = ./.; };
|
||||
pre-commit-check = pre-commit-hooks.lib.${pkgs.system}.run {
|
||||
src = ./.;
|
||||
hooks = {
|
||||
gofmt.enable = true;
|
||||
};
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
inherit (pre-commit-check) shellHook;
|
||||
packages = [
|
||||
goEnv
|
||||
gomod2nix
|
||||
];
|
||||
}
|
@ -1,66 +1,66 @@
|
||||
html {
|
||||
padding-bottom: 10%;
|
||||
padding-bottom: 10%;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
background: #303446;
|
||||
color: #c6d0f5;
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
background: #81a1c1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 250%;
|
||||
text-align: center;
|
||||
font-size: 250%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 40px;
|
||||
color: #626880;
|
||||
margin: 40px;
|
||||
color: #2e3440;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
max-height: 800px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
max-height: 800px;
|
||||
}
|
||||
|
||||
video {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
max-height: 800px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
max-height: 800px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #f2d5cf;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
color: #ebcb8b;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #292c3c;
|
||||
font-size: 1am;
|
||||
padding: 10px;
|
||||
max-width: 800;
|
||||
max-height: 10rem;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
border: none;
|
||||
border-radius: 1rem;
|
||||
background: #2e3440;
|
||||
font-size: 1am;
|
||||
padding: 10px;
|
||||
max-width: 800;
|
||||
max-height: 800;
|
||||
overflow-y: scroll;
|
||||
overflow-x: scroll;
|
||||
border: none;
|
||||
border-radius: 1rem;
|
||||
color: #88c0d0;
|
||||
}
|
||||
|
||||
.file {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: block;
|
||||
text-align: center;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -1,29 +1,30 @@
|
||||
body {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
background: #303446;
|
||||
color: #c6d0f5;
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
background: #81a1c1;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 250%;
|
||||
text-align: center;
|
||||
font-size: 250%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #f2d5cf;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
color: #ebcb8b;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #414559;
|
||||
border-radius: 10px;
|
||||
font-size: 105%;
|
||||
padding: 2px;
|
||||
background: #88c0d0;
|
||||
border-radius: 10px;
|
||||
font-size: 105%;
|
||||
font-style: italic;
|
||||
padding: 2px;
|
||||
}
|
||||
|
31
templates/files.html
Normal file
31
templates/files.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="{{ url_for('static', path='/css/files.css') }}" rel="stylesheet">
|
||||
<title>UpFast: File List</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>File List</h1>
|
||||
|
||||
{% for file in files %}
|
||||
<hr>
|
||||
<div class="file">
|
||||
{% if file.fileType == "image" %}
|
||||
<img src="/files/{{ file.name }}">
|
||||
{% elif file.fileType == "video" %}
|
||||
<video controls src="/files/{{ file.name }}"></video>
|
||||
{% elif file.fileType == "text" %}
|
||||
<pre>{{ file.content }}</pre>
|
||||
{% endif %}
|
||||
<div class="info">
|
||||
<a href="/files/{{ file.name }}" download>{{ file.name }}</a>
|
||||
<a href="/delete/{{ file.name }}">delete!</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</body>
|
||||
</html>
|
26
templates/index.html
Normal file
26
templates/index.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="{{ url_for('static', path='/css/style.css') }}" rel="stylesheet">
|
||||
<title>UpFast</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>UpFast: Minimal file upload and sharing tool</h1>
|
||||
|
||||
<p><a href="https://0x0.st">0x0.st</a> Influenced file upload and sharing tool</p>
|
||||
|
||||
<p>To upload files use curl: <code>curl -F "file=@/path/to/file" "{{ request.url }}"</code></p>
|
||||
|
||||
<p>To delete a file using curl: <code>curl "{{ request.url }}delete/file_name"</code></p>
|
||||
|
||||
<p>To get of all files hosted on the instance check <a href="/files">files</a></p>
|
||||
|
||||
<p>Created and maintained by <a href="https://cronyakatsuki.xyz">Crony Akatsuki</a></p>
|
||||
|
||||
<p>Code is hosted on <a href="https://code.cronyakatsuki.xyz/crony/upfast">https://code.cronyakatsuki.xyz/crony/upfast</a></p>
|
||||
|
||||
</body>
|
||||
</html>
|
15
upfast-nginx
Normal file
15
upfast-nginx
Normal file
@ -0,0 +1,15 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name upfast.example.com ;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
proxy_buffering off;
|
||||
}
|
||||
}
|
13
upfast.service
Normal file
13
upfast.service
Normal file
@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=UpFast service
|
||||
Documentation=https://code.cronyakatsuki.xyz/crony/upfast
|
||||
|
||||
[Service]
|
||||
User=upfast
|
||||
Group=upfast
|
||||
WorkingDirectory=/usr/local/upfast/
|
||||
ExecStart=/usr/local/upfast/env/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --proxy-headers --forwarded-allow-ips='*'
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
Loading…
Reference in New Issue
Block a user