init:first commit
BIN
assets/images/diricon.png
Normal file
After Width: | Height: | Size: 627 B |
BIN
assets/images/fileicon.png
Normal file
After Width: | Height: | Size: 642 B |
BIN
assets/images/leech.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/images/logo-64px-padded.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/images/src/Leech-Big-Colored.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
assets/images/src/Leech-Big-Scaled.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/images/src/Leech-Big.png
Normal file
After Width: | Height: | Size: 159 KiB |
8
config/config.go
Normal file
@ -0,0 +1,8 @@
|
||||
package config
|
||||
|
||||
var Host = ":3125"
|
||||
var InstallDir = "./"
|
||||
|
||||
var ServeDirs = map[string]string{
|
||||
"leech": "/home/ontake/Dev/go/leech",
|
||||
}
|
22
go.mod
Normal file
@ -0,0 +1,22 @@
|
||||
module leech
|
||||
|
||||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/gofiber/fiber/v2 v2.52.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
)
|
29
go.sum
Normal file
@ -0,0 +1,29 @@
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
126
html/html.go
Normal file
@ -0,0 +1,126 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
IsDir bool
|
||||
Name string
|
||||
Size int64
|
||||
}
|
||||
|
||||
func FileListPage(req string, entries []Entry) string {
|
||||
header := `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Stylesheet-->
|
||||
<style>
|
||||
@import url("https://fonts.googleapis.com/css2?family=DotGothic16&display=swap");
|
||||
:root {
|
||||
--text-color: #9e5272;
|
||||
--text-color-accent: #793a73;
|
||||
|
||||
--bg-color: #181a1b;
|
||||
--bg-sec-color: #f0d8e3;
|
||||
|
||||
color: var(--text-color);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
font-family: "DotGothic16", sans-serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
background-color: var(--bg-color);
|
||||
background-image: url(assets/images/logo-64px-padded.png);
|
||||
background-blend-mode: difference;
|
||||
text-align: center;
|
||||
}
|
||||
#content,#footer {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
min-width: 720px;
|
||||
max-width: 1280px;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#content {
|
||||
min-height: calc(100vh - 256px);
|
||||
padding: 128px 32px 32px 32px;
|
||||
text-align: center;
|
||||
}
|
||||
#footer {
|
||||
text-align: left;
|
||||
}
|
||||
a {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
}
|
||||
.entry {
|
||||
display: flex;
|
||||
}
|
||||
.entry-name {
|
||||
margin-right: auto;
|
||||
}
|
||||
#leech {
|
||||
image-rendering: pixelated;
|
||||
transform-origin: center center;
|
||||
height:256px;
|
||||
width:auto;
|
||||
}
|
||||
</style>
|
||||
<!-- Title Element-->
|
||||
<title>home | leech.ontake.dev</title>
|
||||
<!-- Icon-->
|
||||
<link rel="icon" type="image/webp" href="/assets/images/leech.png" />
|
||||
<!-- Metadata-->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://leech.ontake.dev" />
|
||||
<meta property="og:title" content="home | leech.ontake.dev" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.1" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"><img src="/assets/images/leech.png" id="leech"></img>`
|
||||
|
||||
footer := `</div><div id="footer">powered by leech. ontake. 2024.</div></body></html>`
|
||||
|
||||
body := ""
|
||||
entriesWithParent := []Entry{}
|
||||
if req != "" {
|
||||
entriesWithParent = append(entriesWithParent, Entry{
|
||||
IsDir: true,
|
||||
Name: "..",
|
||||
Size: -1,
|
||||
})
|
||||
}
|
||||
entriesWithParent = append(entriesWithParent, entries...)
|
||||
for _, dirEntry := range entriesWithParent {
|
||||
formattedSize := ""
|
||||
if dirEntry.Size != -1 {
|
||||
formattedSize = humanize.Bytes(uint64(dirEntry.Size))
|
||||
}
|
||||
icon := "/assets/images/fileicon.png"
|
||||
if dirEntry.IsDir {
|
||||
icon = "/assets/images/diricon.png"
|
||||
}
|
||||
link := "/" + req + "." + dirEntry.Name
|
||||
if dirEntry.Name == ".." {
|
||||
splitReq := strings.Split(req, ".")
|
||||
link = "/" + strings.Join(splitReq[:len(splitReq)-1], ".")
|
||||
}
|
||||
if req == "" {
|
||||
link = "/" + req + dirEntry.Name
|
||||
}
|
||||
if !dirEntry.IsDir {
|
||||
link = "/serve/" + strings.ReplaceAll(req, ".", "/") + "/" + dirEntry.Name
|
||||
}
|
||||
body += fmt.Sprintf(`<a href="%s"><div class="entry"><img src="%s"></img><div class="entry-name">%s</div><div class="entry-size">%s</div></div></a>`, link, icon, dirEntry.Name, formattedSize)
|
||||
}
|
||||
return header + body + footer
|
||||
}
|
27
main.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"leech/config"
|
||||
"leech/route"
|
||||
"path"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := fiber.New()
|
||||
app.Use(logger.New(logger.Config{
|
||||
Format: "[${ip}]:${port} ${status} - ${method} ${path}\n",
|
||||
}))
|
||||
app.Get("/:req", route.HandleList)
|
||||
app.Get("/", route.HandleList)
|
||||
|
||||
app.Static("/assets", path.Join(config.InstallDir, "assets"))
|
||||
|
||||
for dirName, dirToServe := range config.ServeDirs {
|
||||
app.Static("/serve/"+dirName, dirToServe)
|
||||
}
|
||||
|
||||
app.Listen(config.Host)
|
||||
}
|
64
route/route.go
Normal file
@ -0,0 +1,64 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"leech/config"
|
||||
"leech/html"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func HandleList(c *fiber.Ctx) error {
|
||||
req := c.Params("req")
|
||||
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
|
||||
if req == "" {
|
||||
keys := make([]string, 0, len(config.ServeDirs))
|
||||
for k := range config.ServeDirs {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return i < j
|
||||
})
|
||||
|
||||
entries := make([]html.Entry, 0, len(config.ServeDirs))
|
||||
for _, key := range keys {
|
||||
entries = append(entries, html.Entry{
|
||||
Name: key,
|
||||
Size: -1,
|
||||
IsDir: true,
|
||||
})
|
||||
}
|
||||
|
||||
return c.SendString(html.FileListPage(req, entries))
|
||||
} else {
|
||||
pathSlice := strings.Split(req, ".")
|
||||
pathBase, ok := config.ServeDirs[pathSlice[0]]
|
||||
if ok {
|
||||
pathSlice[0] = pathBase
|
||||
completePath := path.Join(pathSlice...)
|
||||
files, err := os.ReadDir(completePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
entries := []html.Entry{}
|
||||
for _, f := range files {
|
||||
dirEntryInfo, err := f.Info()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).SendString("Internal server error!")
|
||||
}
|
||||
entries = append(entries, html.Entry{
|
||||
Name: f.Name(),
|
||||
Size: dirEntryInfo.Size(),
|
||||
IsDir: f.IsDir(),
|
||||
})
|
||||
}
|
||||
return c.SendString(html.FileListPage(req, entries))
|
||||
} else {
|
||||
return c.Status(fiber.StatusNotFound).SendString("Sorry can't find that!")
|
||||
}
|
||||
}
|
||||
}
|