forked from crony/UpFast
Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
37f1c553b4 | |||
34270dfea0 | |||
0b97c205c5 | |||
a54ca0623a | |||
f5b1e6b77d | |||
fc73213a6c | |||
9d9acbe7d3 | |||
821c0c91c8 | |||
b87a56623b | |||
f914d57a74 | |||
7365240e35 | |||
fc098ec7c6 | |||
61e20c9b06 | |||
82cca8084f | |||
21dc8f1fa1 | |||
7f06794dc2 | |||
f8cc04f18e | |||
bc26af8a97 | |||
dd0e1b63cf | |||
8b1337bdc1 | |||
8c1ab0b53b | |||
2c1342a20e | |||
66fd12473e | |||
e0b51e3c96 | |||
79b65dfb34 | |||
8e9ffa2405 | |||
7beb2ae538 | |||
59104bcb12 | |||
c60056d7ce | |||
8113202f5f | |||
16761d4382 | |||
c2e09996a2 | |||
d68add7803 | |||
912f0049af | |||
34f04d3380 | |||
929df87147 | |||
2edeff726d | |||
cd4592d257 | |||
677ac4f427 | |||
6240310747 | |||
08df1ee3dc | |||
212bf7ca75 | |||
7711c4c552 | |||
8585fe9552 | |||
4a0136c9a3 | |||
8b7b009941 | |||
d85637afcb | |||
47ac321d62 | |||
b5fb6bcd09 | |||
4893dd6bed | |||
6b38fcc0e3 | |||
3c3f05d14b | |||
8991c06849 | |||
f78cf1c68c | |||
094c00eab1 | |||
5c6448f96a | |||
b407949d0a | |||
|
e9563be67a | ||
|
3d1ba3bb5c | ||
|
17c172a73d | ||
|
76963bcd88 | ||
|
1987a3fbef | ||
|
fb7b89a101 | ||
|
972f55f38d | ||
422baeaae2 | |||
|
b376d91e21 | ||
|
4fcd9f5157 | ||
|
da70b22fdf | ||
|
7c593f2fc2 | ||
|
8fdc68b441 |
@ -1,4 +0,0 @@
|
|||||||
__pycache__
|
|
||||||
upload
|
|
||||||
venv
|
|
||||||
.git
|
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,3 +1,7 @@
|
|||||||
__pycache__
|
|
||||||
upload
|
upload
|
||||||
venv
|
env
|
||||||
|
.env
|
||||||
|
files
|
||||||
|
/gomod2nix-template
|
||||||
|
.direnv
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
13
Dockerfile
13
Dockerfile
@ -1,13 +0,0 @@
|
|||||||
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='*'"]
|
|
158
README.md
158
README.md
@ -1,49 +1,89 @@
|
|||||||
# UpFast
|
# UpFast
|
||||||
|
|
||||||
Simple tool for uploading and sharing files that you can self-host.
|
The new and improwed version of upfast, now writen in GO!
|
||||||
|
|
||||||
## How to 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.
|
||||||
|
|
||||||
### Regular system
|
To change the port or the ip adress to listen for you can use `-p` and `-a` options respectivelly.
|
||||||
|
|
||||||
To run on a regular system I recommend to use a virtual environment to install the dependencies and run the project from there.
|
There is alse the `-d` option that will set what domain name upfast will return when returning the file path when uploaded.
|
||||||
|
|
||||||
We will also be setting up an specific user to run the app as safe as possible with a systemd service file for startup
|
> example
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create the user (You can specify a different home-dir)
|
./upfast -p 8080 -a 0.0.0.0 -d https://upfast.cronyakatsuki.xyz
|
||||||
sudo useradd --shell /bin/bash --system \
|
|
||||||
--home-dir "/usr/local/upfast" -m upfast
|
|
||||||
|
|
||||||
# Change to upfast user and go to upfast dir
|
|
||||||
su upfast
|
|
||||||
cd
|
|
||||||
|
|
||||||
# Clone the project directly into upfast-src directory
|
|
||||||
git clone https://code.cronyakatsuki.xyz/crony/upfast .
|
|
||||||
|
|
||||||
# Create the virtual environment and load it for user on default
|
|
||||||
python3 -m venv venv
|
|
||||||
# Activate the virtual environment
|
|
||||||
source venv/bin/activate
|
|
||||||
# Install all the requirements
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
# Load environment by default
|
|
||||||
echo "source /usr/local/upfast/venv/bin/activate" > "/usr/local/upfast/.profile"
|
|
||||||
|
|
||||||
# create the upload directory
|
|
||||||
mkdir upload
|
|
||||||
|
|
||||||
# UpFast go brrr
|
|
||||||
uvicorn main:app --host 127.0.0.1 --port 8000 --proxy-headers --forwarded-allow-ips='*'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Autostart with systemd
|
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.
|
||||||
|
|
||||||
To autostart with systemd you will need to create a systemd .service file in path `/etc/systemd/system/upfast.service` and add this content to it.
|
## Note for the users of the python version
|
||||||
|
|
||||||
```systemd
|
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
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
# Own the directory with upfast user and group
|
||||||
|
chown upfast:upfast -R /usr/local/upfast
|
||||||
|
chmod 700 /usr/local/upfast
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
su upfast
|
||||||
|
cd /usr/local/upfast
|
||||||
|
curl -O https://code.cronyakatsuki.xyz/crony/UpFast/releases/download/v1.2/upfast
|
||||||
|
```
|
||||||
|
|
||||||
|
## Startup automaticallly on system restart
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
> /etc/systemd/system/upfast.service
|
||||||
|
|
||||||
|
```systemd-service
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=UpFast service
|
Description=UpFast service
|
||||||
Documentation=https://code.cronyakatsuki.xyz/crony/upfast
|
Documentation=https://code.cronyakatsuki.xyz/crony/upfast
|
||||||
@ -52,54 +92,34 @@ Documentation=https://code.cronyakatsuki.xyz/crony/upfast
|
|||||||
User=upfast
|
User=upfast
|
||||||
Group=upfast
|
Group=upfast
|
||||||
WorkingDirectory=/usr/local/upfast/
|
WorkingDirectory=/usr/local/upfast/
|
||||||
ExecStart=/usr/local/upfast/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --proxy-headers --forwarded-allow-ips='*'
|
ExecStart=/usr/local/upfast/upfast -p 8000 -a 127.0.0.1 -d https://upfast.cronyakatsuki.xyz
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
```
|
|
||||||
Finally run following commmands.
|
|
||||||
|
|
||||||
```bash
|
[Install]
|
||||||
# refresh services
|
WantedBy=multi-user.target
|
||||||
sudo systemctl daemon-reload
|
|
||||||
# enable the service
|
|
||||||
sudo systemctl enable upfast.service
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker
|
Remember to change the port if you need to, the listening adress and the domain name.
|
||||||
|
|
||||||
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).
|
After that run `systemctl daemon-reload` as root or with sudo.
|
||||||
|
|
||||||
To run the docker container check the container id with `docker images` command.
|
## Nginx proxy setup
|
||||||
|
|
||||||
> sample docker run command
|
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.
|
||||||
```bash
|
|
||||||
sudo docker run -p 8000:8000 -v ./upload:/usr/src/app/upload CONTAINER_ID
|
|
||||||
```
|
|
||||||
|
|
||||||
The sample command will need an upload directory, you can replace `./upload` with a different path to save uploaded stuff.
|
|
||||||
|
|
||||||
### Nginx Proxy setup
|
|
||||||
|
|
||||||
This is an example nginx proxy config for http
|
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
server {
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
|
||||||
listen 80;
|
server_name upfast.example.xyz;
|
||||||
listen [::]:80;
|
|
||||||
|
|
||||||
server_name upfast.example.com ;
|
location / {
|
||||||
|
proxy_pass https://127.0.0.1:8000;
|
||||||
location / {
|
proxy_set_header Host $http_host;
|
||||||
proxy_pass http://127.0.0.1:8000;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_redirect off;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_buffering off;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# Contributions
|
|
||||||
|
|
||||||
Thanks [TLasT](https://craftmenners.men) on his help with testing and documentation.
|
|
||||||
|
21
default.nix
Normal file
21
default.nix
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{ 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
Normal file
210
flake.lock
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
{
|
||||||
|
"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
Normal file
31
flake.nix
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
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
Normal file
19
go.mod
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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
Normal file
47
go.sum
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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=
|
39
gomod2nix.toml
Normal file
39
gomod2nix.toml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
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="
|
247
main.go
Normal file
247
main.go
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
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
113
main.py
@ -1,113 +0,0 @@
|
|||||||
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'))
|
|
32
public/views/files.html
Normal file
32
public/views/files.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{{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}}
|
55
public/views/index.html
Normal file
55
public/views/index.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{{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}}
|
@ -1,14 +0,0 @@
|
|||||||
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
Normal file
32
shell.nix
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{ 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 {
|
html {
|
||||||
padding-bottom: 10%;
|
padding-bottom: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
background: #303446;
|
background: #303446;
|
||||||
color: #c6d0f5;
|
color: #c6d0f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 250%;
|
font-size: 250%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin: 40px;
|
margin: 40px;
|
||||||
color: #626880;
|
color: #626880;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
max-height: 800px;
|
max-height: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
video {
|
video {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
max-height: 800px;
|
max-height: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #f2d5cf;
|
color: #f2d5cf;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-style: bold;
|
font-weight: bold;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: #292c3c;
|
background: #292c3c;
|
||||||
font-size: 1am;
|
font-size: 1am;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
max-width: 800;
|
max-width: 800;
|
||||||
max-height: 800;
|
max-height: 10rem;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
overflow-x: scroll;
|
overflow-x: auto;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file {
|
.file {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,29 @@
|
|||||||
body {
|
body {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
background: #303446;
|
background: #303446;
|
||||||
color: #c6d0f5;
|
color: #c6d0f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 250%;
|
font-size: 250%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #f2d5cf;
|
color: #f2d5cf;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-style: bold;
|
font-weight: bold;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
background: #414559;
|
background: #414559;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: 105%;
|
font-size: 105%;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
<!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>
|
|
@ -1,26 +0,0 @@
|
|||||||
<!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>
|
|
Loading…
Reference in New Issue
Block a user