Skip to content

Commit

Permalink
feat: upload multiple folders and single files together
Browse files Browse the repository at this point in the history
  • Loading branch information
tsukinoko-kun committed Sep 3, 2024
1 parent 3316003 commit a5d6e23
Show file tree
Hide file tree
Showing 16 changed files with 359 additions and 80 deletions.
4 changes: 2 additions & 2 deletions .air.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ root = "."
tmp_dir = "tmp"

[build]
args_bin = ["--port", "4321"]
args_bin = ["--port", "4321", "--path", "~/Downloads/portal"]
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
Expand All @@ -13,7 +13,7 @@ exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = [".", "public"]
include_ext = ["go", "html", "css", "js"]
include_ext = ["go", "html", "css", "js", "webp", "svg"]
include_file = ["main.go"]
kill_delay = "1s"
log = "build-errors.log"
Expand Down
81 changes: 57 additions & 24 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,26 @@ import (
"net"
"net/http"
"os"
"path/filepath"
"time"
)

const (
chunkSize = 1024 * 1024 // 1MB
)

var upgrader = websocket.Upgrader{
ReadBufferSize: chunkSize,
var (
upgrader = websocket.Upgrader{
ReadBufferSize: chunkSize,
}
wd string
)

func init() {
var err error
if wd, err = os.Getwd(); err != nil {
log.Fatal("failed to get working directory", "err", err)
}
}

type (
Expand All @@ -25,6 +37,8 @@ type (
Name string `json:"name"`
// Size is the size of the file in bytes.
Size int `json:"size"`
// LastModified is the last modified time of the file.
LastModified int64 `json:"lastModified"`
}
)

Expand All @@ -45,12 +59,27 @@ func wsHandler(w http.ResponseWriter, r *http.Request) {
}
log.Info("received header", "header", header)

file, err := os.Create(header.Name)
p := filepath.Join(wd, header.Name)

// check if file is inside wd
if relPath, err := filepath.Rel(wd, p); err != nil || relPath == ".." || relPath[:2] == ".." {
log.Error("file is outside working directory", "path", p)
return
}

// create parent directories
if err := os.MkdirAll(filepath.Dir(p), 0777); err != nil {
log.Error("failed to create parent directories", "err", err)
return
}

// create file
f, err := os.Create(p)
if err != nil {
log.Error("failed to create file", "err", err)
return
}
defer file.Close()
defer f.Close()

// Send READY signal to start receiving file chunks
err = conn.WriteMessage(websocket.TextMessage, []byte("READY"))
Expand All @@ -76,7 +105,7 @@ func wsHandler(w http.ResponseWriter, r *http.Request) {
}

if messageType == websocket.BinaryMessage {
_, err = file.Write(p)
_, err = f.Write(p)
if err != nil {
log.Error("failed to write to file", "err", err)
return
Expand All @@ -90,6 +119,12 @@ func wsHandler(w http.ResponseWriter, r *http.Request) {
}
}

// set last modified time
lastModified := time.UnixMilli(header.LastModified)
if err := os.Chtimes(p, lastModified, lastModified); err != nil {
log.Error("failed to set last modified time", "err", err)
}

// send EOF to client to signal that the file has been received
err = conn.WriteMessage(websocket.TextMessage, []byte("EOF"))
if err != nil {
Expand Down Expand Up @@ -119,28 +154,26 @@ func getPublicIP() (string, error) {

func main() {
port := flag.Int("port", 0, "port to listen on")
path := flag.String("path", ".", "path to save files")
flag.Parse()

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/backdrop.webp":
w.Header().Set("Content-Type", "image/webp")
_, _ = w.Write(public.BackdropWebP)
case "/favicon.svg":
w.Header().Set("Content-Type", "image/svg+xml")
_, _ = w.Write(public.FaviconSVG)
case "/index.css":
w.Header().Set("Content-Type", "text/css")
_, _ = w.Write(public.IndexCSS)
case "/":
w.Header().Set("Content-Type", "text/html")
_, _ = w.Write(public.IndexHTML)
case "/index.js":
w.Header().Set("Content-Type", "application/javascript")
_, _ = w.Write(public.IndexJS)
default:
http.NotFound(w, r)
if *path != "." {
if err := os.MkdirAll(*path, 0777); err != nil {
log.Fatal("failed to create directory", "path", path, "err", err)
return
}
if err := os.Chdir(*path); err != nil {
log.Fatal("failed to change directory", "err", err)
return
}
var err error
if wd, err = os.Getwd(); err == nil {
log.Info("working directory", "path", wd)
}
}

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.FileServerFS(public.Fs).ServeHTTP(w, r)
})
http.HandleFunc("/ws", wsHandler)

Expand Down
2 changes: 1 addition & 1 deletion public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-binary.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-code.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-font.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-music.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-pdf.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-play.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/file-earmark-text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/file-earmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 40 additions & 12 deletions public/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ main {

main::before {
content: "Drop files here";
filter: drop-shadow(0 0 1rem black);
position: fixed;
display: block;
font-family: "Garamond", "Bookman Old Style", "Georgia", "Times New Roman", serif;
font-family: "Fondamento", "Garamond", "Bookman Old Style", "Georgia", "Times New Roman", cursive;
top: 5lvh;
left: 0;
width: 100dvw;
font-size: 2rem;
font-size: 3rem;
font-weight: bold;
text-align: center;
}
Expand All @@ -61,7 +62,7 @@ main::before {
content: "Sending...";
}

.ring, .particle, main::before {
.ring, .particle, .file, main::before {
pointer-events: none;
}

Expand Down Expand Up @@ -135,7 +136,7 @@ main::before {
}

.sending .ring {
background: radial-gradient(closest-side, pink, rgba(var(--magic), 1));
background: radial-gradient(closest-side, rgba(255, 200, 230, 0.25), rgba(var(--magic), 1));
}

.particle {
Expand All @@ -162,7 +163,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(50vmin, 10vmin) scale(1);
transform: translate(50vmax, 10vmax) scale(1);
opacity: 0;
}
}
Expand All @@ -176,7 +177,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(-50vmin, -13vmin) scale(1);
transform: translate(-50vmax, -13vmax) scale(1);
opacity: 0;
}
}
Expand All @@ -190,7 +191,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(-10vmin, 50vmin) scale(1);
transform: translate(-10vmax, 50vmax) scale(1);
opacity: 0;
}
}
Expand All @@ -204,7 +205,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(13vmin, -50vmin) scale(1);
transform: translate(13vmax, -50vmax) scale(1);
opacity: 0;
}
}
Expand All @@ -218,7 +219,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(-2vmin, -40vmin) scale(1);
transform: translate(-10vmax, -25vmax) scale(1);
opacity: 0;
}
}
Expand All @@ -232,7 +233,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(40vmin, 2vmin) scale(1);
transform: translate(25vmax, 10vmax) scale(1);
opacity: 0;
}
}
Expand All @@ -246,7 +247,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(-40vmin, -2vmin) scale(1);
transform: translate(-25vmax, -10vmax) scale(1);
opacity: 0;
}
}
Expand All @@ -260,7 +261,7 @@ main::before {
opacity: 1;
}
100% {
transform: translate(2vmin, 40vmin) scale(1);
transform: translate(10vmax, 25vmax) scale(1);
opacity: 0;
}
}
Expand Down Expand Up @@ -306,11 +307,38 @@ main::before {
}

.particle {
animation-direction: normal;
animation-duration: 3s;
}

.drag-over .particle {
animation-direction: reverse;
animation-duration: 2s;
}

.sending .particle {
animation-direction: reverse;
animation-duration: 1s;
}

#file-icons {
z-index: 1000;
position: fixed;
top: 0;
left: 0;
padding: min(10vmin, 128px);
width: 100lvw;
height: 100lvh;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 1vmin;
pointer-events: none;
}

#file-icons .file {
display: block;
width: 5vmin;
aspect-ratio: auto;
}
10 changes: 8 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<title>Portal</title>
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/index.css" rel="stylesheet">
<link href="https://fonts.googleapis.com" rel="preconnect">
<link crossorigin href="https://fonts.gstatic.com" rel="preconnect">
<link href="https://fonts.googleapis.com/css2?family=Fondamento&display=swap" rel="stylesheet">
</head>
<body>
<main>
<input id="fileInput" multiple title="" type="file">
<script src="/index.js"></script>

<div id="file-icons"></div>

<div class="ring"></div>

Expand All @@ -22,6 +26,8 @@
<div class="particle particle-f"></div>
<div class="particle particle-g"></div>
<div class="particle particle-h"></div>

<script src="/index.js"></script>
</main>
</body>
</html>
Loading

0 comments on commit a5d6e23

Please sign in to comment.