upload: basic folder upload support
This commit is contained in:
@@ -4,9 +4,9 @@ go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/Oudwins/tailwind-merge-go v0.2.1 // indirect
|
||||
github.com/a-h/templ v0.3.943 // indirect
|
||||
github.com/a-h/templ v0.3.1020 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/valkey-io/valkey-go v1.0.64 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@ github.com/Oudwins/tailwind-merge-go v0.2.1 h1:jxRaEqGtwwwF48UuFIQ8g8XT7YSualNuG
|
||||
github.com/Oudwins/tailwind-merge-go v0.2.1/go.mod h1:kkZodgOPvZQ8f7SIrlWkG/w1g9JTbtnptnePIh3V72U=
|
||||
github.com/a-h/templ v0.3.943 h1:o+mT/4yqhZ33F3ootBiHwaY4HM5EVaOJfIshvd5UNTY=
|
||||
github.com/a-h/templ v0.3.943/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
|
||||
github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw=
|
||||
github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@@ -10,3 +12,5 @@ github.com/valkey-io/valkey-go v1.0.64 h1:3u4+b6D6zs9JQs254TLy4LqitCMHHr9XorP9GG
|
||||
github.com/valkey-io/valkey-go v1.0.64/go.mod h1:bHmwjIEOrGq/ubOJfh5uMRs7Xj6mV3mQ/ZXUbmqpjqY=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ ENV GOARCH=amd64
|
||||
|
||||
RUN apk add git
|
||||
WORKDIR /src/
|
||||
RUN git clone https://git.hafen.run/lukas/oauth-guard.git
|
||||
RUN git clone https://git.pdx.land/lukas/oauth-guard.git
|
||||
WORKDIR /src/oauth-guard
|
||||
RUN go mod download -x
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -18,6 +19,7 @@ func UploadFiles(client valkey.Client) func(w http.ResponseWriter, r *http.Reque
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseMultipartForm(125_000_000) // 1G max memory usage
|
||||
if err != nil {
|
||||
log.Println("error in parsing form: ", err.Error())
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -26,6 +28,7 @@ func UploadFiles(client valkey.Client) func(w http.ResponseWriter, r *http.Reque
|
||||
expiry_values := form.Value["expiry"]
|
||||
expiry, err := time.ParseDuration(expiry_values[0])
|
||||
if err != nil {
|
||||
log.Println("error in getting expiry: ", err.Error())
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ templ:
|
||||
# Run air for Go hot reload
|
||||
server:
|
||||
air \
|
||||
--build.cmd "go build -o tmp/bin/main ./main.go" \
|
||||
--build.bin "tmp/bin/main" \
|
||||
--build.cmd "go build -o tmp/main ." \
|
||||
--build.bin "tmp/main" \
|
||||
--build.delay "100" \
|
||||
--build.exclude_dir "node_modules" \
|
||||
--build.include_ext "go" \
|
||||
|
||||
+61
-20
@@ -37,6 +37,7 @@ templ Upload(expirations []Expiry, uploadedLink string) {
|
||||
let drop_zone = document.getElementById("drop_zone");
|
||||
let desc = document.getElementById("desc");
|
||||
let fileInput = document.getElementById("files");
|
||||
|
||||
function updateDescription(len) {
|
||||
desc.innerText = `${len} File`
|
||||
if (len > 1) {
|
||||
@@ -44,32 +45,72 @@ templ Upload(expirations []Expiry, uploadedLink string) {
|
||||
}
|
||||
desc.innerText += " Attached"
|
||||
}
|
||||
|
||||
fileInput.addEventListener("change", () => {
|
||||
updateDescription(fileInput.files.length)
|
||||
updateDescription(fileInput.files.length);
|
||||
});
|
||||
drop_zone.addEventListener("click", function (ev) {
|
||||
fileInput.click();
|
||||
})
|
||||
drop_zone.addEventListener("drop", function (ev) {
|
||||
|
||||
drop_zone.addEventListener("click", () => fileInput.click());
|
||||
|
||||
drop_zone.addEventListener("dragover", (e) => {
|
||||
e.preventDefault();
|
||||
drop_zone.classList.add('dragover');
|
||||
});
|
||||
|
||||
drop_zone.addEventListener("dragleave", (e) => {
|
||||
e.preventDefault();
|
||||
drop_zone.classList.remove('dragover');
|
||||
});
|
||||
|
||||
drop_zone.addEventListener("drop", async (ev) => {
|
||||
ev.preventDefault();
|
||||
drop_zone.classList.remove('dragover');
|
||||
const dt = ev.dataTransfer;
|
||||
if (!dt) return
|
||||
|
||||
const files = Array.from(dt.files); // iterable
|
||||
const items = ev.dataTransfer?.items;
|
||||
if (!items) return;
|
||||
|
||||
const newDT = new DataTransfer();
|
||||
files.forEach(f => newDT.items.add(f));
|
||||
fileInput.files = newDT.files;
|
||||
updateDescription(fileInput.files.length)
|
||||
});
|
||||
drop_zone.addEventListener("dragover", function (e) {
|
||||
drop_zone.classList.add('dragover');
|
||||
e.preventDefault();
|
||||
});
|
||||
drop_zone.addEventListener("dragleave", function (e) {
|
||||
drop_zone.classList.remove('dragover');
|
||||
e.preventDefault();
|
||||
const collected = [];
|
||||
|
||||
const readEntries = (reader) => new Promise((resolve, reject) => reader.readEntries(resolve, reject));
|
||||
const getFile = (entry) => new Promise((resolve, reject) => entry.file(resolve, reject));
|
||||
|
||||
async function traverse(entry, path = "") {
|
||||
if (entry.isFile) {
|
||||
const file = await getFile(entry);
|
||||
Object.defineProperty(file, "webkitRelativePath", {
|
||||
value: path + file.name,
|
||||
configurable: true });
|
||||
collected.push(file);
|
||||
} else if (entry.isDirectory) {
|
||||
const reader = entry.createReader();
|
||||
const prefix = path + entry.name + "/";
|
||||
let entries;
|
||||
do {
|
||||
entries = await readEntries(reader);
|
||||
for (const child of entries) {
|
||||
await traverse(child, prefix);
|
||||
}
|
||||
} while (entries.length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const entry = items[i].webkitGetAsEntry();
|
||||
if (entry) {
|
||||
tasks.push(traverse(entry));
|
||||
} else {
|
||||
const file = items[i].getAsFile();
|
||||
if (file) collected.push(file);
|
||||
}
|
||||
}
|
||||
await Promise.all(tasks);
|
||||
|
||||
const dt = new DataTransfer();
|
||||
collected.forEach(f => dt.items.add(f));
|
||||
fileInput.files = dt.files;
|
||||
|
||||
updateDescription(fileInput.files.length);
|
||||
});
|
||||
</script>
|
||||
<style>.dragover {border-color: #00C950;}</style>
|
||||
|
||||
Reference in New Issue
Block a user