1
0
Fork 0

Compare commits

...

34 Commits

Author SHA1 Message Date
Alexander Andreev df3ea75ca9
Added a benchmark for ScanDirectory(). 2023-09-20 04:56:08 +04:00
Alexander Andreev cbe0292e4c
Before allocation of these three vars try to read a dir first. 2023-09-20 04:55:38 +04:00
Alexander Andreev d6463e81e8
A struct DirStats was renamed to DirStat. 2023-09-20 04:07:24 +04:00
Alexander Andreev 99e39fc6c1
Added FLAGS, reorganised targets ${TARGET} and web/*.jade.go. Removed all target. Added clean .PHONY target, removed ${TARGET} from .PHONY. In web/*.jade.go added check for jade executable existence. 2023-09-20 04:06:54 +04:00
Alexander Andreev 5cc9b12030
Updated httpr to v0.3.2. 2023-09-20 04:04:09 +04:00
Alexander Andreev 8b39e60a01
Reorganised CSS. Set min-height for body to 100vh. 2023-08-23 04:49:30 +04:00
Alexander Andreev c06eaecbfc
Removed unused ids. 2023-08-23 04:40:39 +04:00
Alexander Andreev f23e4d713b
Moved error styling to main.css. 2023-08-23 04:40:23 +04:00
Alexander Andreev 1a5d32f1b9
Put my full name in a footer. 2023-08-23 04:33:45 +04:00
Alexander Andreev 775eef657e
Removed id and classes from a logo. 2023-08-23 04:31:57 +04:00
Alexander Andreev 06961fc7c4
Return an error text message if file handling is turned off. 2023-08-23 04:23:51 +04:00
Alexander Andreev 391f589a90
Version set to 23.32.0. 2023-08-13 04:06:34 +04:00
Alexander Andreev 404f88c099
Fix for double slashes. 2023-08-13 03:18:17 +04:00
Alexander Andreev 54bc8d744d
Updated HttpServer to the latest implementation. 2023-08-13 02:25:04 +04:00
Alexander Andreev 6cf74599cc
A separate base.jade template was made. An index.jade rewritten to use it. 2023-08-13 02:24:36 +04:00
Alexander Andreev 4657319d52
Added an error page. 2023-08-13 02:23:47 +04:00
Alexander Andreev c5ffe37c52
Updated httpr to 0.3.1. 2023-08-13 01:58:06 +04:00
Alexander Andreev 0e54693b4a
Added v to archive's name. 2023-06-13 00:15:23 +04:00
Alexander Andreev cc8634fdb5
Oh, remove this test code. 2023-06-13 00:08:12 +04:00
Alexander Andreev 8038a0d551
In a Copyright a year was changed to 2023. 2023-06-13 00:07:34 +04:00
Alexander Andreev aa64903161
Remove jade.go from a repo. 2023-06-13 00:05:05 +04:00
Alexander Andreev 15af164462
Moved to my httpr router. 2023-06-13 00:02:41 +04:00
Alexander Andreev ed62b37dbc
Version was set to 23.24.0. And Makefile and PKGBUILD structure was updated to a recently used one across my other services. 2023-06-12 23:32:54 +04:00
Alexander Andreev 80647144b5
Removed -stdlib that prevented jade.go file's creation. 2023-06-12 23:32:09 +04:00
Alexander Andreev 7d1da65f38
Set Go version to 1.16 as a minimal. 2023-06-12 23:31:34 +04:00
Alexander Andreev eaa49744af
Ignore jade.go and *.jade.go files. 2023-06-12 23:31:06 +04:00
Alexander Andreev 24e46c79e7
In footer 2022 was replaced by 2023. 2023-06-12 23:16:46 +04:00
Alexander Andreev 56399e85bb
Removed Privacy statements section and added a link to it on a main site in a footer. 2023-06-12 23:13:41 +04:00
Alexander Andreev def915607b
ssl is excessive here in nginx.conf. 2023-06-12 23:13:14 +04:00
Alexander Andreev 85e5095120
Do not keep bin directory. 2023-05-14 02:18:15 +04:00
Alexander Andreev e1107e94eb
Added go generate to Makefile. 2023-02-23 18:03:57 +04:00
Alexander Andreev 2b5c26b2db
Remove compiled template. 2023-02-23 18:02:52 +04:00
Alexander Andreev 277333b1cb
Changed arav.top to arav.su. Version up to 23.8.0. 2023-02-23 17:56:26 +04:00
Alexander Andreev bdcdecb612
HTTP server was reorganised in internal/http. 2023-01-12 04:58:54 +04:00
23 changed files with 376 additions and 534 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
bin/*
!bin/.keep
.vscode
.vscode
web/*.jade.go
web/jade.go

View File

@ -1,4 +1,4 @@
Copyright (c) 2022 Alexander "Arav" Andreev <me@arav.top>
Copyright (c) 2022,2023 Alexander "Arav" Andreev <me@arav.su>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -2,28 +2,39 @@ TARGET=dwelling-files
SYSDDIR_=${shell pkg-config systemd --variable=systemdsystemunitdir}
SYSDDIR=${SYSDDIR_:/%=%}
DESTDIR=/
LDFLAGS=-ldflags "-s -w -X main.version=22.50.0" -tags osusergo,netgo
DESTDIR:=
PREFIX:=/usr/local
all: ${TARGET}
VERSION=23.32.0
.PHONY: ${TARGET}
FLAGS=-buildmode=pie -modcacherw -mod=readonly -trimpath
LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}" -tags osusergo,netgo
${TARGET}:
go build -o bin/$@ ${LDFLAGS} cmd/$@/main.go
.PHONY: run install uninstall clean
${TARGET}: web/*.jade.go
go build -o bin/$@ ${LDFLAGS} ${FLAGS} cmd/$@/main.go
web/*.jade.go: web/templates/*.jade
ifeq (,$(wildcard $(shell go env GOPATH)/bin/jade))
go install github.com/Joker/jade/cmd/jade@latest
endif
go generate web/web.go
run:
bin/${TARGET} -file-handling -path /mnt/data -listen 127.0.0.1:19135
install:
install -Dm 0755 bin/${TARGET} ${DESTDIR}usr/bin/${TARGET}
install -Dm 0644 LICENSE ${DESTDIR}usr/share/licenses/${TARGET}/LICENSE
install -Dm 0755 bin/${TARGET} ${DESTDIR}${PREFIX}/bin/${TARGET}
install -Dm 0644 init/systemd/${TARGET}.service ${DESTDIR}${SYSDDIR}/${TARGET}.service
install -Dm 0644 init/systemd/${TARGET}.service ${DESTDIR}/${SYSDDIR}/${TARGET}.service
uninstall:
rm ${DESTDIR}usr/bin/${TARGET}
rm ${DESTDIR}usr/share/licenses/${TARGET}/LICENSE
rm ${DESTDIR}${PREFIX}/bin/${TARGET}
rm ${DESTDIR}${SYSDDIR}/${TARGET}.service
rm ${DESTDIR}/${SYSDDIR}/${TARGET}.service
clean:
rm -f web/*.jade.go
go clean

View File

View File

@ -1,30 +1,24 @@
# Maintainer: Alexander "Arav" Andreev <me@arav.top>
# Maintainer: Alexander "Arav" Andreev <me@arav.su>
pkgname=dwelling-files
pkgver=22.50.0
pkgver=23.32.0
pkgrel=1
pkgdesc="Arav's dwelling / Files"
arch=('i686' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')
url="https://git.arav.top/Arav/dwelling-files"
url="https://git.arav.su/Arav/dwelling-files"
license=('MIT')
groups=()
depends=()
makedepends=('go')
makedepends=('go>=1.16')
provides=('dwelling-files')
conflicts=('dwelling-files')
replaces=()
backup=()
options=()
install=
source=('https://git.arav.top/Arav/dwelling-files/archive/22.50.0.tar.gz')
noextract=()
source=("${pkgver}.tar.gz::https://git.arav.su/Arav/dwelling-files/archive/v${pkgver}.tar.gz")
md5sums=('SKIP')
build() {
cd "$srcdir/$pkgname"
make DESTDIR="$pkgdir/"
export GOPATH="$srcdir"/gopath
make VERSION=$pkgver DESTDIR="$pkgdir" PREFIX="/usr"
}
package() {
cd "$srcdir/$pkgname"
make DESTDIR="$pkgdir/" install
make DESTDIR="$pkgdir" PREFIX="/usr" install
}

View File

@ -1,17 +1,17 @@
package main
import (
"dwelling-files/internal/handlers"
"dwelling-files/internal/server"
dwhttp "dwelling-files/internal/http"
"dwelling-files/web"
"flag"
"fmt"
"log"
"net/netip"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"git.arav.su/Arav/httpr"
)
var version string
@ -26,33 +26,20 @@ func main() {
log.SetFlags(0)
if *showVersion {
fmt.Println("dwelling-files ver.", version, "\nCopyright (c) 2022 Alexander \"Arav\" Andreev <me@arav.top>")
fmt.Println("dwelling-files ver.", version, "\nCopyright (c) 2023 Alexander \"Arav\" Andreev <me@arav.su>")
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)
}
hand := dwhttp.New(directoryPath, !*enableFileHandler)
r := httpr.New()
if ap.Addr().Is4() {
network = "tcp4"
} else if ap.Addr().Is6() {
network = "tcp6"
}
}
r.ServeStatic("/assets/*filepath", web.Assets())
r.Handler(http.MethodGet, "/file/*filepath", hand.File)
r.Handler(http.MethodGet, "/*filepath", hand.Index)
r.Handler(http.MethodGet, "/", hand.Index)
hand := handlers.New(directoryPath, web.Assets(), !*enableFileHandler)
srv := server.NewHttpServer()
srv.GET("/*filepath", hand.Index)
if err := srv.Start(network, *listenAddress); err != nil {
srv := dwhttp.NewHttpServer(r)
if err := srv.Start(*listenAddress); err != nil {
log.Fatalln(err)
}

View File

@ -1,17 +1,17 @@
server {
listen 443 ssl http2;
listen 443 http2;
listen 127.0.0.1:8112; # I2P
listen [300:a98d:d6d0:8a08::d]:80; # Yggdrasil
server_name files.arav.top files.arav.i2p qf5e43nlhvnrutmikuvbdfj3cmtthokpbaxtkm6mjlslttzvtgm4fxid.onion;
server_name files.arav.su files.arav.i2p qf5e43nlhvnrutmikuvbdfj3cmtthokpbaxtkm6mjlslttzvtgm4fxid.onion;
access_log /var/log/nginx/dwelling/files.log main if=$nolog;
ssl_certificate /etc/letsencrypt/live/arav.top/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/arav.top/privkey.pem;
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 'none'; script-src 'self'; style-src 'self'; img-src 'self'; media-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; font-src 'self'; form-action 'self'";

4
go.mod
View File

@ -1,5 +1,5 @@
module dwelling-files
go 1.19
go 1.16
require github.com/julienschmidt/httprouter v1.3.0
require git.arav.su/Arav/httpr v0.3.2

4
go.sum
View File

@ -1,2 +1,2 @@
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
git.arav.su/Arav/httpr v0.3.2 h1:a+ifu+9+FnQe6p/Kd4kgTDKAFN6zBOJjBTMjbAuHxVk=
git.arav.su/Arav/httpr v0.3.2/go.mod h1:z0SVYwe5dBReeVuFU9QH2PmBxICJwchxqY5OfZbeVzU=

View File

@ -1,73 +0,0 @@
package handlers
import (
"dwelling-files/internal/server"
"dwelling-files/pkg/files"
"dwelling-files/pkg/utils"
"dwelling-files/web"
"log"
"net/http"
"strings"
"github.com/julienschmidt/httprouter"
)
type FilesHandlers struct {
directoryPath string
assetsServer http.Handler
fileServer http.Handler
noFileHandling bool
}
func New(directoryPath *string, assetsFS http.FileSystem, noFileHandling bool) *FilesHandlers {
var fSrv http.Handler
if noFileHandling {
fSrv = nil
} else {
fSrv = http.FileServer(http.Dir(*directoryPath))
}
return &FilesHandlers{
directoryPath: *directoryPath,
assetsServer: http.FileServer(assetsFS),
fileServer: fSrv,
noFileHandling: noFileHandling}
}
func (FilesHandlers) AssetsFS() http.FileSystem {
return web.Assets()
}
func (h *FilesHandlers) Index(w http.ResponseWriter, r *http.Request) {
path := httprouter.CleanPath(server.GetURLParam(r, "filepath"))
if strings.HasPrefix(path, "/assets") {
h.assetsServer.ServeHTTP(w, r)
return
}
if strings.HasPrefix(path, "/robots.txt") {
fc, _ := web.AssetsGetFile("robots.txt")
w.Write(fc)
return
}
if strings.HasPrefix(path, "/file") {
if h.noFileHandling {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
r.URL.Path = path[5:]
h.fileServer.ServeHTTP(w, r)
return
}
currentPath := files.CurrentPath(path)
entries, stats, err := files.ScanDirectory(h.directoryPath+path, path)
if err != nil {
log.Println(err)
return
}
web.Index(utils.MainSite(r.Host), currentPath, &stats, &entries, r, w)
}

68
internal/http/handlers.go Normal file
View File

@ -0,0 +1,68 @@
package http
import (
"dwelling-files/pkg/files"
"dwelling-files/pkg/utils"
"dwelling-files/web"
"log"
"net/http"
"strings"
"git.arav.su/Arav/httpr"
)
type FilesHandlers struct {
directoryPath string
fileServer http.Handler
noFileHandling bool
}
func New(directoryPath *string, noFileHandling bool) *FilesHandlers {
var fSrv http.Handler
if noFileHandling {
fSrv = nil
} else {
fSrv = http.FileServer(http.Dir(*directoryPath))
}
return &FilesHandlers{
directoryPath: *directoryPath,
fileServer: fSrv,
noFileHandling: noFileHandling}
}
func (h *FilesHandlers) Index(w http.ResponseWriter, r *http.Request) {
path := "/" + httpr.Param(r, "filepath")
if !strings.HasSuffix(path, "/") {
path += "/"
}
currentPath := files.CurrentPath(path)
entries, stats, err := files.ScanDirectory(h.directoryPath+path, path)
if err != nil {
log.Println(err)
Error(w, http.StatusNotFound, "", "", r.Referer())
return
}
web.Index("Files", utils.MainSite(r.Host), currentPath, &stats, &entries, r, w)
}
func (h *FilesHandlers) File(w http.ResponseWriter, r *http.Request) {
if h.noFileHandling {
Error(w, http.StatusServiceUnavailable, "File handling is turned off.", "", r.Referer())
return
}
r.URL.Path = httpr.Param(r, "filepath")
h.fileServer.ServeHTTP(w, r)
}
func RobotsTxt(w http.ResponseWriter, r *http.Request) {
fc, _ := web.AssetsGetFile("robots.txt")
w.Write(fc)
}
func Error(w http.ResponseWriter, code int, reason, message, referer string) {
w.WriteHeader(code)
web.ErrorXXX("/ "+http.StatusText(code), reason, message, referer, code, w)
}

76
internal/http/http.go Normal file
View File

@ -0,0 +1,76 @@
package http
import (
"context"
"log"
"net"
"net/http"
"net/netip"
"os"
"strings"
"time"
)
type HttpServer struct {
s http.Server
addr net.Addr
}
func NewHttpServer(r http.Handler) *HttpServer {
return &HttpServer{s: http.Server{
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
Handler: r}}
}
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
}
if listener.Addr().Network() == "unix" {
os.Chmod(address, 0777)
}
s.addr = listener.Addr()
go func() {
if err = s.s.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Fatalln(err)
}
}()
return nil
}
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
}
return nil
}

View File

@ -1,68 +0,0 @@
package server
import (
"context"
"log"
"net"
"net/http"
"os"
"time"
"github.com/julienschmidt/httprouter"
)
type HttpServer struct {
server *http.Server
router *httprouter.Router
}
func NewHttpServer() *HttpServer {
r := httprouter.New()
return &HttpServer{
server: &http.Server{
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
Handler: r,
},
router: r,
}
}
func (s *HttpServer) GET(path string, handler http.HandlerFunc) {
s.router.Handler(http.MethodGet, path, handler)
}
// GetURLParam wrapper around underlying router for getting URL parameters.
func GetURLParam(r *http.Request, param string) string {
return httprouter.ParamsFromContext(r.Context()).ByName(param)
}
func (s *HttpServer) Start(network, address string) error {
listener, err := net.Listen(network, address)
if err != nil {
return err
}
if listener.Addr().Network() == "unix" {
os.Chmod(address, 0777)
}
go func() {
if err = s.server.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Fatalln(err)
}
}()
return nil
}
func (s *HttpServer) Stop() error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := s.server.Shutdown(ctx); err != nil {
return err
}
return nil
}

View File

@ -9,7 +9,7 @@ import (
const FileDateFormat = "2006-01-02 15:04:05 MST"
type DirStats struct {
type DirStat struct {
Files int64
FilesSize string
Directories int64
@ -22,16 +22,16 @@ type DirEntry struct {
Size string
}
func ScanDirectory(path, urlBase string) (entries []DirEntry, stats DirStats, err error) {
var dirEntries []DirEntry = make([]DirEntry, 0)
var fileEntries []DirEntry = make([]DirEntry, 0)
var totalFilesSize int64 = 0
func ScanDirectory(path, urlBase string) (entries []DirEntry, stats DirStat, err error) {
dir, err := os.ReadDir(path)
if err != nil {
return
}
var dirEntries []DirEntry = make([]DirEntry, 0)
var fileEntries []DirEntry = make([]DirEntry, 0)
var totalFilesSize int64 = 0
for _, ent := range dir {
entry, _ := ent.Info()

13
pkg/files/files_test.go Normal file
View File

@ -0,0 +1,13 @@
package files
import "testing"
const path = "/mnt/data/music/Various"
const urlBase = "/srv/ftp/"
func BenchmarkScanDirectory(b *testing.B) {
for i := 0; i < b.N; i++ {
/*e, _, _ :=*/ ScanDirectory(path, urlBase)
// b.Log(e[len(e)-1], len(e))
}
}

View File

@ -16,7 +16,7 @@ func MainSite(host string) string {
return "http://[300:a98d:d6d0:8a08::f]"
}
return "https://arav.top"
return "https://arav.su"
}
// ToClientTimezone converts given time to timezone set in a

View File

@ -30,6 +30,8 @@
::placeholder { color: var(--primary-color); }
.hidden { display: none; }
a {
color: var(--primary-color);
text-decoration: none; }
@ -58,8 +60,6 @@ h2 {
font-size: 1.4rem;
margin: 1rem 0; }
.hidden { display: none; }
html { margin-left: calc(100vw - 100%); }
body {
@ -69,6 +69,7 @@ body {
font-size: 1.1rem;
margin: 0 auto;
max-width: 960px;
min-height: 100vh;
width: 98%; }
header {
@ -76,24 +77,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; }
@ -105,6 +104,60 @@ nav h1 {
section { margin-top: 1rem; }
input[name="filter"] {
background-color: var(--background-color);
border: none;
border-bottom: 1px solid var(--primary-color);
color: var(--text-color);
font: inherit;
width: 100%; }
table {
overflow-y: scroll;
width: 100%; }
tr { vertical-align: top; }
tr:hover,
tr:focus-within {
background-color: var(--primary-color);
color: white; }
tr:hover a,
tr:focus-within a { color: white; }
th { text-align: left; }
th:nth-child(2),
th:last-child {
width: 1%;
white-space: nowrap; }
th,
td { line-break: strict; }
th:nth-child(2),
td:nth-child(2) { padding: 0 1rem; }
td a {
display: block;
width: 100%; }
td a:hover { transition: none; }
td:nth-child(2),
td:last-child { white-space: nowrap; }
thead tr th.clickable { cursor: pointer; }
thead tr th.clickable:hover { color: var(--secondary-color); }
thead tr th.clickable:not(.sort-up):not(.sort-down)::after { content: '⇅'; }
thead tr th.clickable.sort-up::after { content: '↑'; }
thead tr th.clickable.sort-down::after { content: '↓'; }
#overlay {
align-items: center;
background-color: var(--overlay-background-color);
@ -152,51 +205,13 @@ section { margin-top: 1rem; }
background-color: var(--background-color);
color: var(--primary-color); }
input[name="filter"] {
background-color: var(--background-color);
border: none;
border-bottom: 1px solid var(--primary-color);
color: var(--text-color);
font: inherit;
width: 100%; }
#error {
font-size: 3.5rem;
line-height: 5rem;
text-align: center;
margin: 6rem 0; }
table { overflow-y: scroll; width: 100%; }
tr { vertical-align: top; }
tr:hover,
tr:focus-within { background-color: var(--primary-color); color: white; }
tr:hover a,
tr:focus-within a { color: white; }
th { text-align: left; }
th:nth-child(2),
th:last-child { width: 1%; white-space: nowrap; }
th,
td { line-break: strict; }
th:nth-child(2),
td:nth-child(2) { padding: 0 1rem; }
td a { display: block; width: 100%; }
td a:hover { transition: none; }
td:nth-child(2),
td:last-child { white-space: nowrap; }
thead tr th.clickable { cursor: pointer; }
thead tr th.clickable:hover { color: var(--secondary-color); }
thead tr th.clickable:not(.sort-up):not(.sort-down)::after { content: '⇅'; }
thead tr th.clickable.sort-up::after { content: '↑'; }
thead tr th.clickable.sort-down::after { content: '↓'; }
#error h1 { font-size: 8rem; }
footer {
font-size: .8rem;
@ -206,7 +221,7 @@ footer {
@media screen and (max-width: 640px) {
header { display: block; }
#logo {
header svg {
margin: 0 auto;
width: 100%; }

View File

@ -1,68 +0,0 @@
// Code generated by "jade.go"; DO NOT EDIT.
package web
import (
"dwelling-files/pkg/files"
"dwelling-files/pkg/utils"
"fmt"
"html"
"io"
"net/http"
"strconv"
)
const (
index__0 = `<!DOCTYPE html><html lang="en"><head><title>Arav's dwelling / Files</title><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><meta name="theme-color" content="#cd2682"/><meta name="description" content="My file share."/><link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg" sizes="any"/><link href="/assets/css/main.css" rel="stylesheet"/><script src="/assets/js/main.js" defer=""></script></head><body><header><svg id="logo" viewBox="0 -25 216 40"><text class="logo">Arav's dwelling</text><text class="under" y="11">Welcome to my sacred place, wanderer</text></svg><nav><a href="`
index__1 = `">Back to main website</a><h1>Files</h1></nav></header><section id="files"><span>`
index__2 = `</span><p>Files: `
index__3 = ` (`
index__4 = `); Directories: `
index__5 = `.</p><input class="hidden" type="text" name="filter" placeholder="Type in to filter this directory (case insensitive)"/><table><thead><tr><th>Name</th><th>Date</th><th>Size</th></tr><tr tabindex="`
index__6 = `"><td><a href="../">../</a></td></tr></thead><tbody>`
index__7 = `</tbody></table></section><section><span>`
index__8 = `</span></section><section id="usage"><p>On a page use up and down arrow keys to navigate through list. Use home and end keys to go to the start and end of a list. Use Ctrl+Backspace to return to a parent directory.</p><p>In an overlay use a mouse wheel to change a scale of a video or a picture. Use left and right arrow keys to go through media. Use space key to toggle pause. Use escape key to close an overlay, or click outside a media. An audio volume is being kept across site using LocalStorage API.</p></section><section id="privacy"><h2>Privacy statements</h2><p>I collect access logs that include access date and time, IP-address, User-Agent, referer URL that tells me where have you came from, request that you sent to me. In addition there are GeoIP information added based on your IP-address that includes country, region, and city for my convenience.</p><p>This site makes use of JavaScript purely for convenient functionality, like being able to watch video, listen to music, and look images in an overlay without the need to open a file in a new tab or return back.</p></section><footer>2017&mdash;2022 Arav &lt;<a href="mailto:me@arav.top">me@arav.top</a>&gt;</footer><div id="overlay"><button name="prev">&#10096;</button><div><div></div><span></span></div><button name="next">&#10097;</button></div></body></html>`
index__9 = `<tr tabindex="`
index__10 = `"><td><a href="`
index__11 = `">`
index__12 = `</a></td><td>`
index__13 = `</td><td>`
index__14 = `</td></tr>`
)
func Index(mainSite, currentPath string, stats *files.DirStats, items *[]files.DirEntry, r *http.Request, wr io.Writer) {
buffer := &WriterAsBuffer{wr}
buffer.WriteString(index__0)
buffer.WriteString(html.EscapeString(mainSite))
buffer.WriteString(index__1)
buffer.WriteString(currentPath)
buffer.WriteString(index__2)
buffer.WriteString(html.EscapeString(fmt.Sprintf("%v", stats.Files)))
buffer.WriteString(index__3)
buffer.WriteString(html.EscapeString(fmt.Sprintf("%v", stats.FilesSize)))
buffer.WriteString(index__4)
buffer.WriteString(html.EscapeString(fmt.Sprintf("%v", stats.Directories)))
buffer.WriteString(index__5)
buffer.WriteString(html.EscapeString(strconv.FormatInt(int64(0), 10)))
buffer.WriteString(index__6)
for i, item := range *items {
buffer.WriteString(index__9)
buffer.WriteString(html.EscapeString(strconv.FormatInt(int64(i+1), 10)))
buffer.WriteString(index__10)
buffer.WriteString(html.EscapeString(fmt.Sprintf("%v", item.Link)))
buffer.WriteString(index__11)
buffer.WriteString(html.EscapeString(fmt.Sprintf("%v", item.Name)))
buffer.WriteString(index__12)
buffer.WriteString(utils.ToClientTimezone(item.Datetime, r).Format(files.FileDateFormat))
buffer.WriteString(index__13)
buffer.WriteString(html.EscapeString(fmt.Sprintf("%v", item.Size)))
buffer.WriteString(index__14)
}
buffer.WriteString(index__7)
buffer.WriteString(currentPath)
buffer.WriteString(index__8)
}

View File

@ -1,141 +0,0 @@
// Code generated by "jade.go"; DO NOT EDIT.
package web
import (
"bytes"
"io"
"strconv"
)
var (
escaped = []byte{'<', '>', '"', '\'', '&'}
replacing = []string{"&lt;", "&gt;", "&#34;", "&#39;", "&amp;"}
)
func WriteEscString(st string, buffer *WriterAsBuffer) {
for i := 0; i < len(st); i++ {
if n := bytes.IndexByte(escaped, st[i]); n >= 0 {
buffer.WriteString(replacing[n])
} else {
buffer.WriteByte(st[i])
}
}
}
type WriterAsBuffer struct {
io.Writer
}
func (w *WriterAsBuffer) WriteString(s string) (n int, err error) {
n, err = w.Write([]byte(s))
return
}
func (w *WriterAsBuffer) WriteByte(b byte) (err error) {
_, err = w.Write([]byte{b})
return
}
type stringer interface {
String() string
}
func WriteAll(a interface{}, escape bool, buffer *WriterAsBuffer) {
switch v := a.(type) {
case string:
if escape {
WriteEscString(v, buffer)
} else {
buffer.WriteString(v)
}
case int:
WriteInt(int64(v), buffer)
case int8:
WriteInt(int64(v), buffer)
case int16:
WriteInt(int64(v), buffer)
case int32:
WriteInt(int64(v), buffer)
case int64:
WriteInt(v, buffer)
case uint:
WriteUint(uint64(v), buffer)
case uint8:
WriteUint(uint64(v), buffer)
case uint16:
WriteUint(uint64(v), buffer)
case uint32:
WriteUint(uint64(v), buffer)
case uint64:
WriteUint(v, buffer)
case float32:
buffer.WriteString(strconv.FormatFloat(float64(v), 'f', -1, 64))
case float64:
buffer.WriteString(strconv.FormatFloat(v, 'f', -1, 64))
case bool:
WriteBool(v, buffer)
case stringer:
if escape {
WriteEscString(v.String(), buffer)
} else {
buffer.WriteString(v.String())
}
default:
buffer.WriteString("\n<<< unprinted type, fmt.Stringer implementation needed >>>\n")
}
}
func ternary(condition bool, iftrue, iffalse interface{}) interface{} {
if condition {
return iftrue
} else {
return iffalse
}
}
// Used part of go source:
// https://github.com/golang/go/blob/master/src/strconv/itoa.go
func WriteUint(u uint64, buffer *WriterAsBuffer) {
var a [64 + 1]byte
i := len(a)
if ^uintptr(0)>>32 == 0 {
for u > uint64(^uintptr(0)) {
q := u / 1e9
us := uintptr(u - q*1e9)
for j := 9; j > 0; j-- {
i--
qs := us / 10
a[i] = byte(us - qs*10 + '0')
us = qs
}
u = q
}
}
us := uintptr(u)
for us >= 10 {
i--
q := us / 10
a[i] = byte(us - q*10 + '0')
us = q
}
i--
a[i] = byte(us + '0')
buffer.Write(a[i:])
}
func WriteInt(i int64, buffer *WriterAsBuffer) {
if i < 0 {
buffer.WriteByte('-')
i = -i
}
WriteUint(uint64(i), buffer)
}
func WriteBool(b bool, buffer *WriterAsBuffer) {
if b {
buffer.WriteString("true")
return
}
buffer.WriteString("false")
}

23
web/templates/base.jade Normal file
View File

@ -0,0 +1,23 @@
doctype html
html(lang='en')
head
block head
title Arav's dwelling / #{title}
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible' content='IE=edge')
meta(name='viewport' content='width=device-width, initial-scale=1.0')
meta(name='theme-color' content='#cd2682')
meta(name='description' content='My file share.')
link(rel='icon' type='image/svg+xml' href='/assets/img/favicon.svg' sizes='any')
link(href='/assets/css/main.css' rel='stylesheet')
script(src='/assets/js/main.js' defer='')
body
header
svg(viewBox='0 -25 216 40')
text Arav's dwelling
text(y='11') Welcome to my sacred place, wanderer
nav
block nav
block content
footer
| 2017&mdash;2023 Alexander &quot;Arav&quot; Andreev &lt;#[a(href='mailto:me@arav.su') me@arav.su]&gt; #[a(href='/privacy') Privacy statements]

View File

@ -0,0 +1,21 @@
extends base.jade
block meta_description
meta(name='description' content=http.StatusText(code))
block nav
a(href='/') Back to index page
h1 #{http.StatusText(code)}
block content
:go:func ErrorXXX(title, reason, message, referer string, code int)
section#error
h1 #{code}
| #{http.StatusText(code)}
if reason != ""
p #{reason}
if message != ""
p #{message}
if referer != ""
section
h2 #[a(href=referer) Go back]

View File

@ -1,60 +1,39 @@
:go:func Index(mainSite, currentPath string, stats *files.DirStats, items *[]files.DirEntry, r *http.Request)
extends base.jade
:go:import "dwelling-files/pkg/files"
:go:import "dwelling-files/pkg/utils"
block nav
a(href=mainSite) Back to main website
h1 Files
doctype html
html(lang='en')
head
title Arav's dwelling / Files
meta(charset='utf-8')
meta(http-equiv='X-UA-Compatible' content='IE=edge')
meta(name='viewport' content='width=device-width, initial-scale=1.0')
meta(name='theme-color' content='#cd2682')
meta(name='description' content='My file share.')
link(rel='icon' type='image/svg+xml' href='/assets/img/favicon.svg' sizes='any')
link(href='/assets/css/main.css' rel='stylesheet')
script(src='/assets/js/main.js' defer='')
body
header
svg#logo(viewBox='0 -25 216 40')
text.logo Arav's dwelling
text.under(y='11') Welcome to my sacred place, wanderer
nav
a(href=mainSite) Back to main website
h1 Files
section#files
span!= currentPath
p Files: #{stats.Files} (#{stats.FilesSize}); Directories: #{stats.Directories}.
input.hidden(type="text", name="filter" placeholder="Type in to filter this directory (case insensitive)")
table
thead
tr
th Name
th Date
th Size
tr(tabindex=0)
td #[a(href="../") ../]
tbody
each item, i in *items
tr(tabindex=i+1)
td #[a(href=item.Link) #{item.Name}]
td!= utils.ToClientTimezone(item.Datetime, r).Format(files.FileDateFormat)
td= item.Size
section
span!= currentPath
section#usage
p On a page use up and down arrow keys to navigate through list. Use home and end keys to go to the start and end of a list. Use Ctrl+Backspace to return to a parent directory.
p In an overlay use a mouse wheel to change a scale of a video or a picture. Use left and right arrow keys to go through media. Use space key to toggle pause. Use escape key to close an overlay, or click outside a media. An audio volume is being kept across site using LocalStorage API.
section#privacy
h2 Privacy statements
p I collect access logs that include access date and time, IP-address, User-Agent, referer URL that tells me where have you came from, request that you sent to me. In addition there are GeoIP information added based on your IP-address that includes country, region, and city for my convenience.
p This site makes use of JavaScript purely for convenient functionality, like being able to watch video, listen to music, and look images in an overlay without the need to open a file in a new tab or return back.
footer
| 2017&mdash;2022 Arav &lt;#[a(href='mailto:me@arav.top') me@arav.top]&gt;
div#overlay
button(name='prev') &#10096;
block content
:go:func Index(title, mainSite, currentPath string, stats *files.DirStat, items *[]files.DirEntry, r *http.Request)
:go:import "dwelling-files/pkg/files"
:go:import "dwelling-files/pkg/utils"
section
span!= currentPath
p Files: #{stats.Files} (#{stats.FilesSize}); Directories: #{stats.Directories}.
input.hidden(type="text", name="filter" placeholder="Type in to filter this directory (case insensitive)")
table
thead
tr
th Name
th Date
th Size
tr(tabindex=0)
td #[a(href="../") ../]
tbody
each item, i in *items
tr(tabindex=i+1)
td #[a(href=item.Link) #{item.Name}]
td!= utils.ToClientTimezone(item.Datetime, r).Format(files.FileDateFormat)
td= item.Size
section
span!= currentPath
section
p On a page use up and down arrow keys to navigate through list. Use home and end keys to go to the start and end of a list. Use Ctrl+Backspace to return to a parent directory.
p In an overlay use a mouse wheel to change a scale of a video or a picture. Use left and right arrow keys to go through media. Use space key to toggle pause. Use escape key to close an overlay, or click outside a media. An audio volume is being kept across site using LocalStorage API.
div#overlay
button(name='prev') &#10096;
div
div
div
span
button(name='next') &#10097;
span
button(name='next') &#10097;

View File

@ -2,16 +2,19 @@ package web
import (
"embed"
"io/fs"
"net/http"
)
//go:generate $GOPATH/bin/jade -pkg=web -stdlib -stdbuf -writer templates/index.jade
//go:generate $GOPATH/bin/jade -pkg=web -stdbuf -writer templates/index.jade
//go:generate $GOPATH/bin/jade -pkg=web -stdbuf -writer templates/errorXXX.jade
//go:embed assets
var assetsDir embed.FS
func Assets() http.FileSystem {
return http.FS(assetsDir)
f, _ := fs.Sub(assetsDir, "assets")
return http.FS(f)
}
func AssetsGetFile(path string) ([]byte, error) {