1
0
Fork 0

Compare commits

...

50 Commits

Author SHA1 Message Date
Alexander Andreev 7218fbc27f
Version set to 23.34.0. 2023-08-23 04:04:22 +04:00
Alexander Andreev 5148202a0f
A small reorganisation in main.css. 2023-08-23 03:48:52 +04:00
Alexander Andreev 1edba52d32
Disallow all pages but the index. 2023-08-23 03:43:35 +04:00
Alexander Andreev 9c42144b45
Updated index.jade. 2023-08-22 18:46:02 +04:00
Alexander Andreev 58197af85d
Added new options to .service files. Hope it won't break a program. xD 2023-08-22 18:24:02 +04:00
Alexander Andreev 4e098ec665
style-src unsafe-inline was removed because it doesn't needed anymore. 2023-08-22 18:21:25 +04:00
Alexander Andreev 5567de7de1
Error styling was moved out to main.css. 2023-08-22 18:20:13 +04:00
Alexander Andreev 63e03f6459
Removed unused ids. 2023-08-22 18:19:47 +04:00
Alexander Andreev 90837f1299
Changed logo. Removed id and classes. 2023-08-22 18:16:26 +04:00
Alexander Andreev b50a80a33d
Updated httpr package dependency to 0.3.1. 2023-08-13 04:19:14 +04:00
Alexander Andreev 92212c97eb
Add time for better randomisation for a hash. 2023-08-06 07:33:34 +04:00
Alexander Andreev 3f16c3e799
Removed sysusers.d/dwelling-upload.conf from Makefile. 2023-08-06 06:36:20 +04:00
Alexander Andreev 34c31448bb
Shortened two sentences on an index page. 2023-08-06 04:33:09 +04:00
Alexander Andreev 514c6208e2
Make available space bold. 2023-08-06 04:00:09 +04:00
Alexander Andreev 88b989bfbb
Fixed new code for HttpServer. Had to add net.Addr field to the struct to hold addr and network to use in Stop() method. 2023-08-06 03:06:24 +04:00
Alexander Andreev 7915091b96
Defer srv.Stop after successful call of srv.Start. 2023-08-06 03:00:31 +04:00
Alexander Andreev f22fa1ee1d
Do not create a system user, use a dynamic one instead. 2023-08-06 02:21:02 +04:00
Alexander Andreev 34890ff93f
Version was set to 23.31.1. 2023-08-06 02:13:56 +04:00
Alexander Andreev fea3e7104d
In nginx.conf added an onion domain to a server_name. And a space between it and access_log. 2023-08-06 02:12:15 +04:00
Alexander Andreev 2dc523271c
In dwelling-upload.service changed a formatting of one line to reduce its length. :) 2023-08-06 02:05:30 +04:00
Alexander Andreev 4048390bfb
Changed handlers.Error() signature to be like http.Error()'s one. 2023-08-06 02:02:58 +04:00
Alexander Andreev 6106e817cf
In utils.NetworkType() added Yggdrasil. 2023-08-06 01:59:25 +04:00
Alexander Andreev 30ffa6805b
Omit IP if a network type is not www or yggdrasil. 2023-08-06 01:54:01 +04:00
Alexander Andreev 17781c5445
Fixed copy-pasta. log.Fatalln() replaced with return err. 2023-08-06 01:42:56 +04:00
Alexander Andreev 3b15052330
Setup upload dir watcher at the last step. 2023-08-06 01:34:55 +04:00
Alexander Andreev 271c27f4ad
In dwelling-upload's main changed aliases for imports. 2023-08-06 01:28:55 +04:00
Alexander Andreev 532e8d9da6
Code for determining of network type was moved to HttpServer's Start() func. 2023-08-06 01:26:41 +04:00
Alexander Andreev 72d3104150
Explicit check for address type, now check if an addr passed is IPv6. Otherwise, network var will remain empty. 2023-08-06 01:21:14 +04:00
Alexander Andreev 5bd6120ae5
In var () section removed unnecessary type declarations. 2023-08-06 01:18:41 +04:00
Alexander Andreev d919d00cc0
In dwelling-upload-clean converted an expiry var to time.Duration. Added an 'h' unit in a .service to represent hours. 2023-08-05 19:28:05 +04:00
Alexander Andreev 1e30e5e8ff
Removed unnecessary .center class from sections. | replaced by p.center for space left. 2023-08-05 06:51:39 +04:00
Alexander Andreev c486acef79
Moved available space next to an Upload button. 2023-08-05 06:34:21 +04:00
Alexander Andreev aaa6aca743
Removing -buildmode=pie flag, it causes permission denied 203/EXEC failure when starting a service. 2023-08-05 06:27:48 +04:00
Alexander Andreev d251d174e3
Added a set of export CGO_*FLAGS statements recommended in https://wiki.archlinux.org/title/Go_package_guidelines 2023-08-05 06:02:54 +04:00
Alexander Andreev 82915fb300
Added some necessary flags for go build. 2023-08-05 06:01:10 +04:00
Alexander Andreev e7e45259ba
Oh, in os.Stat() path.Join() is needed, since DirEntry.Name() contains only name of a file itself, not a full path. 2023-08-05 05:07:59 +04:00
Alexander Andreev df8baf153b
Replaced deprecated ioutil.ReadDir() with os.ReadDir() + os.Stat() in filesize.go. 2023-08-05 05:03:34 +04:00
Alexander Andreev b513a5ff1d
Found that in an Error() func status code wasn't set. Fixed that. 2023-08-05 04:54:52 +04:00
Alexander Andreev c2278fdd2e
A little refactoring in handlers.go. 2023-08-05 04:46:25 +04:00
Alexander Andreev df3c5e0678
Oops, use Error() func instead of direct call of ErrorXXX(). 2023-08-05 04:26:11 +04:00
Alexander Andreev 266cf9dbb0
Removed nospace.jade. Replaced by errorXXX.jade. 2023-08-05 04:22:27 +04:00
Alexander Andreev 6b56037ded
Updated httpr dependency. 2023-08-05 04:17:17 +04:00
Alexander Andreev 9e20c424be
Version was set to 23.31.0. 2023-08-01 16:52:08 +04:00
Alexander Andreev d77552049a
In PKGBUILD build() removed spaces between lines. 2023-08-01 16:51:30 +04:00
Alexander Andreev 59e0629597
On index page only available storage is shown. 2023-08-01 16:50:39 +04:00
Alexander Andreev 6b8a7dffc7
In .service files flags was replaced by the new ones. 2023-08-01 16:46:50 +04:00
Alexander Andreev 084872be61
Returned Onion-Location header, and removed a separate I2P listen statement. 2023-08-01 16:44:30 +04:00
Alexander Andreev 5590b0ad71
In upload service flags was renamed. 2023-08-01 16:43:41 +04:00
Alexander Andreev e430f0bfc9
ioutil package is deprecated, so its ReadDir() was replaced by os. ReadDir() and os.Stat(). Also fixed existing error messages a little. 2023-08-01 16:42:39 +04:00
Alexander Andreev 1c7f03053f
In clean service: args are renamed: upload-dir -> dir, keep-for -> expiry. 2023-08-01 16:40:31 +04:00
22 changed files with 214 additions and 211 deletions

View File

@ -5,8 +5,9 @@ SYSDDIR_=${shell pkg-config systemd --variable=systemdsystemunitdir}
SYSDDIR=${SYSDDIR_:/%=%}
DESTDIR=/
VERSION=23.24.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

View File

@ -1,6 +1,6 @@
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
pkgname=dwelling-upload
pkgver=23.24.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/"
}

View File

@ -1,4 +0,0 @@
# sysusers.d
g dwupload - -
u dwupload - -
m dwupload dwupload

View File

@ -3,7 +3,6 @@ package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path"
@ -11,10 +10,10 @@ import (
)
var (
uploadDir *string = flag.String("upload-dir", "/srv/upload", "path to a directory where uploaded files are stored")
keepForHours *int64 = flag.Int64("keep-for", 36, "keep files for this much hours")
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 *bool = flag.Bool("v", false, "show version")
showVersion = flag.Bool("v", false, "show version")
)
var version string
@ -28,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(*keepForHours)*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)
}
}
}

View File

@ -1,30 +1,28 @@
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)")
uploadDir *string = flag.String("upload-dir", "/srv/upload", "directory where uploaded files are stored")
keepForHours *int = flag.Int("keep-for", 36, "keep files for this much hours")
storageSize *int64 = flag.Int64("storage", 102400, "storage size in MiB for uploads")
fileSizeLimit *int64 = flag.Int64("file-size", 128, "max. size in MiB for files")
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")
)
@ -40,36 +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)
}
if ap.Addr().Is4() {
network = "tcp4"
} else {
network = "tcp6"
}
}
watcha, err := watcher.NewInotifyWatcher()
if err != nil {
log.Fatalln(err)
}
defer watcha.Close()
if err := watcha.AddWatch(*uploadDir, watcher.CrDelMask); err != nil {
log.Fatalln(err)
}
uploadDirNotify := make(chan uint32)
go watcha.WatchForMask(uploadDirNotify, watcher.CrDelMask)
hashSalt, err := os.ReadFile(path.Join(os.Getenv("CREDENTIALS_DIRECTORY"), "salt"))
if err != nil {
log.Fatalln("failed to read hash salt file:", err)
@ -90,39 +58,52 @@ func main() {
log.Fatalf("failed to get initial size of %s: %s", *uploadDir, err)
}
hand := http.NewUploadHandlers(logFile, *uploadDir, &uploadDirSize, string(hashSalt),
*keepForHours, *storageSize, *fileSizeLimit)
hand := duihttp.NewUploadHandlers(logFile, *uploadDir, &uploadDirSize, string(hashSalt),
*expiry, *storageSize, *fileSize)
r := httpr.New()
r.NotFoundHandler = func(w nethttp.ResponseWriter, r *nethttp.Request) {
http.Error(w, r, nethttp.StatusNotFound, "")
r.NotFoundHandler = func(w http.ResponseWriter, r *http.Request) {
duihttp.Error(w, r, "", http.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.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(nethttp.MethodGet, "/robots.txt", http.RobotsTxt)
r.Handler(nethttp.MethodGet, "/favicon.svg", http.Favicon)
r.Handler(http.MethodGet, "/robots.txt", duihttp.RobotsTxt)
r.Handler(http.MethodGet, "/favicon.svg", duihttp.Favicon)
srv := http.NewHttpServer(r)
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)
}
}()
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)
watcha, err := watcher.NewInotifyWatcher()
if err != nil {
log.Fatalln(err)
}
defer watcha.Close()
if err := watcha.AddWatch(*uploadDir, watcher.CrDelMask); err != nil {
log.Fatalln(err)
}
uploadDirNotify := make(chan uint32)
go watcha.WatchForMask(uploadDirNotify, watcher.CrDelMask)
go func() {
for {
select {

View File

@ -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
View File

@ -4,4 +4,4 @@ go 1.17
require github.com/pkg/errors v0.9.1
require git.arav.su/Arav/httpr v0.2.0
require git.arav.su/Arav/httpr v0.3.1

4
go.sum
View File

@ -1,4 +1,4 @@
git.arav.su/Arav/httpr v0.2.0 h1:rtwUVl4ZDfvMf9DeLktxvups5GDY0ARVcUuUHR7Eb9E=
git.arav.su/Arav/httpr v0.2.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=

View File

@ -3,9 +3,8 @@ Description=dwelling-upload-clean
[Service]
Type=oneshot
User=dwupload
Group=dwupload
ExecStart=/usr/bin/dwelling-upload-clean -upload-dir /srv/upload -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

View File

@ -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/upload \
-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.
@ -29,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

View File

@ -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,14 +62,14 @@ 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 os.Remove(fHandler.Filename)
@ -81,23 +79,20 @@ func (h *UploadHandlers) Upload(w http.ResponseWriter, r *http.Request) {
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)
@ -109,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()
@ -122,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 {
@ -160,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
}
@ -171,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)
}
@ -194,22 +197,25 @@ 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())
if strings.Contains(r.UserAgent(), "curl") || strings.Contains(r.UserAgent(), "Wget") {
fmt.Fprintln(w, "File was successfully deleted.")
@ -218,12 +224,13 @@ 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)
}

View File

@ -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
}

View File

@ -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"
}

View File

@ -1,7 +1,8 @@
package utils
import (
"io/ioutil"
"os"
"path"
"strconv"
"strings"
@ -28,14 +29,18 @@ func ConvertFileSize(size int64) (float64, string, string) {
strings.Join([]string{fSizeStr, sizeSuffixes[idx]}, " ")
}
func DirectorySize(path string) (dirSz int64, err error) {
dir, err := ioutil.ReadDir(path)
func DirectorySize(dirPath string) (dirSz int64, err error) {
dir, err := os.ReadDir(dirPath)
if err != nil {
return 0, errors.Wrapf(err, "failed to compute %s directory size", path)
return 0, errors.Wrapf(err, "failed to compute %s directory size", dirPath)
}
for _, entry := range dir {
dirSz += entry.Size()
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

View File

@ -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; }
#occupied-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%; }

View File

@ -1,2 +1,3 @@
User-agent: *
Disallow: /assets/
Allow: /$
Disallow: /

View File

@ -12,9 +12,9 @@ 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

View File

@ -1,20 +1,10 @@
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)}

View File

@ -4,33 +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 #[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#occupied-space.center
h2 Occupied 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 &lt;site&gt;/&lt;hash&gt;/&lt;file&gt;.&lt;ext&gt;].
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/&lt;hash&gt;]
p Over I2P: #[code curl --proxy 127.0.0.1:4444 -XDELETE http://upload.arav.i2p/&lt;hash&gt;]
section
p Using cURL: #[code curl -XDELETE https://upload.arav.su/&lt;hash&gt;]

View File

@ -1,22 +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
center
a(href="/") Back to index page

View File

@ -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}

View File

@ -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