feat(config): support *_MB env vars for upload size limits
- Add `applyMegabytesOrBytesEnv` to accept size settings in either bytes or MB - Prefer `*_BYTES` when set, otherwise convert `*_MB` to bytes with overflow guard - Add coverage for MB-based environment overrides - Introduce `static/js/upload-popups.js` to lazy-load and cache popup templatesfeat(config): support *_MB env vars for upload size limits - Add `applyMegabytesOrBytesEnv` to accept size settings in either bytes or MB - Prefer `*_BYTES` when set, otherwise convert `*_MB` to bytes with overflow guard - Add coverage for MB-based environment overrides - Introduce `static/js/upload-popups.js` to lazy-load and cache popup templates
This commit is contained in:
3
static/popups/about.html
Normal file
3
static/popups/about.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<h3>WarpBox</h3>
|
||||
<p><strong>WarpBox</strong> was made by <strong>Daniel Legt</strong>.</p>
|
||||
<p>Temporary file boxes, terminal-friendly uploads, and old-web UI charm.</p>
|
||||
6
static/popups/clear.html
Normal file
6
static/popups/clear.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<h3>Confirm clear</h3>
|
||||
<p>This removes the current queue, resets progress, and unlocks the Start upload button.</p>
|
||||
<div class="copy-fallback-actions">
|
||||
<button class="win98-button" type="button" id="confirm-clear-yes">Clear</button>
|
||||
<button class="win98-button" type="button" id="confirm-clear-no">Cancel</button>
|
||||
</div>
|
||||
9
static/popups/cli.html
Normal file
9
static/popups/cli.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<h3>Upload with cURL</h3>
|
||||
<p>WarpBox accepts normal multipart form uploads through the compatibility endpoint:</p>
|
||||
<pre>curl \
|
||||
-F 'files=@./my-file.zip' \
|
||||
-F 'retention=1h' \
|
||||
{{ origin }}/upload
|
||||
</pre>
|
||||
<h4>Browser flow</h4>
|
||||
<p>The browser uses the manifest API: it creates a box, uploads each file, and marks failed uploads so the download page does not wait forever.</p>
|
||||
7
static/popups/copy-failed.html
Normal file
7
static/popups/copy-failed.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<h3>Clipboard access failed</h3>
|
||||
<p>The browser refused clipboard access. Copy it manually from the field below.</p>
|
||||
<textarea class="copy-fallback-text" readonly>{{ value }}</textarea>
|
||||
<div class="copy-fallback-actions">
|
||||
{{ openLink }}
|
||||
<button class="win98-button" type="button" id="fallback-close">Close</button>
|
||||
</div>
|
||||
8
static/popups/duplicate.html
Normal file
8
static/popups/duplicate.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<h3>Duplicate file names detected</h3>
|
||||
<p>These files have the same names as files already in the queue.</p>
|
||||
<ol class="duplicate-list">{{ list }}</ol>
|
||||
<p>Skip them, or append numbers so they become names like <code>file (2).zip</code>.</p>
|
||||
<div class="copy-fallback-actions">
|
||||
<button class="win98-button" type="button" id="duplicate-append">Append numbers</button>
|
||||
<button class="win98-button" type="button" id="duplicate-skip">Skip duplicates</button>
|
||||
</div>
|
||||
102
static/popups/examples.html
Normal file
102
static/popups/examples.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<h3>Upload examples</h3>
|
||||
<h4>Basic CLI upload</h4>
|
||||
<pre>curl \
|
||||
-F 'files=@./photo.png' \
|
||||
-F 'retention=24h' \
|
||||
{{ origin }}/upload
|
||||
</pre>
|
||||
<h4>Multiple files with password</h4>
|
||||
<pre>curl \
|
||||
-F 'files=@./one.png' \
|
||||
-F 'files=@./two.zip' \
|
||||
-F 'retention=1h' \
|
||||
-F 'password=secret-pass' \
|
||||
{{ origin }}/upload
|
||||
</pre>
|
||||
<h4>Go</h4>
|
||||
<pre>package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file, err := os.Open("photo.png")
|
||||
if err != nil { panic(err) }
|
||||
defer file.Close()
|
||||
|
||||
var body bytes.Buffer
|
||||
writer := multipart.NewWriter(&body)
|
||||
part, err := writer.CreateFormFile("files", "photo.png")
|
||||
if err != nil { panic(err) }
|
||||
if _, err := io.Copy(part, file); err != nil { panic(err) }
|
||||
_ = writer.WriteField("retention", "1h")
|
||||
writer.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", "{{ origin }}/upload", &body)
|
||||
if err != nil { panic(err) }
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil { panic(err) }
|
||||
defer resp.Body.Close()
|
||||
out, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
</pre>
|
||||
<h4>Java 11+ HttpClient</h4>
|
||||
<pre>import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class UploadWarpBox {
|
||||
public static void main(String[] args) throws Exception {
|
||||
String boundary = "----WarpBoxBoundary" + System.currentTimeMillis();
|
||||
Path file = Path.of("photo.png");
|
||||
byte[] prefix = ("--" + boundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"retention\"\r\n\r\n" +
|
||||
"1h\r\n" +
|
||||
"--" + boundary + "\r\n" +
|
||||
"Content-Disposition: form-data; name=\"files\"; filename=\"photo.png\"\r\n" +
|
||||
"Content-Type: application/octet-stream\r\n\r\n").getBytes();
|
||||
byte[] suffix = ("\r\n--" + boundary + "--\r\n").getBytes();
|
||||
byte[] fileBytes = Files.readAllBytes(file);
|
||||
byte[] body = new byte[prefix.length + fileBytes.length + suffix.length];
|
||||
System.arraycopy(prefix, 0, body, 0, prefix.length);
|
||||
System.arraycopy(fileBytes, 0, body, prefix.length, fileBytes.length);
|
||||
System.arraycopy(suffix, 0, body, prefix.length + fileBytes.length, suffix.length);
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("{{ origin }}/upload"))
|
||||
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
|
||||
.POST(HttpRequest.BodyPublishers.ofByteArray(body))
|
||||
.build();
|
||||
HttpResponse<String> response = HttpClient.newHttpClient()
|
||||
.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println(response.body());
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<h4>JavaScript Node.js</h4>
|
||||
<pre>import { openAsBlob } from 'node:fs';
|
||||
|
||||
const file = await openAsBlob('./photo.png');
|
||||
const form = new FormData();
|
||||
form.append('files', file, 'photo.png');
|
||||
form.append('retention', '1h');
|
||||
|
||||
const res = await fetch('{{ origin }}/upload', {
|
||||
method: 'POST',
|
||||
body: form
|
||||
});
|
||||
|
||||
console.log(await res.text());
|
||||
</pre>
|
||||
17
static/popups/faq.html
Normal file
17
static/popups/faq.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Help & FAQ</h3>
|
||||
<section class="shortcut-section">
|
||||
<h4>Keyboard shortcuts</h4>
|
||||
<ul class="shortcut-list">
|
||||
<li><span><span class="kbd">Ctrl</span> + <span class="kbd">O</span></span><span>Browse for files.</span></li>
|
||||
<li><span><span class="kbd">Ctrl</span> + <span class="kbd">U</span></span><span>Start the current upload.</span></li>
|
||||
<li><span><span class="kbd">Ctrl</span> + <span class="kbd">K</span></span><span>Copy the full cURL command.</span></li>
|
||||
<li><span><span class="kbd">Ctrl</span> + <span class="kbd">L</span></span><span>Copy the share URL after upload.</span></li>
|
||||
<li><span><span class="kbd">F1</span></span><span>Open this window.</span></li>
|
||||
<li><span><span class="kbd">Esc</span></span><span>Close menus and popups.</span></li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="faq-list">
|
||||
<div class="faq-item"><p><strong>Can I password protect uploads?</strong></p><p>Yes. Set a password in Box Options before starting the upload.</p></div>
|
||||
<div class="faq-item"><p><strong>What happens if one file fails?</strong></p><p>The failed row stays red, successful files remain available, and WarpBox marks the failed file in the manifest.</p></div>
|
||||
<div class="faq-item"><p><strong>Are all options server-backed?</strong></p><p>Expiry, password, ZIP download, and one-time download are sent to the backend. Notes like box name, custom slug, and API key mode are saved locally until backend support exists.</p></div>
|
||||
</div>
|
||||
12
static/popups/upload-limits.html
Normal file
12
static/popups/upload-limits.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<h3>Upload limits</h3>
|
||||
<div class="quota-meter-list">
|
||||
<div class="quota-meter">
|
||||
<div class="quota-meter-head"><span>Box size</span><span>{{ boxLimit }}</span></div>
|
||||
<div class="quota-meter-track"><span class="quota-meter-bar" style="width:{{ boxPercent }}%"></span></div>
|
||||
</div>
|
||||
<div class="quota-meter">
|
||||
<div class="quota-meter-head"><span>Single file</span><span>{{ fileLimit }}</span></div>
|
||||
<div class="quota-meter-track"><span class="quota-meter-bar" style="width:{{ filePercent }}%"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="quota-note">These values come from the running WarpBox configuration.</p>
|
||||
5
static/popups/warning.html
Normal file
5
static/popups/warning.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<h3>{{ title }}</h3>
|
||||
{{ content }}
|
||||
<div class="copy-fallback-actions">
|
||||
<button class="win98-button" type="button" id="fallback-close">OK</button>
|
||||
</div>
|
||||
Reference in New Issue
Block a user