Compare commits
73 Commits
Author | SHA1 | Date |
---|---|---|
Alexander Andreev | 7218fbc27f | |
Alexander Andreev | 5148202a0f | |
Alexander Andreev | 1edba52d32 | |
Alexander Andreev | 9c42144b45 | |
Alexander Andreev | 58197af85d | |
Alexander Andreev | 4e098ec665 | |
Alexander Andreev | 5567de7de1 | |
Alexander Andreev | 63e03f6459 | |
Alexander Andreev | 90837f1299 | |
Alexander Andreev | b50a80a33d | |
Alexander Andreev | 92212c97eb | |
Alexander Andreev | 3f16c3e799 | |
Alexander Andreev | 34c31448bb | |
Alexander Andreev | 514c6208e2 | |
Alexander Andreev | 88b989bfbb | |
Alexander Andreev | 7915091b96 | |
Alexander Andreev | f22fa1ee1d | |
Alexander Andreev | 34890ff93f | |
Alexander Andreev | fea3e7104d | |
Alexander Andreev | 2dc523271c | |
Alexander Andreev | 4048390bfb | |
Alexander Andreev | 6106e817cf | |
Alexander Andreev | 30ffa6805b | |
Alexander Andreev | 17781c5445 | |
Alexander Andreev | 3b15052330 | |
Alexander Andreev | 271c27f4ad | |
Alexander Andreev | 532e8d9da6 | |
Alexander Andreev | 72d3104150 | |
Alexander Andreev | 5bd6120ae5 | |
Alexander Andreev | d919d00cc0 | |
Alexander Andreev | 1e30e5e8ff | |
Alexander Andreev | c486acef79 | |
Alexander Andreev | aaa6aca743 | |
Alexander Andreev | d251d174e3 | |
Alexander Andreev | 82915fb300 | |
Alexander Andreev | e7e45259ba | |
Alexander Andreev | df8baf153b | |
Alexander Andreev | b513a5ff1d | |
Alexander Andreev | c2278fdd2e | |
Alexander Andreev | df3c5e0678 | |
Alexander Andreev | 266cf9dbb0 | |
Alexander Andreev | 6b56037ded | |
Alexander Andreev | 9e20c424be | |
Alexander Andreev | d77552049a | |
Alexander Andreev | 59e0629597 | |
Alexander Andreev | 6b8a7dffc7 | |
Alexander Andreev | 084872be61 | |
Alexander Andreev | 5590b0ad71 | |
Alexander Andreev | e430f0bfc9 | |
Alexander Andreev | 1c7f03053f | |
Alexander Andreev | 7e4af6b977 | |
Alexander Andreev | 5c6cf2586f | |
Alexander Andreev | 9c72131dc8 | |
Alexander Andreev | d81a29bd5b | |
Alexander Andreev | d5052994b8 | |
Alexander Andreev | 2d4169245c | |
Alexander Andreev | 949c2e68c8 | |
Alexander Andreev | 3799c1d178 | |
Alexander Andreev | 99adb7fbf8 | |
Alexander Andreev | aa3ca9a4ad | |
Alexander Andreev | 96fd7e5883 | |
Alexander Andreev | 31f84721ad | |
Alexander Andreev | e695becbcd | |
Alexander Andreev | 114d3cc931 | |
Alexander Andreev | 9a889e746a | |
Alexander Andreev | 345869608e | |
Alexander Andreev | d94030a4e8 | |
Alexander Andreev | 54f87951c1 | |
Alexander Andreev | da001dbe39 | |
Alexander Andreev | 51494e87be | |
Alexander Andreev | 92710f7f5a | |
Alexander Andreev | 2abf5a9d7f | |
Alexander Andreev | 860d719cdd |
11
Makefile
11
Makefile
|
@ -5,8 +5,9 @@ SYSDDIR_=${shell pkg-config systemd --variable=systemdsystemunitdir}
|
|||
SYSDDIR=${SYSDDIR_:/%=%}
|
||||
DESTDIR=/
|
||||
|
||||
VERSION=23.21.0
|
||||
VERSION=23.34.0
|
||||
|
||||
FLAGS=-trimpath -mod=readonly -modcacherw
|
||||
LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}" -tags osusergo,netgo
|
||||
|
||||
all: web/*.jade.go ${TARGET}
|
||||
|
@ -14,8 +15,8 @@ all: web/*.jade.go ${TARGET}
|
|||
.PHONY: ${TARGET}
|
||||
|
||||
${TARGET}:
|
||||
go build -o bin/$@ ${LDFLAGS} cmd/$@/main.go
|
||||
go build -o bin/$@-clean ${LDFLAGS} cmd/$@-clean/main.go
|
||||
go build -o bin/$@ ${FLAGS} ${LDFLAGS} cmd/$@/main.go
|
||||
go build -o bin/$@-clean ${FLAGS} ${LDFLAGS} cmd/$@-clean/main.go
|
||||
|
||||
web/*.jade.go: web/templates/*.jade
|
||||
go install github.com/Joker/jade/cmd/jade@latest
|
||||
|
@ -32,8 +33,6 @@ install:
|
|||
install -Dm 0644 init/systemd/${TARGET}-clean.timer ${DESTDIR}${SYSDDIR}/${TARGET}-clean.timer
|
||||
install -Dm 0644 init/systemd/${TARGET}-clean.service ${DESTDIR}${SYSDDIR}/${TARGET}-clean.service
|
||||
|
||||
install -Dm 0644 build/dwelling-upload.conf ${DESTDIR}usr/lib/sysusers.d/dwelling-upload.conf
|
||||
|
||||
uninstall:
|
||||
rm ${DESTDIR}usr/bin/${TARGET}
|
||||
rm ${DESTDIR}usr/bin/${TARGET}-clean
|
||||
|
@ -44,8 +43,6 @@ uninstall:
|
|||
rm ${DESTDIR}${SYSDDIR}/${TARGET}-clean.timer
|
||||
rm ${DESTDIR}${SYSDDIR}/${TARGET}-clean.service
|
||||
|
||||
rm ${DESTDIR}usr/lib/sysusers.d/dwelling-upload.conf
|
||||
|
||||
clean:
|
||||
rm -f web/*.jade.go
|
||||
go clean
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
|
||||
pkgname=dwelling-upload
|
||||
pkgver=23.21.0
|
||||
pkgver=23.34.0
|
||||
pkgrel=1
|
||||
pkgdesc="Arav's Dwelling / Upload"
|
||||
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')
|
||||
|
@ -14,9 +14,11 @@ md5sums=('SKIP')
|
|||
|
||||
build() {
|
||||
cd "$srcdir/$pkgname"
|
||||
|
||||
export GOPATH="$srcdir"/gopath
|
||||
|
||||
export CGO_CPPFLAGS="${CPPFLAGS}"
|
||||
export CGO_CFLAGS="${CFLAGS}"
|
||||
export CGO_CXXFLAGS="${CXXFLAGS}"
|
||||
export CGO_LDFLAGS="${LDFLAGS}"
|
||||
make VERSION=$pkgver DESTDIR="$pkgdir/"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
# sysusers.d
|
||||
g dwupload - -
|
||||
u dwupload - -
|
||||
m dwupload dwupload
|
|
@ -3,16 +3,18 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
var uploadDir *string = flag.String("upload-dir", "/srv/uploads", "path to a directory where uploaded files are stored")
|
||||
var keepFileForHours *int64 = flag.Int64("keep-for", 36, "keep files for this much hours")
|
||||
var showVersion *bool = flag.Bool("v", false, "show version")
|
||||
var (
|
||||
uploadDir = flag.String("dir", "/srv/upload", "path to a directory where uploaded files are stored")
|
||||
expiry = flag.Duration("expiry", 36*time.Hour, "keep files for this much hours")
|
||||
|
||||
showVersion = flag.Bool("v", false, "show version")
|
||||
)
|
||||
|
||||
var version string
|
||||
|
||||
|
@ -25,15 +27,21 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
uploadsDir, err := ioutil.ReadDir(*uploadDir)
|
||||
uploadsDir, err := os.ReadDir(*uploadDir)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open directory %s: %s\n", *uploadDir, err)
|
||||
log.Fatalf("failed to open a directory %s: %s\n", *uploadDir, err)
|
||||
}
|
||||
|
||||
for _, entry := range uploadsDir {
|
||||
if time.Now().UTC().Sub(entry.ModTime().UTC()) >= time.Duration(*keepFileForHours)*time.Hour {
|
||||
file, err := os.Stat(path.Join(*uploadDir, entry.Name()))
|
||||
if err != nil {
|
||||
log.Printf("failed to stat a file %s: %s", entry.Name(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
if time.Now().UTC().Sub(file.ModTime().UTC()) >= *expiry {
|
||||
if err := os.Remove(path.Join(*uploadDir, entry.Name())); err != nil {
|
||||
log.Printf("failed to remove file %s: %s", entry.Name(), err)
|
||||
log.Printf("failed to remove a file %s: %s", entry.Name(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"dwelling-upload/internal/http"
|
||||
duihttp "dwelling-upload/internal/http"
|
||||
"dwelling-upload/pkg/utils"
|
||||
"dwelling-upload/pkg/watcher"
|
||||
"dwelling-upload/web"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
nethttp "net/http"
|
||||
"net/netip"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"git.arav.su/Arav/httpr"
|
||||
)
|
||||
|
||||
var listenAddress *string = flag.String("listen", "/var/run/dwelling-upload/sock", "listen address (ip:port|unix_path)")
|
||||
var uploadDir *string = flag.String("upload-dir", "/srv/uploads", "path to a directory where uploaded files are stored")
|
||||
var keepFileForHours *int = flag.Int("keep-for", 36, "keep files for this much hours")
|
||||
var limitStorage *int64 = flag.Int64("storage", 102400, "storage size in MiB for uploads")
|
||||
var limitFileSize *int64 = flag.Int64("file-size", 128, "max. size in MiB for uploaded files")
|
||||
var showVersion *bool = flag.Bool("v", false, "show version")
|
||||
var (
|
||||
listenAddress = flag.String("listen", "/var/run/dwelling-upload/sock", "listen address (ip:port|unix_path)")
|
||||
uploadDir = flag.String("dir", "/srv/upload", "directory where uploaded files are stored")
|
||||
expiry = flag.Int("expiry", 36, "keep files for this much hours")
|
||||
storageSize = flag.Int64("storage", 102400, "storage size in MiB for uploads")
|
||||
fileSize = flag.Int64("file", 128, "max. size in MiB for files")
|
||||
|
||||
showVersion *bool = flag.Bool("v", false, "show version")
|
||||
)
|
||||
|
||||
var version string
|
||||
|
||||
|
@ -37,25 +38,6 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
var network string
|
||||
if !strings.ContainsRune(*listenAddress, ':') {
|
||||
network = "unix"
|
||||
defer os.Remove(*listenAddress)
|
||||
} else {
|
||||
ap, err := netip.ParseAddrPort(*listenAddress)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
} else if !ap.IsValid() {
|
||||
log.Fatalln(*listenAddress, "is not valid")
|
||||
}
|
||||
|
||||
if ap.Addr().Is4() {
|
||||
network = "tcp4"
|
||||
} else if ap.Addr().Is6() {
|
||||
network = "tcp6"
|
||||
}
|
||||
}
|
||||
|
||||
hashSalt, err := os.ReadFile(path.Join(os.Getenv("CREDENTIALS_DIRECTORY"), "salt"))
|
||||
if err != nil {
|
||||
log.Fatalln("failed to read hash salt file:", err)
|
||||
|
@ -71,6 +53,44 @@ func main() {
|
|||
|
||||
logFile := log.New(logFileFd, "", log.LstdFlags)
|
||||
|
||||
uploadDirSize, err := utils.DirectorySize(*uploadDir)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get initial size of %s: %s", *uploadDir, err)
|
||||
}
|
||||
|
||||
hand := duihttp.NewUploadHandlers(logFile, *uploadDir, &uploadDirSize, string(hashSalt),
|
||||
*expiry, *storageSize, *fileSize)
|
||||
|
||||
r := httpr.New()
|
||||
|
||||
r.NotFoundHandler = func(w http.ResponseWriter, r *http.Request) {
|
||||
duihttp.Error(w, r, "", http.StatusNotFound)
|
||||
}
|
||||
|
||||
r.Handler(http.MethodGet, "/", hand.Index)
|
||||
r.Handler(http.MethodPost, "/", hand.Upload)
|
||||
r.Handler(http.MethodGet, "/:hash/:name", hand.Download)
|
||||
r.Handler(http.MethodPost, "/delete", hand.Delete)
|
||||
r.Handler(http.MethodDelete, "/:hash", hand.Delete)
|
||||
|
||||
r.ServeStatic("/assets/*filepath", web.Assets())
|
||||
r.Handler(http.MethodGet, "/robots.txt", duihttp.RobotsTxt)
|
||||
r.Handler(http.MethodGet, "/favicon.svg", duihttp.Favicon)
|
||||
|
||||
srv := duihttp.NewHttpServer(r)
|
||||
|
||||
if err := srv.Start(*listenAddress); err != nil {
|
||||
log.Fatalln("failed to start a server:", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := srv.Stop(); err != nil {
|
||||
log.Fatalln("failed to properly shutdown a server:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
doneSignal := make(chan os.Signal, 1)
|
||||
signal.Notify(doneSignal, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
watcha, err := watcher.NewInotifyWatcher()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
|
@ -82,40 +102,7 @@ func main() {
|
|||
}
|
||||
|
||||
uploadDirNotify := make(chan uint32)
|
||||
uploadDirSize, err := utils.DirectorySize(*uploadDir)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get initial size of %s: %s", *uploadDir, err)
|
||||
}
|
||||
|
||||
watcha.WatchForMask(uploadDirNotify, watcher.CrDelMask)
|
||||
|
||||
hand := http.NewUploadHandlers(logFile, *uploadDir, &uploadDirSize, string(hashSalt),
|
||||
*keepFileForHours, *limitStorage, *limitFileSize)
|
||||
|
||||
r := httpr.New()
|
||||
|
||||
r.NotFoundHandler = func(w nethttp.ResponseWriter, r *nethttp.Request) {
|
||||
http.Error(w, r, nethttp.StatusNotFound, "")
|
||||
}
|
||||
|
||||
r.Handler(nethttp.MethodGet, "/", hand.Index)
|
||||
r.Handler(nethttp.MethodPost, "/", hand.Upload)
|
||||
r.Handler(nethttp.MethodGet, "/:hash/:name", hand.Download)
|
||||
r.Handler(nethttp.MethodPost, "/delete", hand.Delete)
|
||||
r.Handler(nethttp.MethodDelete, "/:hash", hand.Delete)
|
||||
|
||||
r.ServeStatic("/assets/*filepath", web.Assets())
|
||||
r.Handler(nethttp.MethodGet, "/robots.txt", http.RobotsTxt)
|
||||
r.Handler(nethttp.MethodGet, "/favicon.svg", http.Favicon)
|
||||
|
||||
srv := http.NewHttpServer(r)
|
||||
|
||||
if err := srv.Start(network, *listenAddress); err != nil {
|
||||
log.Fatalln("failed to start a server:", err)
|
||||
}
|
||||
|
||||
doneSignal := make(chan os.Signal, 1)
|
||||
signal.Notify(doneSignal, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
go watcha.WatchForMask(uploadDirNotify, watcher.CrDelMask)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
|
@ -130,8 +117,4 @@ func main() {
|
|||
}()
|
||||
|
||||
<-doneSignal
|
||||
|
||||
if err := srv.Stop(); err != nil {
|
||||
log.Fatalln("failed to properly shutdown a server:", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
server {
|
||||
listen 443 ssl http2;
|
||||
# listen 8094; # Tor
|
||||
listen 127.0.0.1:8114; # I2P
|
||||
listen 8094; # Tor I2P
|
||||
listen [300:a98d:d6d0:8a08::c]:80; # Yggdrasil
|
||||
|
||||
server_name upload.arav.su upload.arav.i2p;
|
||||
server_name upload.arav.su upload.arav.i2p 4usftbmjpfexkr2x5xbp5ukmygpmg4fgrnx2wbifsexqctooz5hmviyd.onion;
|
||||
|
||||
access_log /var/log/nginx/dwelling/upload.log main if=$nolog;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/arav.su/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/arav.su/privkey.pem;
|
||||
|
||||
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self'; media-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; font-src 'self'; form-action 'self'";
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'self'; img-src 'self'; media-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; font-src 'self'; form-action 'self'";
|
||||
add_header X-Frame-Options "DENY";
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
|
||||
# add_header Onion-Location "http://.onion$request_uri";
|
||||
add_header Onion-Location "http://4usftbmjpfexkr2x5xbp5ukmygpmg4fgrnx2wbifsexqctooz5hmviyd.onion$request_uri";
|
||||
|
||||
|
||||
location / {
|
||||
|
|
2
go.mod
2
go.mod
|
@ -4,4 +4,4 @@ go 1.17
|
|||
|
||||
require github.com/pkg/errors v0.9.1
|
||||
|
||||
require git.arav.su/Arav/httpr v0.1.0
|
||||
require git.arav.su/Arav/httpr v0.3.1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,4 +1,4 @@
|
|||
git.arav.su/Arav/httpr v0.1.0 h1:jp1BDGioYUV/egITFSGHbCRvIORjjnCvv42DZFJ7uWg=
|
||||
git.arav.su/Arav/httpr v0.1.0/go.mod h1:z0SVYwe5dBReeVuFU9QH2PmBxICJwchxqY5OfZbeVzU=
|
||||
git.arav.su/Arav/httpr v0.3.1 h1:8ba90SJ4XYUWfIlC3V0Zuw3+CcOb9IYVkOZ/2mB9JO0=
|
||||
git.arav.su/Arav/httpr v0.3.1/go.mod h1:z0SVYwe5dBReeVuFU9QH2PmBxICJwchxqY5OfZbeVzU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -3,9 +3,8 @@ Description=dwelling-upload-clean
|
|||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=dwupload
|
||||
Group=dwupload
|
||||
ExecStart=/usr/bin/dwelling-upload-clean -upload-dir /srv/uploads -keep-for 36
|
||||
DynamicUser=yes
|
||||
ExecStart=/usr/bin/dwelling-upload-clean -dir /srv/upload -expiry 36h
|
||||
|
||||
ReadOnlyPaths=/
|
||||
# Set here path to directory where uploads are stored.
|
||||
|
@ -20,18 +19,33 @@ LockPersonality=true
|
|||
MemoryDenyWriteExecute=true
|
||||
NoNewPrivileges=true
|
||||
PrivateDevices=true
|
||||
PrivateTmp=true
|
||||
PrivateUsers=true
|
||||
ProcSubset=pid
|
||||
ProtectClock=true
|
||||
ProtectControlGroups=true
|
||||
ProtectHome=true
|
||||
ProtectHostname=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectProc=noaccess
|
||||
ProtectSystem=strict
|
||||
RestrictAddressFamilies=
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
RestrictNamespaces=true
|
||||
RestrictRealtime=true
|
||||
RestrictSUIDSGID=true
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=~@clock
|
||||
SystemCallFilter=~@cpu-emulation
|
||||
SystemCallFilter=~@debug
|
||||
SystemCallFilter=~@module
|
||||
SystemCallFilter=~@mount
|
||||
SystemCallFilter=~@obsolete
|
||||
SystemCallFilter=~@privileged
|
||||
SystemCallFilter=~@raw-io
|
||||
SystemCallFilter=~@reboot
|
||||
SystemCallFilter=~@swap
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -5,10 +5,9 @@ After=network.target
|
|||
[Service]
|
||||
Type=simple
|
||||
Restart=on-failure
|
||||
User=dwupload
|
||||
Group=dwupload
|
||||
ExecStart=/usr/bin/dwelling-upload -listen /var/run/dwelling-upload/sock -upload-dir /srv/uploads \
|
||||
-keep-for 36 -storage 102400 -file-size 128
|
||||
DynamicUser=yes
|
||||
ExecStart=/usr/bin/dwelling-upload -listen /var/run/dwelling-upload/sock \
|
||||
-dir /srv/upload -expiry 36 -storage 102400 -file 128
|
||||
|
||||
ReadOnlyPaths=/
|
||||
# Set here path to directory where uploads are stored.
|
||||
|
@ -19,8 +18,7 @@ ExecPaths=/usr/bin/dwelling-upload
|
|||
RuntimeDirectory=dwelling-upload
|
||||
LogsDirectory=dwelling-upload
|
||||
|
||||
# Use gen-salt.sh to generate salt! It will create override.conf or add to the
|
||||
# end of existing one.
|
||||
# Use gen-salt.sh to generate salt! It will create / append to an override.conf.
|
||||
SetCredentialEncrypted=
|
||||
|
||||
AmbientCapabilities=
|
||||
|
@ -30,18 +28,33 @@ LockPersonality=true
|
|||
MemoryDenyWriteExecute=true
|
||||
NoNewPrivileges=true
|
||||
PrivateDevices=true
|
||||
PrivateTmp=true
|
||||
PrivateUsers=true
|
||||
ProcSubset=pid
|
||||
ProtectClock=true
|
||||
ProtectControlGroups=true
|
||||
ProtectHome=true
|
||||
ProtectHostname=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectProc=noaccess
|
||||
ProtectSystem=strict
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
RestrictNamespaces=true
|
||||
RestrictRealtime=true
|
||||
RestrictSUIDSGID=true
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=~@clock
|
||||
SystemCallFilter=~@cpu-emulation
|
||||
SystemCallFilter=~@debug
|
||||
SystemCallFilter=~@module
|
||||
SystemCallFilter=~@mount
|
||||
SystemCallFilter=~@obsolete
|
||||
SystemCallFilter=~@privileged
|
||||
SystemCallFilter=~@raw-io
|
||||
SystemCallFilter=~@reboot
|
||||
SystemCallFilter=~@swap
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -48,12 +48,10 @@ func (h *UploadHandlers) Index(w http.ResponseWriter, r *http.Request) {
|
|||
var storCapacity int64 = h.limitStorage << 20
|
||||
var fMaxSize int64 = h.limitFileSize << 20
|
||||
|
||||
_, _, capStr := utils.ConvertFileSize(storCapacity)
|
||||
_, _, usedStr := utils.ConvertFileSize(*h.uploadDirSize)
|
||||
_, _, availStr := utils.ConvertFileSize(storCapacity - *h.uploadDirSize)
|
||||
_, _, fMaxSzStr := utils.ConvertFileSize(fMaxSize)
|
||||
|
||||
web.Index(utils.MainSite(r.Host), storCapacity, *h.uploadDirSize, h.keepForHours, fMaxSzStr, usedStr, capStr, availStr, w)
|
||||
web.Index(utils.MainSite(r.Host), h.keepForHours, fMaxSzStr, availStr, w)
|
||||
}
|
||||
|
||||
func (h *UploadHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -64,42 +62,37 @@ func (h *UploadHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if err := r.ParseMultipartForm(fMaxSizeBytes); err != nil {
|
||||
log.Println("failed to parse upload form:", err)
|
||||
Error(w, r, http.StatusExpectationFailed, "Failed to parse upload form.")
|
||||
Error(w, r, "Failed to parse upload form.", http.StatusExpectationFailed)
|
||||
return
|
||||
}
|
||||
|
||||
f, fHandler, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
log.Println("failed to open incoming file:", err)
|
||||
Error(w, r, http.StatusInternalServerError, "Error reading an incoming file.")
|
||||
Error(w, r, "Error reading an incoming file.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
os.Remove(fHandler.Filename)
|
||||
f.Close()
|
||||
}()
|
||||
defer os.Remove(fHandler.Filename)
|
||||
defer f.Close()
|
||||
|
||||
var leftSpace int64 = storCapacity - *h.uploadDirSize
|
||||
|
||||
if leftSpace < fHandler.Size {
|
||||
log.Println("not enough space left in storage, only", leftSpace>>20, "MiB left")
|
||||
if strings.Contains(r.UserAgent(), "curl") || strings.Contains(r.UserAgent(), "Wget") {
|
||||
http.Error(w, "Not enough space left, sorry", http.StatusInternalServerError)
|
||||
} else {
|
||||
web.ErrorNoSpace(utils.MainSite(r.Host), w)
|
||||
}
|
||||
Error(w, r, "Not enough space left, sorry.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
s256 := sha256.New()
|
||||
if _, err := io.Copy(s256, f); err != nil {
|
||||
log.Println("failed to compute a SHA-256 hash:", err)
|
||||
Error(w, r, http.StatusInternalServerError, "A hash for the file cannot be computed.")
|
||||
Error(w, r, "A hash for the file cannot be computed.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
fHash := hex.EncodeToString(s256.Sum(nil))
|
||||
s256.Write([]byte(h.hashSalt))
|
||||
s256.Write([]byte(time.Now().String()))
|
||||
fSaltedHash := base64.RawURLEncoding.EncodeToString(s256.Sum(nil))
|
||||
|
||||
f.Seek(0, io.SeekStart)
|
||||
|
@ -111,7 +104,7 @@ func (h *UploadHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||
fDst, err := os.Create(fPath)
|
||||
if err != nil {
|
||||
log.Println("failed to open file for writing", err)
|
||||
Error(w, r, http.StatusInternalServerError, "File cannot be written.")
|
||||
Error(w, r, "File cannot be written.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer fDst.Close()
|
||||
|
@ -124,17 +117,20 @@ func (h *UploadHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||
fDst.Write([]byte{0})
|
||||
fDst.Seek(0, io.SeekStart)
|
||||
|
||||
_, err = io.Copy(fDst, f)
|
||||
if err != nil {
|
||||
if _, err = io.Copy(fDst, f); err != nil {
|
||||
log.Println("failed to copy uploaded file to destination:", err)
|
||||
Error(w, r, http.StatusInternalServerError, "Failed to copy uploaded file to the storage.")
|
||||
Error(w, r, "Failed to copy uploaded file to the storage.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
typ, _ := utils.NetworkType(r.Host)
|
||||
ip := r.Header.Get("X-Real-IP")
|
||||
if typ != "www" && typ != "ygg" {
|
||||
ip = ""
|
||||
}
|
||||
|
||||
h.logFile.Printf("| up | %s | %s | %s | SHA256 %s | %s | %d | %s", r.Header.Get("X-Real-IP"), typ,
|
||||
fHandler.Filename, fHash, fSaltedHash, fHandler.Size, r.UserAgent())
|
||||
h.logFile.Printf("| up | %s | %s | %s | SHA256 %s | %s | %d | %s",
|
||||
ip, typ, fHandler.Filename, fHash, fSaltedHash, fHandler.Size, r.UserAgent())
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
} else {
|
||||
|
@ -148,13 +144,8 @@ func (h *UploadHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||
_, scheme := utils.NetworkType(r.Host)
|
||||
site := scheme + "://" + r.Host
|
||||
|
||||
if strings.Contains(r.UserAgent(), "curl") {
|
||||
w.Write([]byte(site + downloadURLParsed.String() + "\r\n"))
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(r.UserAgent(), "curl") || strings.Contains(r.UserAgent(), "Wget") {
|
||||
fmt.Fprintln(w, downloadURLParsed.String(), "will be kept for", h.keepForHours)
|
||||
fmt.Fprintln(w, site+downloadURLParsed.String(), "will be kept for", h.keepForHours)
|
||||
} else {
|
||||
web.Uploaded(utils.MainSite(r.Host), site, downloadURLParsed.String(), h.keepForHours, w)
|
||||
}
|
||||
|
@ -167,7 +158,7 @@ func (h *UploadHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
stat, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
Error(w, r, http.StatusNotFound, "")
|
||||
Error(w, r, "", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -178,14 +169,19 @@ func (h *UploadHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Println("failed to open file to read:", err)
|
||||
Error(w, r, http.StatusInternalServerError, "Failed to open file to read.")
|
||||
Error(w, r, "Failed to open file to read.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
netTyp, _ := utils.NetworkType(r.Host)
|
||||
typ, _ := utils.NetworkType(r.Host)
|
||||
ip := r.Header.Get("X-Real-IP")
|
||||
if typ != "www" && typ != "ygg" {
|
||||
ip = ""
|
||||
}
|
||||
|
||||
h.logFile.Printf("| dw | %s | %s | %s | %s | %s", r.Header.Get("X-Real-IP"), netTyp, name, saltedHash, r.UserAgent())
|
||||
h.logFile.Printf("| dw | %s | %s | %s | %s | %s",
|
||||
ip, typ, name, saltedHash, r.UserAgent())
|
||||
|
||||
http.ServeContent(w, r, path, stat.ModTime(), fd)
|
||||
}
|
||||
|
@ -201,24 +197,26 @@ func (h *UploadHandlers) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
path := path.Join(h.uploadDir, saltedHash)
|
||||
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
Error(w, r, http.StatusNotFound, "")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
Error(w, r, "", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
if err := os.Remove(path); err != nil {
|
||||
log.Println("failed to remove a file:", err)
|
||||
Error(w, r, http.StatusInternalServerError, "Failed to remove a file.")
|
||||
Error(w, r, "Failed to remove a file.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
netTyp, _ := utils.NetworkType(r.Host)
|
||||
typ, _ := utils.NetworkType(r.Host)
|
||||
ip := r.Header.Get("X-Real-IP")
|
||||
if typ != "www" && typ != "ygg" {
|
||||
ip = ""
|
||||
}
|
||||
|
||||
h.logFile.Printf("| dt | %s | %s | %s | %s", r.Header.Get("X-Real-IP"), netTyp, saltedHash, r.UserAgent())
|
||||
h.logFile.Printf("| dt | %s | %s | %s | %s",
|
||||
ip, typ, saltedHash, r.UserAgent())
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
if strings.Contains(r.UserAgent(), "curl") || strings.Contains(r.UserAgent(), "Wget") {
|
||||
fmt.Fprintln(w, "File was successfully deleted.")
|
||||
} else {
|
||||
|
@ -226,19 +224,16 @@ func (h *UploadHandlers) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func Error(w http.ResponseWriter, r *http.Request, code int, reason string) {
|
||||
func Error(w http.ResponseWriter, r *http.Request, reason string, code int) {
|
||||
if strings.Contains(r.UserAgent(), "curl") || strings.Contains(r.UserAgent(), "Wget") {
|
||||
http.Error(w, reason, code)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(code)
|
||||
web.ErrorXXX(utils.MainSite(r.Host), code, reason, w)
|
||||
}
|
||||
|
||||
func AssetsFS() http.FileSystem {
|
||||
return web.Assets()
|
||||
}
|
||||
|
||||
func RobotsTxt(w http.ResponseWriter, r *http.Request) {
|
||||
data, _ := web.AssetsGetFile("robots.txt")
|
||||
w.Write(data)
|
||||
|
|
|
@ -5,12 +5,15 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HttpServer struct {
|
||||
s http.Server
|
||||
s http.Server
|
||||
addr net.Addr
|
||||
}
|
||||
|
||||
func NewHttpServer(r http.Handler) *HttpServer {
|
||||
|
@ -20,7 +23,23 @@ func NewHttpServer(r http.Handler) *HttpServer {
|
|||
Handler: r}}
|
||||
}
|
||||
|
||||
func (s *HttpServer) Start(network, address string) error {
|
||||
func (s *HttpServer) Start(address string) error {
|
||||
var network string
|
||||
if !strings.ContainsRune(address, ':') {
|
||||
network = "unix"
|
||||
} else {
|
||||
ap, err := netip.ParseAddrPort(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ap.Addr().Is4() {
|
||||
network = "tcp4"
|
||||
} else if ap.Addr().Is6() {
|
||||
network = "tcp6"
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen(network, address)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -30,6 +49,8 @@ func (s *HttpServer) Start(network, address string) error {
|
|||
os.Chmod(address, 0777)
|
||||
}
|
||||
|
||||
s.addr = listener.Addr()
|
||||
|
||||
go func() {
|
||||
if err = s.s.Serve(listener); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalln(err)
|
||||
|
@ -43,6 +64,10 @@ func (s *HttpServer) Stop() error {
|
|||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if s.addr.Network() == "unix" {
|
||||
defer os.Remove(s.addr.String())
|
||||
}
|
||||
|
||||
if err := s.s.Shutdown(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ func NetworkType(host string) (string, string) {
|
|||
return "i2p", "http"
|
||||
} else if strings.Contains(host, "onion") {
|
||||
return "tor", "http"
|
||||
} else if strings.Contains(host, "[300:") {
|
||||
return "ygg", "http"
|
||||
} else {
|
||||
return "www", "https"
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -14,10 +14,10 @@ var sizeSuffixes = [...]string{"B", "KiB", "MiB", "GiB", "TiB"}
|
|||
// ConvertFileSize converts size in bytes down to biggest units it represents.
|
||||
// Returns converted size, unit and, a concatenation of size and unit
|
||||
func ConvertFileSize(size int64) (float64, string, string) {
|
||||
var idx int
|
||||
var fSize float64 = float64(size)
|
||||
idx := 0
|
||||
fSize := float64(size)
|
||||
|
||||
for idx = 0; fSize >= 1024; fSize /= 1024 {
|
||||
for ; fSize >= 1024; fSize /= 1024 {
|
||||
idx++
|
||||
}
|
||||
|
||||
|
@ -25,23 +25,23 @@ func ConvertFileSize(size int64) (float64, string, string) {
|
|||
fSizeStr = strings.TrimRight(fSizeStr, "0")
|
||||
fSizeStr = strings.TrimSuffix(fSizeStr, ".")
|
||||
|
||||
return fSize, sizeSuffixes[idx], strings.Join([]string{fSizeStr, sizeSuffixes[idx]}, " ")
|
||||
return fSize, sizeSuffixes[idx],
|
||||
strings.Join([]string{fSizeStr, sizeSuffixes[idx]}, " ")
|
||||
}
|
||||
|
||||
func DirectorySize(path string) (dirSz int64, err error) {
|
||||
err = filepath.Walk(path, func(_ string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to compute %s directory size", path)
|
||||
}
|
||||
|
||||
dirSz += info.Size()
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
func DirectorySize(dirPath string) (dirSz int64, err error) {
|
||||
dir, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, errors.Wrapf(err, "failed to compute %s directory size", dirPath)
|
||||
}
|
||||
|
||||
return dirSz - 4096, nil
|
||||
for _, entry := range dir {
|
||||
file, err := os.Stat(path.Join(dirPath, entry.Name()))
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "failed to stat a file %s", entry.Name())
|
||||
}
|
||||
dirSz += file.Size()
|
||||
}
|
||||
|
||||
return dirSz, nil
|
||||
}
|
||||
|
|
|
@ -54,26 +54,24 @@ func (w *InotifyWatcher) AddWatch(path string, mask uint32) error {
|
|||
// WatchForMask checks for events specified in `mask` and sends them
|
||||
// to channel `fired`. See `man inotify` for events.
|
||||
func (w *InotifyWatcher) WatchForMask(fired chan uint32, mask uint32) {
|
||||
go func() {
|
||||
for !w.closed {
|
||||
buffer := make([]byte, syscall.SizeofInotifyEvent*inotifyCount)
|
||||
n, err := syscall.Read(w.fd, buffer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
for !w.closed {
|
||||
buffer := make([]byte, syscall.SizeofInotifyEvent*inotifyCount)
|
||||
n, err := syscall.Read(w.fd, buffer)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if n < syscall.SizeofInotifyEvent {
|
||||
continue
|
||||
}
|
||||
if n < syscall.SizeofInotifyEvent {
|
||||
continue
|
||||
}
|
||||
|
||||
for offset := 0; offset < len(buffer); offset += syscall.SizeofInotifyEvent {
|
||||
event := (*syscall.InotifyEvent)(unsafe.Pointer(&buffer[offset]))
|
||||
if event.Mask&mask > 0 {
|
||||
fired <- event.Mask
|
||||
}
|
||||
for offset := 0; offset < len(buffer); offset += syscall.SizeofInotifyEvent {
|
||||
event := (*syscall.InotifyEvent)(unsafe.Pointer(&buffer[offset]))
|
||||
if event.Mask&mask > 0 {
|
||||
fired <- event.Mask
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Close removes all watchers, closes inotify descriptor and stops all
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
background-color: var(--secondary-color);
|
||||
color: var(--background-color); }
|
||||
|
||||
.center { text-align: center; }
|
||||
|
||||
a,
|
||||
button {
|
||||
color: var(--primary-color);
|
||||
|
@ -50,8 +52,8 @@ input[type="file"] {
|
|||
color: var(--text-color);
|
||||
font: inherit; }
|
||||
|
||||
input[type="file"]::file-selector-button,
|
||||
button {
|
||||
button,
|
||||
input[type="file"]::file-selector-button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--primary-color);
|
||||
|
@ -91,17 +93,6 @@ h2 {
|
|||
|
||||
small { font-size: .8rem; }
|
||||
|
||||
progress {
|
||||
background-color: var(--secondary-color);
|
||||
border: none;
|
||||
color: var(--primary-color);
|
||||
height: 1.1rem;
|
||||
width: 30%; }
|
||||
|
||||
progress::-moz-progress-bar { background-color: var(--primary-color); }
|
||||
|
||||
.center { text-align: center; }
|
||||
|
||||
html { margin-left: calc(100vw - 100%); }
|
||||
|
||||
body {
|
||||
|
@ -118,24 +109,22 @@ header {
|
|||
flex-wrap: wrap;
|
||||
justify-content: space-between; }
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
width: 360px; }
|
||||
header svg { width: 360px; }
|
||||
|
||||
#logo text { fill: var(--text-color); }
|
||||
header svg text { fill: var(--text-color); }
|
||||
|
||||
#logo .logo {
|
||||
header svg text:first-child {
|
||||
font-size: 2rem;
|
||||
font-variant-caps: small-caps;
|
||||
font-weight: bold; }
|
||||
|
||||
header svg text:last-child { font-size: .88rem; }
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
#logo .logo { font-size: 2.082rem; } }
|
||||
header svg text:first-child { font-size: 2.082rem; } }
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
#logo .logo { font-size: 2rem; } }
|
||||
|
||||
#logo .under { font-size: .88rem; }
|
||||
header svg text:first-child { font-size: 2rem; } }
|
||||
|
||||
nav { margin-top: .5rem; }
|
||||
|
||||
|
@ -147,7 +136,13 @@ nav h1 {
|
|||
|
||||
section { margin-top: 1rem; }
|
||||
|
||||
#used-space div span { margin: 0 .2rem; }
|
||||
#error {
|
||||
font-size: 3.5rem;
|
||||
line-height: 5rem;
|
||||
text-align: center;
|
||||
margin: 6rem 0; }
|
||||
|
||||
#error h1 { font-size: 8rem; }
|
||||
|
||||
footer {
|
||||
font-size: .8rem;
|
||||
|
@ -157,7 +152,7 @@ footer {
|
|||
@media screen and (max-width: 640px) {
|
||||
header { display: block; }
|
||||
|
||||
#logo {
|
||||
header svg {
|
||||
margin: 0 auto;
|
||||
width: 100%; }
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
User-agent: *
|
||||
Disallow: /assets/
|
||||
Allow: /$
|
||||
Disallow: /
|
|
@ -12,12 +12,12 @@ html(lang="en")
|
|||
block head
|
||||
body
|
||||
header
|
||||
svg#logo(viewBox="0 -25 216 40")
|
||||
text.logo Arav's dwelling
|
||||
text.under(y="11") Welcome to my sacred place, wanderer
|
||||
svg(viewBox="0 -25 216 40")
|
||||
text Arav's dwelling
|
||||
text(y="11") Welcome to my sacred place, wanderer
|
||||
nav
|
||||
a(href=mainSite title="Arav's dwelling") Back to main website
|
||||
block header
|
||||
block body
|
||||
footer
|
||||
| 2022,2023 Alexander "Arav" Andreev <#[a(href="mailto:me@arav.su") me@arav.su]>
|
||||
| 2022,2023 Alexander "Arav" Andreev <#[a(href="mailto:me@arav.su") me@arav.su]> #[a(href=mainSite+'/privacy') Privacy statements]
|
|
@ -1,22 +1,14 @@
|
|||
extends base.jade
|
||||
|
||||
block head
|
||||
:go:func ErrorXXX(mainSite string, code int, errorMsg string)
|
||||
style(type="text/css").
|
||||
#error {
|
||||
font-size: 3.5rem;
|
||||
line-height: 5rem;
|
||||
text-align: center;
|
||||
margin: 6rem 0; }
|
||||
|
||||
#error h1 { font-size: 8rem; }
|
||||
|
||||
block header
|
||||
h1 Еггог
|
||||
|
||||
block body
|
||||
:go:func ErrorXXX(mainSite string, code int, errorMsg string)
|
||||
section#error
|
||||
h1 #{code}
|
||||
| #{http.StatusText(code)}
|
||||
if errorMsg != ""
|
||||
p #{errorMsg}
|
||||
center
|
||||
a(href="/") Back to index page
|
||||
|
|
|
@ -4,37 +4,25 @@ block header
|
|||
h1 Upload
|
||||
|
||||
block body
|
||||
:go:func Index(mainSite string, storageCapacity, storageUsed int64, keepForHours int, fileMaxSize, storageUsedStr, storageCapacityStr, storageAvailableStr string)
|
||||
section#rules.center
|
||||
:go:func Index(mainSite string, keepForHours int, fileMaxSize, storageAvailableStr string)
|
||||
section
|
||||
h2 Rules
|
||||
p Maximum file size is #{fileMaxSize} and it will be kept for #{keepForHours} hours.
|
||||
p Maximum file size is #[b #{fileMaxSize}] and it will be kept for #[b #{keepForHours}] hours.
|
||||
p Content you upload should comply with Russian Federation's law. Generally speaking, anything illegal, like CP, extremist literature, and so on is forbidden.
|
||||
section#used-space.center
|
||||
h2 Free space
|
||||
div
|
||||
span #{storageUsedStr}
|
||||
progress(value=storageUsed max=storageCapacity)
|
||||
span #{storageCapacityStr}
|
||||
div
|
||||
| #{storageAvailableStr}
|
||||
section#upload.center
|
||||
section.center
|
||||
h2 Upload
|
||||
form(action="/" method="POST" enctype="multipart/form-data")
|
||||
input(type="file" name="file" multiple=false)
|
||||
button(type="submit") Upload
|
||||
section.center
|
||||
p You can use cURL to upload a file: #[code curl -F 'file=@somefile.ext' https://upload.arav.su]
|
||||
p Over I2P: #[code curl --proxy 127.0.0.1:4444 -F 'file=@somefile.ext' http://upload.arav.i2p]
|
||||
p A resulted link looks like this: #[code /base64rawURL(salted SHA-256)/filename.ext].
|
||||
p.center #[b #{storageAvailableStr}] left.
|
||||
section
|
||||
p Using cURL: #[code curl -F 'file=@somefile.ext' https://upload.arav.su]
|
||||
p Also works under other networks (I2P, Tor, Yggdrasil). For Tor and I2P you'll need to add a #[code --proxy] option for cURL. Same for deletion.
|
||||
p A resulted link has the following structure: #[code <site>/<hash>/<file>.<ext>].
|
||||
section.center
|
||||
h2 Delete
|
||||
form(action="/delete" method="POST")
|
||||
input(type="text", name="hash" placeholder="File hash goes here" minlength="43" maxlength="43" size="43" required="")
|
||||
button(type="submit") Delete
|
||||
section.center
|
||||
p You can delete a file using cURL: #[code curl -XDELETE https://upload.arav.su/<hash>]
|
||||
p Over I2P: #[code curl --proxy 127.0.0.1:4444 -XDELETE http://upload.arav.i2p/<hash>]
|
||||
section
|
||||
h2 Privacy statements
|
||||
p Any abuses should be sent to #[a(href="mailto:admin@arav.su") admin@arav.su]. I WILL cooperate with law enforcements and provide them with logs.
|
||||
p Logs include: access time, IP-address, file name it was uploaded/downloaded with, a SHA-256 hash of it, size of it, and User-Agent.
|
||||
p Using cURL: #[code curl -XDELETE https://upload.arav.su/<hash>]
|
|
@ -1,20 +0,0 @@
|
|||
extends base.jade
|
||||
|
||||
block head
|
||||
:go:func ErrorNoSpace(mainSite string)
|
||||
style(type="text/css").
|
||||
#error {
|
||||
font-size: 3.5rem;
|
||||
line-height: 5rem;
|
||||
text-align: center;
|
||||
margin: 6rem 0; }
|
||||
|
||||
#error h1 { font-size: 6rem; }
|
||||
|
||||
block header
|
||||
h1 :(
|
||||
|
||||
block body
|
||||
- wr.(http.ResponseWriter).WriteHeader(http.StatusInternalServerError)
|
||||
section#error
|
||||
h1 Not enough space left
|
|
@ -5,7 +5,7 @@ block header
|
|||
|
||||
block body
|
||||
:go:func Uploaded(mainSite, site, downloadLink string, keepForHours int)
|
||||
section#file
|
||||
section
|
||||
h2 Your link
|
||||
center
|
||||
a(href=downloadLink) #{site}#{downloadLink}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
)
|
||||
|
||||
//go:generate $GOPATH/bin/jade -pkg=web -writer templates/index.jade
|
||||
//go:generate $GOPATH/bin/jade -pkg=web -writer templates/nospace.jade
|
||||
//go:generate $GOPATH/bin/jade -pkg=web -writer templates/deleted.jade
|
||||
//go:generate $GOPATH/bin/jade -pkg=web -writer templates/uploaded.jade
|
||||
//go:generate $GOPATH/bin/jade -pkg=web -writer templates/errorXXX.jade
|
||||
|
|
Loading…
Reference in New Issue