Replace CSS classes with semantic HTML

This commit is contained in:
2022-01-11 13:28:22 +01:00
parent c6f0421ad3
commit 8719d9b0f9
37 changed files with 1215 additions and 1279 deletions

View File

@@ -46,8 +46,11 @@ a > svg { vertical-align: middle; }
*, *::before, *::after { *, *::before, *::after {
box-sizing: border-box; box-sizing: border-box;
} }
html, body { overflow-x: hidden; } html, body {
body{ /* This makes sure that no scrollbar shows up when the menu is open on small screens*/
overflow-x: hidden;
}
body {
margin: 0; margin: 0;
font-family: system-ui, sans-serif; font-family: system-ui, sans-serif;
font-size: 17px; font-size: 17px;
@@ -56,20 +59,26 @@ body{
background-color: var(--layer_1_color); background-color: var(--layer_1_color);
padding: 0; padding: 0;
} }
.checkers { header, footer, .checkers {
background-image: url("{{bgPattern}}"); background-image: url("{{bgPattern}}");
background-color: #111111; /* Fallback */ background-color: #111111; /* Fallback */
background-color: var(--layer_1_color); background-color: var(--layer_1_color);
background-repeat: repeat; background-repeat: repeat;
background-blend-mode: luminosity; background-blend-mode: luminosity;
} }
.inset { header, footer {
padding-top: 70px; padding-top: 70px;
box-shadow: inset 1px 1px 10px 0 var(--shadow_color); box-shadow: inset 1px 1px 10px 0 var(--shadow_color);
border-radius: 12px; border-radius: 12px;
text-align: center; text-align: center;
overflow: hidden; overflow: hidden;
} }
header > h1 {
text-shadow: 1px 1px 10px var(--shadow_color);
}
footer {
padding: 200px 8px 40px 8px;
}
/* Page layout elements */ /* Page layout elements */
@@ -131,7 +140,7 @@ body{
padding: 4px; padding: 4px;
} }
} }
.limit_width { section {
position: relative; position: relative;
display: inline-block; display: inline-block;
max-width: 1000px; max-width: 1000px;
@@ -141,10 +150,6 @@ body{
text-align: left; text-align: left;
clear: both; clear: both;
} }
.page_body > h1 {
text-shadow: 1px 1px 25px #000000;
text-shadow: 1px 1px 25px var(--shadow_color);
}
/* Page contents */ /* Page contents */

View File

@@ -7,10 +7,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>This page does not exist!</h1> <h1>This page does not exist!</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
If you came here by a link from this very same website you can If you came here by a link from this very same website you can
tell me about it on tell me about it on
@@ -23,7 +23,7 @@
<p> <p>
Bye! Bye!
</p> </p>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -7,15 +7,15 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>451, Unavailable For Legal Reasons</h1> <h1>451, Unavailable For Legal Reasons</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
Hello. This file has received an abuse report and has been taken Hello. This file has received an abuse report and has been taken
down. It cannot be shared anymore. down. It cannot be shared anymore.
</p> </p>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -7,10 +7,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>You broke pixeldrain</h1> <h1>You broke pixeldrain</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
Great job. Great job.
</p> </p>
@@ -24,7 +24,7 @@
try again in a few minutes (or hours), or go back to the <a try again in a few minutes (or hours), or go back to the <a
href='/'>home page</a> and start over. href='/'>home page</a> and start over.
</p> </p>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -7,10 +7,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>Change website appearance</h1> <h1>Change website appearance</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
You can change how pixeldrain looks! Your theme choice will You can change how pixeldrain looks! Your theme choice will
be saved in a cookie. be saved in a cookie.
@@ -30,7 +30,7 @@
(Inspired by <a href="https://www.gnome-look.org/p/1441725/" target="_blank">Skeuos GTK</a>)<br/> (Inspired by <a href="https://www.gnome-look.org/p/1441725/" target="_blank">Skeuos GTK</a>)<br/>
<!--<input type="radio" id="style_sunny" name="style"><label for="style_sunny">Sunny Style</label>--> <!--<input type="radio" id="style_sunny" name="style"><label for="style_sunny">Sunny Style</label>-->
</div> </div>
</div> </section>
<script> <script>
// Style selector // Style selector

View File

@@ -14,10 +14,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>Apps</h1> <h1>Apps</h1>
</div> </header>
<div class="limit_width"> <section>
<h2>ShareX</h2> <h2>ShareX</h2>
<div class="specs"> <div class="specs">
Platform: Windows 7, 8.1 and 10 | Platform: Windows 7, 8.1 and 10 |
@@ -110,7 +110,7 @@
please send them to please send them to
<a href="mailto:support@pixeldrain.com">support@pixeldrain.com</a>. <a href="mailto:support@pixeldrain.com">support@pixeldrain.com</a>.
</p> </p>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -7,27 +7,38 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="limit_width"> {{if eq .Other "success"}}
{{if eq .Other "success"}} <header>
<h1>Success!</h1> <h1>Success!</h1>
</header>
<section>
<p> <p>
Your account's e-mail address has been updated. Your account's e-mail address has been updated.
</p> </p>
{{else if eq .Other "not_found"}} </section>
{{else if eq .Other "not_found"}}
<header>
<h1>E-mail change failed</h1> <h1>E-mail change failed</h1>
</header>
<section>
<p> <p>
This e-mail change request does not exist or has expired. This e-mail change request does not exist or has expired.
Please try again if you still want to change your e-mail Please try again if you still want to change your e-mail
address. address.
</p> </p>
{{else}} </section>
{{else}}
<header>
<h1>Error</h1> <h1>Error</h1>
</header>
<section>
<p> <p>
Something went wrong while processing this request. Please Something went wrong while processing this request. Please
try again later. try again later.
</p> </p>
{{end}} </section>
</div> {{end}}
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -7,10 +7,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>404, File Not Found!</h1> <h1>404, File Not Found!</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
This file does not exist, or it has been removed. Possible This file does not exist, or it has been removed. Possible
reasons for this are: reasons for this are:
@@ -34,7 +34,7 @@
<p> <p>
I'm sorry for the inconvenience. I'm sorry for the inconvenience.
</p> </p>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>
@@ -49,10 +49,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>404, List Not Found!</h1> <h1>404, List Not Found!</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
This list does not exist, or it has been removed. Possible This list does not exist, or it has been removed. Possible
reasons for this are: reasons for this are:
@@ -70,7 +70,7 @@
<p> <p>
I'm sorry for the inconvenience. I'm sorry for the inconvenience.
</p> </p>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -25,10 +25,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>{{.Title}}</h1> <h1>{{.Title}}</h1>
</div> </header>
<div class="limit_width"> <section>
{{if eq .Other.Type "file"}} {{if eq .Other.Type "file"}}
Download <a href="{{.APIEndpoint}}/file/{{.Other.APIResponse.ID}}?download">{{.Other.APIResponse.Name}}</a> here. Download <a href="{{.APIEndpoint}}/file/{{.Other.APIResponse.ID}}?download">{{.Other.APIResponse.Name}}</a> here.
{{else}} {{else}}
@@ -66,7 +66,7 @@
<li><a href="https://www.apple.com/safari/">Safari</a> (Mac OS)</li> <li><a href="https://www.apple.com/safari/">Safari</a> (Mac OS)</li>
<li><a href="https://www.microsoft.com/en-us/edge">Edge</a> (Windows)</li> <li><a href="https://www.microsoft.com/en-us/edge">Edge</a> (Windows)</li>
</ul> </ul>
</div> </section>
{{template "analytics"}} {{template "analytics"}}
</body> </body>
</html> </html>

View File

@@ -110,14 +110,14 @@
<body> <body>
<div id='body' class="body"> <div id='body' class="body">
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>{{.Form.Title}}</h1> <h1>{{.Form.Title}}</h1>
</div> </header>
<br/> <br/>
<div class="limit_width"> <section>
{{template "form" .Form}} {{template "form" .Form}}
<br/> <br/>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
</div> </div>

View File

@@ -7,12 +7,12 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>{{.Title}}</h1> <h1>{{.Title}}</h1>
</div> </header>
<div class="limit_width"> <section>
{{.Other}} {{.Other}}
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -1,6 +1,6 @@
{{define "page_menu"}} {{define "page_menu"}}
<button id="button_toggle_navigation" class="button_toggle_navigation" onclick="toggleMenu();"></button> <button id="button_toggle_navigation" class="button_toggle_navigation" onclick="toggleMenu();"></button>
<div id="page_navigation" class="page_navigation"> <nav id="page_navigation" class="page_navigation">
<a href="/">Home</a> <a href="/">Home</a>
<hr /> <hr />
{{if .Authenticated}}<a href="/user">{{.User.Username}}</a> {{if .Authenticated}}<a href="/user">{{.User.Username}}</a>
@@ -26,7 +26,7 @@
{{if eq .User.Subscription.ID ""}} {{if eq .User.Subscription.ID ""}}
<a href="https://pixeldrain.com/vouchercodes">Shopping discounts</a> <a href="https://pixeldrain.com/vouchercodes">Shopping discounts</a>
{{end}} {{end}}
</div> </nav>
<script> <script>
function toggleMenu() { function toggleMenu() {
var nav = document.getElementById("page_navigation"); var nav = document.getElementById("page_navigation");
@@ -54,7 +54,7 @@ function resetMenu() {
{{end}} {{end}}
{{define "page_bottom"}} {{define "page_bottom"}}
<div class="inset checkers" style="padding-top: 150px"> <footer>
<div style="display: inline-block; margin: 0 8px 0 8px;"> <div style="display: inline-block; margin: 0 8px 0 8px;">
Pixeldrain is a product by <a href="//fornaxian.tech" target="_blank">Fornaxian Technologies</a> Pixeldrain is a product by <a href="//fornaxian.tech" target="_blank">Fornaxian Technologies</a>
</div> </div>
@@ -71,8 +71,6 @@ function resetMenu() {
<span class="small_footer_text" style="font-size: .75em; line-height: .75em;"> <span class="small_footer_text" style="font-size: .75em; line-height: .75em;">
page rendered by {{.Hostname}} page rendered by {{.Hostname}}
</span> </span>
<br/> </footer>
<br/>
</div>
</div><!-- end page_body --> </div><!-- end page_body -->
{{end}} {{end}}

View File

@@ -5,24 +5,23 @@
{{template "user_style" .}} {{template "user_style" .}}
</head> </head>
<body> <body>
<div id='body' class="body"> {{template "page_top" .}}
{{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>Upload History</h1> <h1>Upload History</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
Here are all files you have previously uploaded to pixeldrain using this computer. Here are all files you have previously uploaded to pixeldrain using this computer.
This data is saved locally in your web browser and gets updated every time you upload a file through your current browser. This data is saved locally in your web browser and gets updated every time you upload a file through your current browser.
</p> </p>
<br/> <br/>
<div id="uploaded_files" class="highlight_dark"></div>
</section>
{{template "page_bottom" .}}
<div id="uploaded_files" class="highlight_dark"></div>
</div>
{{template "page_bottom" .}}
</div>
<script> <script>
let apiEndpoint = '{{.APIEndpoint}}'; let apiEndpoint = '{{.APIEndpoint}}';
{{template `util.js`}} {{template `util.js`}}

View File

@@ -93,21 +93,21 @@
</head> </head>
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset" style="padding-bottom: 60px;"> <header style="padding-bottom: 60px;">
<picture> <picture>
<source media="(max-width: 800px)" srcset="/res/img/header_orbitron.png"> <source media="(max-width: 800px)" srcset="/res/img/header_orbitron.png">
<img class="header_image" src="/res/img/header_orbitron_wide.png" alt="Header image"> <img class="header_image" src="/res/img/header_orbitron_wide.png" alt="Header image">
</picture> </picture>
</div> </header>
<!-- Svelte element --> <!-- Svelte element -->
<div id="uploader" class="page_content"></div> <div id="uploader" class="page_content"></div>
<div class="checkers inset"> <header>
<h1>What is pixeldrain?</h1> <h1>What is pixeldrain?</h1>
</div> </header>
<div class="limit_width"> <section>
<p> <p>
Pixeldrain is a file sharing website built for speed and ease of Pixeldrain is a file sharing website built for speed and ease of
use. You can upload files you want to share online to our use. You can upload files you want to share online to our
@@ -475,7 +475,7 @@
logging in head to the <a href="/user/transactions">transactions logging in head to the <a href="/user/transactions">transactions
page</a> to deposit your coins. page</a> to deposit your coins.
</p> </p>
</div> </section>
<template id="tpl_file_expiry"> <template id="tpl_file_expiry">
<p> <p>

View File

@@ -10,9 +10,9 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>My Buckets</h1> <h1>My Buckets</h1>
</div> </header>
<div id="page_content" class="page_content"></div> <div id="page_content" class="page_content"></div>
{{template "page_bottom" .}} {{template "page_bottom" .}}

View File

@@ -6,15 +6,14 @@
</head> </head>
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header>
<h1>Please confirm that you want to log out of your pixeldrain account</h1> <h1>Please confirm that you want to log out of your pixeldrain account</h1>
<br/> </header>
</div>
<br/> <br/>
<form method="POST" action="/logout"> <form method="POST" action="/logout">
<input type="submit" value="I want to log out of pixeldrain on this computer" class="button_highlight"/> <input type="submit" value="I want to log out of pixeldrain on this computer" class="button_highlight"/>
</form> </form>
<div class="limit_width"> <section>
<br/> <br/>
<h2>Why do I need to confirm my logout?</h2> <h2>Why do I need to confirm my logout?</h2>
<p> <p>
@@ -31,7 +30,7 @@
page visit we can confirm that you really want to log out. page visit we can confirm that you really want to log out.
</p> </p>
<br/> <br/>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
{{template "analytics"}} {{template "analytics"}}
</body> </body>

View File

@@ -8,10 +8,10 @@
<body> <body>
{{template "page_top" .}} {{template "page_top" .}}
<div class="checkers inset"> <header">
<h1>Widget showcase</h1> <h1>Widget showcase</h1>
</div> </header>
<div class="limit_width"> <section>
<h2>Size 2 header</h2> <h2>Size 2 header</h2>
<h3>Size 3 header</h3> <h3>Size 3 header</h3>
<h4>Size 4 header</h4> <h4>Size 4 header</h4>
@@ -64,7 +64,7 @@
<br/><br/> <br/><br/>
<iframe src="https://pixeldrain.com/u/Nygt1on4?embed" style="border: none; width: 800px; max-width: 100%; height: 600px; max-height: 100%; border-radius: 16px;"></iframe> <iframe src="https://pixeldrain.com/u/Nygt1on4?embed" style="border: none; width: 800px; max-width: 100%; height: 600px; max-height: 100%; border-radius: 16px;"></iframe>
</div> </section>
{{template "page_bottom" .}} {{template "page_bottom" .}}
</body> </body>
</html> </html>

View File

@@ -82,85 +82,83 @@ const delete_reporter = async (email) => {
onMount(get_reporters); onMount(get_reporters);
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner /> </div>
{/if}
<section>
<div class="toolbar" style="text-align: left;">
<div class="toolbar_spacer"></div>
<button class:button_highlight={creating} on:click={() => {creating = !creating}}>
<i class="icon">create</i> Add abuse reporter
</button>
</div>
{#if creating}
<div class="highlight_light">
<form on:submit|preventDefault={create_reporter}>
<table class="form">
<tr>
<td>E-mail address</td>
<td><input type="text" bind:this={new_reporter_email}/></td>
</tr>
<tr>
<td>Name</td>
<td><input type="text" bind:this={new_reporter_name} value="Anonymous tip"/></td>
</tr>
<tr>
<td>Type</td>
<td>
<input id="reporter_type_individual" name="reporter_type" type="radio" bind:group={new_reporter_type} value="individual" />
<label for="reporter_type_individual">Individual</label>
<br/>
<input id="reporter_type_org" name="reporter_type" type="radio" bind:group={new_reporter_type} value="org" />
<label for="reporter_type_org">Organisation</label>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button_highlight" type="submit" style="float: right;">
<i class="icon">save</i> Save
</button>
</td>
</tr>
</table>
</form>
</div> </div>
{/if} {/if}
</section>
<div class="limit_width"> <br/>
<div class="toolbar" style="text-align: left;">
<div class="toolbar_spacer"></div>
<button class:button_highlight={creating} on:click={() => {creating = !creating}}>
<i class="icon">create</i> Add abuse reporter
</button>
</div>
{#if creating}
<div class="highlight_light">
<form on:submit|preventDefault={create_reporter}>
<table class="form">
<tr>
<td>E-mail address</td>
<td><input type="text" bind:this={new_reporter_email}/></td>
</tr>
<tr>
<td>Name</td>
<td><input type="text" bind:this={new_reporter_name} value="Anonymous tip"/></td>
</tr>
<tr>
<td>Type</td>
<td>
<input id="reporter_type_individual" name="reporter_type" type="radio" bind:group={new_reporter_type} value="individual" />
<label for="reporter_type_individual">Individual</label>
<br/>
<input id="reporter_type_org" name="reporter_type" type="radio" bind:group={new_reporter_type} value="org" />
<label for="reporter_type_org">Organisation</label>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button_highlight" type="submit" style="float: right;">
<i class="icon">save</i> Save
</button>
</td>
</tr>
</table>
</form>
</div>
{/if}
</div>
<br/> <div class="table_scroll">
<table style="text-align: left;">
<div class="table_scroll"> <tr>
<table style="text-align: left;"> <td>E-mail</td>
<td>Name</td>
<td>Blocked</td>
<td>Type</td>
<td>Last used</td>
<td>Created</td>
<td></td>
</tr>
{#each reporters as reporter (reporter.email)}
<tr> <tr>
<td>E-mail</td> <td>{reporter.email}</td>
<td>Name</td> <td>{reporter.name}</td>
<td>Blocked</td> <td>{reporter.files_blocked}</td>
<td>Type</td> <td>{reporter.type}</td>
<td>Last used</td> <td>{formatDate(reporter.last_used, true, true, false)}</td>
<td>Created</td> <td>{formatDate(reporter.created, false, false, false)}</td>
<td></td> <td>
<button on:click|preventDefault={() => {delete_reporter(reporter.email)}} class="button button_red round">
<i class="icon">delete</i>
</button>
</td>
</tr> </tr>
{#each reporters as reporter (reporter.email)} {/each}
<tr> </table>
<td>{reporter.email}</td>
<td>{reporter.name}</td>
<td>{reporter.files_blocked}</td>
<td>{reporter.type}</td>
<td>{formatDate(reporter.last_used, true, true, false)}</td>
<td>{formatDate(reporter.created, false, false, false)}</td>
<td>
<button on:click|preventDefault={() => {delete_reporter(reporter.email)}} class="button button_red round">
<i class="icon">delete</i>
</button>
</td>
</tr>
{/each}
</table>
</div>
</div> </div>
<style> <style>

View File

@@ -91,38 +91,36 @@ onMount(() => {
}); });
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner />
</div>
{/if}
<div class="limit_width">
<div class="toolbar" style="text-align: left;">
<div class="toolbar_spacer"></div>
<div>Start:</div>
<input type="date" bind:this={startPicker}/>
<div>End:</div>
<input type="date" bind:this={endPicker}/>
<button on:click={get_reports}>Go</button>
</div>
<h2>Pending</h2>
{#each reports_pending as report (report.id)}
{#if report.status === "pending"}
<AbuseReport report={report} on:refresh={get_reports}/>
{/if}
{/each}
<h2>Resolved</h2>
{#each reports_processed as report (report.id)}
{#if report.status !== "pending"}
<AbuseReport report={report} on:refresh={get_reports}/>
{/if}
{/each}
</div> </div>
</div> {/if}
<section>
<div class="toolbar" style="text-align: left;">
<div class="toolbar_spacer"></div>
<div>Start:</div>
<input type="date" bind:this={startPicker}/>
<div>End:</div>
<input type="date" bind:this={endPicker}/>
<button on:click={get_reports}>Go</button>
</div>
<h2>Pending</h2>
{#each reports_pending as report (report.id)}
{#if report.status === "pending"}
<AbuseReport report={report} on:refresh={get_reports}/>
{/if}
{/each}
<h2>Resolved</h2>
{#each reports_processed as report (report.id)}
{#if report.status !== "pending"}
<AbuseReport report={report} on:refresh={get_reports}/>
{/if}
{/each}
</section>
<style> <style>
.spinner_container { .spinner_container {

View File

@@ -65,17 +65,12 @@ onMount(() => {
}) })
</script> </script>
<div> <section>
<div class="limit_width"> <h2>File removal</h2>
<h2>File removal</h2> <p>
<p> Paste any pixeldrain file links in here to remove them
Paste any pixeldrain file links in here to remove them </p>
</p> <div class="highlight_dark">
<div class="highlight_dark"> <Form config={block_form}></Form>
<Form config={block_form}></Form>
</div>
</div> </div>
</div> </section>
<style>
</style>

View File

@@ -159,168 +159,160 @@ onDestroy(() => {
}) })
</script> </script>
<div> <section>
<div class="limit_width"> <h3>Bandwidth usage and file views</h3>
<h3>Bandwidth usage and file views</h3> </section>
</div> <div class="highlight_dark" style="margin-bottom: 6px;">
<div class="highlight_dark" style="margin-bottom: 6px;"> <button on:click={() => { loadGraph(1440, 1, true) }}>Day</button>
<button on:click={() => { loadGraph(1440, 1, true) }}>Day</button> <button on:click={() => { loadGraph(10080, 10, false) }}>Week</button>
<button on:click={() => { loadGraph(10080, 10, false) }}>Week</button> <button on:click={() => { loadGraph(20160, 60, false) }}>Two Weeks</button>
<button on:click={() => { loadGraph(20160, 60, false) }}>Two Weeks</button> <button on:click={() => { loadGraph(43200, 60, false) }}>Month</button>
<button on:click={() => { loadGraph(43200, 60, false) }}>Month</button> <button on:click={() => { loadGraph(131400, 1440, false) }}>Quarter</button>
<button on:click={() => { loadGraph(131400, 1440, false) }}>Quarter</button> <button on:click={() => { loadGraph(262800, 1440, false) }}>Half-year</button>
<button on:click={() => { loadGraph(262800, 1440, false) }}>Half-year</button> <button on:click={() => { loadGraph(525600, 1440, false) }}>Year</button>
<button on:click={() => { loadGraph(525600, 1440, false) }}>Year</button> <button on:click={() => { loadGraph(1051200, 1440, false) }}>Two Years</button>
<button on:click={() => { loadGraph(1051200, 1440, false) }}>Two Years</button> </div>
</div> <Chart bind:this={graphBandwidth} data_type="bytes" legend={false} />
<Chart bind:this={graphBandwidth} data_type="bytes" legend={false} /> <Chart bind:this={graphViews} data_type="number" legend={false} />
<Chart bind:this={graphViews} data_type="number" legend={false} /> <div class="highlight_dark">
<div class="highlight_dark"> Total usage from {start_time} to {end_time}<br/>
Total usage from {start_time} to {end_time}<br/> {formatDataVolume(total_bandwidth, 3)} bandwidth,
{formatDataVolume(total_bandwidth, 3)} bandwidth, {formatDataVolume(total_bandwidth_paid, 3)} paid bandwidth,
{formatDataVolume(total_bandwidth_paid, 3)} paid bandwidth, {formatThousands(total_views, 3)} views and
{formatThousands(total_views, 3)} views and {formatThousands(total_downloads, 3)} downloads
{formatThousands(total_downloads, 3)} downloads
</div>
<br/>
<a class="button" href="/api/admin/call_stack">Call stack</a>
<a class="button" href="/api/admin/heap_profile">Heap profile</a>
<a class="button" href="/api/admin/cpu_profile">CPU profile (wait 1 min)</a>
<br/>
<div class="limit_width">
<table>
<tr>
<td>DB Time</td>
<td>{formatDate(new Date(status.db_time), true, true, true)}</td>
<td>DB Latency</td>
<td>{formatNumber(status.db_latency / 1000, 3)} ms</td>
</tr>
</table>
<h3>Pixelstore peers</h3>
<div class="table_scroll">
<table>
<thead>
<tr>
<td>Address</td>
<td>Pos</td>
<td>Alive</td>
<td>Err</td>
<td>1m</td>
<td>5m</td>
<td>15m</td>
<td>Ping</td>
<td>Free</td>
<td>Min free</td>
</tr>
</thead>
<tbody>
{#each status.peers as peer}
<tr class="peer_row"
class:highlight_red={peer.free_space < peer.min_free_space / 2 || !peer.reachable}
class:highlight_yellow={peer.free_space < peer.min_free_space}
class:highlight_green={peer.reachable}
>
<td>{peer.address}</td>
<td>{peer.position}</td>
<td>{peer.reachable}</td>
<td>{peer.unreachable_count}</td>
<td>{peer.load_1_min.toFixed(1)}</td>
<td>{peer.load_5_min.toFixed(1)}</td>
<td>{peer.load_15_min.toFixed(1)}</td>
<td>{formatDuration(peer.latency, 3)}</td>
<td>{formatDataVolume(peer.free_space, 4)}</td>
<td>{formatDataVolume(peer.min_free_space, 3)}</td>
</tr>
{/each}
</tbody>
</table>
</div>
<h3>Pixelstore stats</h3>
<table>
<thead>
<tr>
<td>Local reads</td>
<td>Local read size</td>
<td>Remote reads</td>
<td>Remote read size</td>
</tr>
</thead>
<tbody>
<tr>
<td>{status.local_reads}</td>
<td>{formatDataVolume(status.local_read_size, 4)}</td>
<td>{status.remote_reads}</td>
<td>{formatDataVolume(status.remote_read_size, 4)}</td>
</tr>
<tr>
<td>{status.local_reads_per_sec.toPrecision(4)} / s</td>
<td>{formatDataVolume(status.local_read_size_per_sec, 4)} / s</td>
<td>{status.remote_reads_per_sec.toPrecision(4)} / s</td>
<td>{formatDataVolume(status.remote_read_size_per_sec, 4)} /s</td>
</tr>
</tbody>
</table>
<h3>Socket statistics</h3>
<table>
<thead>
<tr>
<td>Watcher</td>
<td>Threads</td>
<td>Listeners</td>
<td>Avg</td>
</tr>
</thead>
<tbody>
<tr>
<td>File statistics</td>
<td>{status.stats_watcher_threads}</td>
<td>{status.stats_watcher_listeners}</td>
<td>{(status.stats_watcher_listeners / status.stats_watcher_threads).toPrecision(3)}</td>
</tr>
<tr>
<td>Downloads</td>
<td>{status.download_clients}</td>
<td>{status.download_connections}</td>
<td>{(status.download_connections / status.download_clients).toPrecision(3)}</td>
</tr>
</tbody>
</table>
<h3>Query statistics</h3>
<div class="table_scroll" style="text-align: left;">
<table>
<thead>
<tr>
<td style="cursor: pointer;" on:click={() => { getStats('query_name') }}>Query</td>
<td style="cursor: pointer;" on:click={() => { getStats('calls') }}>Calls</td>
<td style="cursor: pointer;" on:click={() => { getStats('average_duration') }}>Avg dur</td>
<td style="cursor: pointer;" on:click={() => { getStats('total_duration') }}>Total dur</td>
<td>Callers</td>
</tr>
</thead>
<tbody id="tstat_body">
{#each status.query_statistics as q}
<tr>
<td>{q.query_name}</td>
<td>{q.calls}</td>
<td>{q.average_duration}ms</td>
<td>{formatDuration(q.total_duration, 0)}</td>
<td>
{#each q.callers as caller}
{caller.count}x {caller.name}<br/>
{/each}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
</div> </div>
<style> <br/>
.peer_row { <a class="button" href="/api/admin/call_stack">Call stack</a>
text-align: left; <a class="button" href="/api/admin/heap_profile">Heap profile</a>
} <a class="button" href="/api/admin/cpu_profile">CPU profile (wait 1 min)</a>
</style> <br/>
<section>
<table>
<tr>
<td>DB Time</td>
<td>{formatDate(new Date(status.db_time), true, true, true)}</td>
<td>DB Latency</td>
<td>{formatNumber(status.db_latency / 1000, 3)} ms</td>
</tr>
</table>
<h3>Pixelstore peers</h3>
<div class="table_scroll">
<table>
<thead>
<tr>
<td>Address</td>
<td>Pos</td>
<td>Alive</td>
<td>Err</td>
<td>1m</td>
<td>5m</td>
<td>15m</td>
<td>Ping</td>
<td>Free</td>
<td>Min free</td>
</tr>
</thead>
<tbody>
{#each status.peers as peer}
<tr style="text-align: left"
class:highlight_red={peer.free_space < peer.min_free_space / 2 || !peer.reachable}
class:highlight_yellow={peer.free_space < peer.min_free_space}
class:highlight_green={peer.reachable}
>
<td>{peer.address}</td>
<td>{peer.position}</td>
<td>{peer.reachable}</td>
<td>{peer.unreachable_count}</td>
<td>{peer.load_1_min.toFixed(1)}</td>
<td>{peer.load_5_min.toFixed(1)}</td>
<td>{peer.load_15_min.toFixed(1)}</td>
<td>{formatDuration(peer.latency, 3)}</td>
<td>{formatDataVolume(peer.free_space, 4)}</td>
<td>{formatDataVolume(peer.min_free_space, 3)}</td>
</tr>
{/each}
</tbody>
</table>
</div>
<h3>Pixelstore stats</h3>
<table>
<thead>
<tr>
<td>Local reads</td>
<td>Local read size</td>
<td>Remote reads</td>
<td>Remote read size</td>
</tr>
</thead>
<tbody>
<tr>
<td>{status.local_reads}</td>
<td>{formatDataVolume(status.local_read_size, 4)}</td>
<td>{status.remote_reads}</td>
<td>{formatDataVolume(status.remote_read_size, 4)}</td>
</tr>
<tr>
<td>{status.local_reads_per_sec.toPrecision(4)} / s</td>
<td>{formatDataVolume(status.local_read_size_per_sec, 4)} / s</td>
<td>{status.remote_reads_per_sec.toPrecision(4)} / s</td>
<td>{formatDataVolume(status.remote_read_size_per_sec, 4)} /s</td>
</tr>
</tbody>
</table>
<h3>Socket statistics</h3>
<table>
<thead>
<tr>
<td>Watcher</td>
<td>Threads</td>
<td>Listeners</td>
<td>Avg</td>
</tr>
</thead>
<tbody>
<tr>
<td>File statistics</td>
<td>{status.stats_watcher_threads}</td>
<td>{status.stats_watcher_listeners}</td>
<td>{(status.stats_watcher_listeners / status.stats_watcher_threads).toPrecision(3)}</td>
</tr>
<tr>
<td>Downloads</td>
<td>{status.download_clients}</td>
<td>{status.download_connections}</td>
<td>{(status.download_connections / status.download_clients).toPrecision(3)}</td>
</tr>
</tbody>
</table>
<h3>Query statistics</h3>
<div class="table_scroll" style="text-align: left;">
<table>
<thead>
<tr>
<td style="cursor: pointer;" on:click={() => { getStats('query_name') }}>Query</td>
<td style="cursor: pointer;" on:click={() => { getStats('calls') }}>Calls</td>
<td style="cursor: pointer;" on:click={() => { getStats('average_duration') }}>Avg dur</td>
<td style="cursor: pointer;" on:click={() => { getStats('total_duration') }}>Total dur</td>
<td>Callers</td>
</tr>
</thead>
<tbody id="tstat_body">
{#each status.query_statistics as q}
<tr>
<td>{q.query_name}</td>
<td>{q.calls}</td>
<td>{q.average_duration}ms</td>
<td>{formatDuration(q.total_duration, 0)}</td>
<td>
{#each q.callers as caller}
{caller.count}x {caller.name}<br/>
{/each}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</section>

View File

@@ -80,91 +80,89 @@ const delete_ban = async (addr) => {
onMount(get_bans); onMount(get_bans);
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner /> </div>
{/if}
<section>
<div class="toolbar" style="text-align: left;">
<div class="toolbar_spacer"></div>
<button class:button_highlight={creating} on:click={() => {creating = !creating}}>
<i class="icon">create</i> Add IP ban
</button>
</div>
{#if creating}
<div class="highlight_light">
<form on:submit|preventDefault={create_ban}>
<table class="form">
<tr>
<td>IP address</td>
<td><input type="text" bind:this={new_ban_address}/></td>
</tr>
<tr>
<td>Reason</td>
<td>
<input id="reason_unknown" name="reporter_type" type="radio" bind:group={new_ban_reason} value="unknown" />
<label for="reason_unknown">unknown</label>
<br/>
<input id="reason_copyright" name="reporter_type" type="radio" bind:group={new_ban_reason} value="copyright" />
<label for="reason_copyright">copyright</label>
<br/>
<input id="reason_child_abuse" name="reporter_type" type="radio" bind:group={new_ban_reason} value="child_abuse" />
<label for="reason_child_abuse">child_abuse</label>
<br/>
<input id="reason_terrorism" name="reporter_type" type="radio" bind:group={new_ban_reason} value="terorrism" />
<label for="reason_terrorism">terrorism</label>
<br/>
<input id="reason_gore" name="reporter_type" type="radio" bind:group={new_ban_reason} value="gore" />
<label for="reason_gore">gore</label>
<br/>
<input id="reason_malware" name="reporter_type" type="radio" bind:group={new_ban_reason} value="malware" />
<label for="reason_malware">malware</label>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button_highlight" type="submit" style="float: right;">
<i class="icon">save</i> Save
</button>
</td>
</tr>
</table>
</form>
</div> </div>
{/if} {/if}
</section>
<div class="limit_width"> <br/>
<div class="toolbar" style="text-align: left;">
<div class="toolbar_spacer"></div>
<button class:button_highlight={creating} on:click={() => {creating = !creating}}>
<i class="icon">create</i> Add IP ban
</button>
</div>
{#if creating}
<div class="highlight_light">
<form on:submit|preventDefault={create_ban}>
<table class="form">
<tr>
<td>IP address</td>
<td><input type="text" bind:this={new_ban_address}/></td>
</tr>
<tr>
<td>Reason</td>
<td>
<input id="reason_unknown" name="reporter_type" type="radio" bind:group={new_ban_reason} value="unknown" />
<label for="reason_unknown">unknown</label>
<br/>
<input id="reason_copyright" name="reporter_type" type="radio" bind:group={new_ban_reason} value="copyright" />
<label for="reason_copyright">copyright</label>
<br/>
<input id="reason_child_abuse" name="reporter_type" type="radio" bind:group={new_ban_reason} value="child_abuse" />
<label for="reason_child_abuse">child_abuse</label>
<br/>
<input id="reason_terrorism" name="reporter_type" type="radio" bind:group={new_ban_reason} value="terorrism" />
<label for="reason_terrorism">terrorism</label>
<br/>
<input id="reason_gore" name="reporter_type" type="radio" bind:group={new_ban_reason} value="gore" />
<label for="reason_gore">gore</label>
<br/>
<input id="reason_malware" name="reporter_type" type="radio" bind:group={new_ban_reason} value="malware" />
<label for="reason_malware">malware</label>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button_highlight" type="submit" style="float: right;">
<i class="icon">save</i> Save
</button>
</td>
</tr>
</table>
</form>
</div>
{/if}
</div>
<br/> <div class="table_scroll">
<table style="text-align: left;">
<div class="table_scroll"> <tr>
<table style="text-align: left;"> <td>Address</td>
<td>Reason</td>
<td>Ban time</td>
<td>Expire time</td>
<td>Offences</td>
<td></td>
</tr>
{#each rows as row (row.address)}
<tr> <tr>
<td>Address</td> <td>{row.address}</td>
<td>Reason</td> <td>{row.reason}</td>
<td>Ban time</td> <td>{formatDate(row.ban_time, true, true, false)}</td>
<td>Expire time</td> <td>{formatDate(row.expire_time, true, true, false)}</td>
<td>Offences</td> <td>{row.offences}</td>
<td></td> <td>
<button on:click|preventDefault={() => {delete_ban(row.address)}} class="button button_red round">
<i class="icon">delete</i>
</button>
</td>
</tr> </tr>
{#each rows as row (row.address)} {/each}
<tr> </table>
<td>{row.address}</td>
<td>{row.reason}</td>
<td>{formatDate(row.ban_time, true, true, false)}</td>
<td>{formatDate(row.expire_time, true, true, false)}</td>
<td>{row.offences}</td>
<td>
<button on:click|preventDefault={() => {delete_ban(row.address)}} class="button button_red round">
<i class="icon">delete</i>
</button>
</td>
</tr>
{/each}
</table>
</div>
</div> </div>
<style> <style>

View File

@@ -26,7 +26,7 @@ onMount(() => {
}) })
</script> </script>
<div class="checkers inset"> <header>
<h1>Admin Panel</h1> <h1>Admin Panel</h1>
<div class="tab_bar"> <div class="tab_bar">
@@ -77,7 +77,7 @@ onMount(() => {
Update global settings Update global settings
</a> </a>
</div> </div>
</div> </header>
{#if page === "status"} {#if page === "status"}
<Home></Home> <Home></Home>

View File

@@ -173,7 +173,7 @@ onMount(get_coupons)
</script> </script>
<div class="limit_width"> <section>
{#if loading} {#if loading}
<div class="spinner_container"> <div class="spinner_container">
<Spinner /> <Spinner />
@@ -221,7 +221,7 @@ onMount(get_coupons)
{/each} {/each}
</table> </table>
</div> </div>
</div> </section>
<style> <style>
.spinner_container { .spinner_container {

View File

@@ -386,18 +386,18 @@ const keydown = (e) => {
<div> <div>
<!-- If the user is logged in and has used more than 50% of their storage space we will show a progress bar --> <!-- If the user is logged in and has used more than 50% of their storage space we will show a progress bar -->
{#if window.user.username !== "" && window.user.storage_space_used/window.user.subscription.storage_space > 0.5} {#if window.user.username !== "" && window.user.storage_space_used/window.user.subscription.storage_space > 0.5}
<div class="limit_width"> <section>
<StorageProgressBar used={window.user.storage_space_used} total={window.user.subscription.storage_space}></StorageProgressBar> <StorageProgressBar used={window.user.storage_space_used} total={window.user.subscription.storage_space}></StorageProgressBar>
</div> </section>
{/if} {/if}
<div class="instruction limit_width" style="margin-top: 0; border-top: none;"> <section class="instruction" style="margin-top: 0; border-top: none;">
<span class="big_number">1</span> <span class="big_number">1</span>
<span class="instruction_text">Select files to upload</span> <span class="instruction_text">Select files to upload</span>
<br/> <br/>
You can also drop files on this page from your file manager or You can also drop files on this page from your file manager or
paste an image from your clipboard paste an image from your clipboard
</div> </section>
<br/> <br/>
<input bind:this={file_input_field} on:change={file_input_change} type="file" name="file" multiple="multiple"/> <input bind:this={file_input_field} on:change={file_input_change} type="file" name="file" multiple="multiple"/>
@@ -416,7 +416,7 @@ const keydown = (e) => {
<a href="/about#content-policy">content policy</a>. <a href="/about#content-policy">content policy</a>.
<p> <p>
<div class="instruction limit_width" style="margin-bottom: 0;"> <section class="instruction" style="margin-bottom: 0;">
<span class="big_number">2</span> <span class="big_number">2</span>
<span class="instruction_text">Wait for the files to finish uploading</span> <span class="instruction_text">Wait for the files to finish uploading</span>
<br/> <br/>
@@ -426,7 +426,7 @@ const keydown = (e) => {
<div>ETA {formatDuration(remaining_time, 0)}</div> <div>ETA {formatDuration(remaining_time, 0)}</div>
<div>Rate {formatDataVolume(total_rate, 3)}/s</div> <div>Rate {formatDataVolume(total_rate, 3)}/s</div>
</div> </div>
</div> </section>
<div class="progress_bar_outer" style="margin-bottom: 1.5em;"> <div class="progress_bar_outer" style="margin-bottom: 1.5em;">
<div bind:this={progress_bar_inner} class="progress_bar_inner"></div> <div bind:this={progress_bar_inner} class="progress_bar_inner"></div>
</div> </div>
@@ -441,10 +441,10 @@ const keydown = (e) => {
<UploadProgressBar bind:this={file.component} job={file}></UploadProgressBar> <UploadProgressBar bind:this={file.component} job={file}></UploadProgressBar>
{/each} {/each}
<div class="instruction limit_width"> <section class="instruction">
<span class="big_number">3</span> <span class="big_number">3</span>
<span class="instruction_text">Share the files</span> <span class="instruction_text">Share the files</span>
</div> </section>
<br/> <br/>
{#if upload_queue.length > 1} {#if upload_queue.length > 1}
@@ -518,11 +518,11 @@ const keydown = (e) => {
<br/> <br/>
{#if window.user.subscription.name === ""} {#if window.user.subscription.name === ""}
<div class="instruction limit_width"> <section>
<span class="big_number">4</span> <div class="instruction">
<span class="instruction_text">Support me on Patreon!</span> <span class="big_number">4</span>
</div> <span class="instruction_text">Support me on Patreon!</span>
<div class="limit_width"> </div>
<p> <p>
Pixeldrain is struggling to get by financially. Because anyone Pixeldrain is struggling to get by financially. Because anyone
can upload anything it's hard to find reputable advertisers who can upload anything it's hard to find reputable advertisers who
@@ -537,13 +537,15 @@ const keydown = (e) => {
help with making pixeldrain the easiest and fastest way to share help with making pixeldrain the easiest and fastest way to share
files online! files online!
</p> </p>
</div> <br/>
<br/> <div style="text-align: center;">
<a href="#pro" class="button big_button" style="min-width: 350px;"> <a href="#pro" class="button big_button" style="min-width: 350px;">
<i class="icon">arrow_downward</i> <i class="icon">arrow_downward</i>
Check out Pro Check out Pro
<i class="icon">arrow_downward</i> <i class="icon">arrow_downward</i>
</a> </a>
</div>
</section>
{/if} {/if}
</div> </div>

View File

@@ -1,13 +1,10 @@
<script> <script>
import { fs_delete_bucket } from "../filesystem/FilesystemAPI.svelte"; import { fs_delete_bucket } from "../filesystem/FilesystemAPI.svelte";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import Expandable from "../util/Expandable.svelte";
let dispatch = createEventDispatcher() let dispatch = createEventDispatcher()
export let bucket export let bucket
let details_hidden = true
const expand_bucket = () => {
details_hidden = !details_hidden
}
const save_bucket = () => { const save_bucket = () => {
alert("save") alert("save")
@@ -32,21 +29,14 @@ const delete_bucket = async () => {
</script> </script>
<div class="bucket"> <Expandable>
<div class="bucket_header"> <div slot="header">
<a href={'/d/' + bucket.id} class="bucket_title"> <a href={'/d/' + bucket.id} class="bucket_title">
<img class="bucket_icon" src="/res/img/mime/folder-remote.png" alt="Bucket icon"/> <img class="bucket_icon" src="/res/img/mime/folder-remote.png" alt="Bucket icon"/>
{bucket.name} {bucket.name}
</a> </a>
<button class="bucket_expand" on:click={expand_bucket}>
{#if details_hidden}
<i class="icon">expand_more</i>
{:else}
<i class="icon">expand_less</i>
{/if}
</button>
</div> </div>
<div class="bucket_details" class:hidden={details_hidden}> <div>
<form on:submit|preventDefault={save_bucket}> <form on:submit|preventDefault={save_bucket}>
<table class="form"> <table class="form">
<tr class="form"> <tr class="form">
@@ -66,26 +56,9 @@ const delete_bucket = async () => {
</table> </table>
</form> </form>
</div> </div>
</div> </Expandable>
<style> <style>
.bucket {
text-decoration: none;
background-color: var(--layer_3_color);
transition: box-shadow 0.5s;
box-shadow: 1px 1px 5px 0 var(--shadow_color);
margin: 1em 0;
border-radius: 8px;
overflow: hidden;
}
.bucket_header {
display: flex;
flex-direction: row;
color: var(--text_color);
}
.bucket_header:hover {
background-color: var(--input_color_dark)
}
.bucket_title { .bucket_title {
flex: 1 1 auto; flex: 1 1 auto;
align-self: center; align-self: center;
@@ -98,18 +71,4 @@ const delete_bucket = async () => {
margin: 4px; margin: 4px;
vertical-align: middle; vertical-align: middle;
} }
.bucket_expand {
flex: 0 0 auto;
}
.bucket_details {
display: flex;
padding: 0.4em;
flex-direction: column;
text-decoration: none;
border-top: 1px solid var(--layer_3_color_border);
color: var(--text_color);
}
.hidden {
display: none;
}
</style> </style>

View File

@@ -41,51 +41,49 @@ const create_bucket = async () => {
onMount(get_buckets); onMount(get_buckets);
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner /> </div>
{/if}
<section>
<div class="toolbar" style="text-align: right;">
<button
class:button_highlight={creating_bucket}
on:click={() => {creating_bucket = !creating_bucket}}
>
<i class="icon">create_new_folder</i> New bucket
</button>
</div>
{#if creating_bucket}
<div class="highlight_light">
<form on:submit|preventDefault={create_bucket}>
<table class="form">
<tr>
<td>
Name
</td>
<td>
<input type="text" bind:this={new_bucket_name}/>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button_highlight" type="submit" style="float: right;">
<i class="icon">save</i> Save
</button>
</td>
</tr>
</table>
</form>
</div> </div>
{/if} {/if}
<div class="limit_width"> {#each buckets as bucket (bucket.id)}
<div class="toolbar" style="text-align: right;"> <UserBucket bucket={bucket} on:refresh={get_buckets}></UserBucket>
<button {/each}
class:button_highlight={creating_bucket} </section>
on:click={() => {creating_bucket = !creating_bucket}}
>
<i class="icon">create_new_folder</i> New bucket
</button>
</div>
{#if creating_bucket}
<div class="highlight_light">
<form on:submit|preventDefault={create_bucket}>
<table class="form">
<tr>
<td>
Name
</td>
<td>
<input type="text" bind:this={new_bucket_name}/>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button_highlight" type="submit" style="float: right;">
<i class="icon">save</i> Save
</button>
</td>
</tr>
</table>
</form>
</div>
{/if}
{#each buckets as bucket}
<UserBucket bucket={bucket} on:refresh={get_buckets}></UserBucket>
{/each}
</div>
</div>
<style> <style>
.spinner_container { .spinner_container {

View File

@@ -82,87 +82,85 @@ const logout = async (key) => {
} }
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner /> </div>
{/if}
<section>
{#if !loaded}
<div class="highlight_yellow">
<h2>Warning</h2>
<p>
API keys are sensitive information. They can be used to gain
full control over your account. Do not show your API keys to
someone or something you don't trust!
</p>
<button class="button_red" on:click={load_keys}>
<i class="icon">lock_open</i> Show API keys
</button>
</div>
{:else}
<div class="toolbar" style="text-align: left;">
<div class="toolbar_spacer"></div>
<button on:click={create_key}>
<i class="icon">add</i> Create new API key
</button>
</div> </div>
{/if} {/if}
<div class="limit_width"> <p>
{#if !loaded} If you delete the API key that you are currently using you will be
<div class="highlight_yellow"> logged out of your account. API keys expire 90 days after the last
<h2>Warning</h2> time they're used. If you think someone is using your account
<p> without your authorization it's probably a good idea to delete all
API keys are sensitive information. They can be used to gain your keys.
full control over your account. Do not show your API keys to </p>
someone or something you don't trust! </section>
</p> <div class="table_scroll">
<button class="button_red" on:click={load_keys}> <table style="text-align: left;">
<i class="icon">lock_open</i> Show API keys <tr>
</button> <td>Key</td>
</div> <td>Created</td>
{:else} <td>Last used ▼</td>
<div class="toolbar" style="text-align: left;"> <td>IP address</td>
<div class="toolbar_spacer"></div> <td></td>
<button on:click={create_key}> </tr>
<i class="icon">add</i> Create new API key {#each rows as row (row.auth_key)}
</button> <tr style="border-bottom: none;">
</div> <td>{row.auth_key}</td>
{/if} <td>{formatDate(row.creation_time, true, true, false)}</td>
<td>{formatDate(row.last_used_time, true, true, false)}</td>
<p> <td>{row.creation_ip_address}</td>
If you delete the API key that you are currently using you will be <td>
logged out of your account. API keys expire 90 days after the last <button on:click|preventDefault={() => {logout(row.auth_key)}} class="button button_red round">
time they're used. If you think someone is using your account <i class="icon">delete</i>
without your authorization it's probably a good idea to delete all </button>
your keys. </td>
</p>
</div>
<div class="table_scroll">
<table style="text-align: left;">
<tr>
<td>Key</td>
<td>Created</td>
<td>Last used ▼</td>
<td>IP address</td>
<td></td>
</tr> </tr>
{#each rows as row (row.auth_key)} <tr>
<tr style="border-bottom: none;"> <td colspan="1">
<td>{row.auth_key}</td> {#if row.app_name === "website login"}
<td>{formatDate(row.creation_time, true, true, false)}</td> <img src="/res/img/pixeldrain_32.png" alt="Pixeldrain logo" class="app_icon"/>
<td>{formatDate(row.last_used_time, true, true, false)}</td> Pixeldrain website
<td>{row.creation_ip_address}</td> {:else if row.app_name === "website keys page"}
<td> <i class="icon">vpn_key</i>
<button on:click|preventDefault={() => {logout(row.auth_key)}} class="button button_red round"> Pixeldrain keys page
<i class="icon">delete</i> {:else if row.app_name === "sharex"}
</button> <img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon"/>
</td> ShareX
</tr> {:else if row.app_name === "jdownloader"}
<tr> <img src="/res/img/jdownloader.png" alt="JDownloader logo" class="app_icon"/>
<td colspan="1"> JDownloader
{#if row.app_name === "website login"} {:else}
<img src="/res/img/pixeldrain_32.png" alt="Pixeldrain logo" class="app_icon"/> Unknown app: {row.app_name}
Pixeldrain website {/if}
{:else if row.app_name === "website keys page"} </td>
<i class="icon">vpn_key</i> <td colspan="4">User-Agent: {row.user_agent}</td>
Pixeldrain keys page </tr>
{:else if row.app_name === "sharex"} {/each}
<img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon"/> </table>
ShareX
{:else if row.app_name === "jdownloader"}
<img src="/res/img/jdownloader.png" alt="JDownloader logo" class="app_icon"/>
JDownloader
{:else}
Unknown app: {row.app_name}
{/if}
</td>
<td colspan="4">User-Agent: {row.user_agent}</td>
</tr>
{/each}
</table>
</div>
</div> </div>
<style> <style>

View File

@@ -137,29 +137,24 @@ let delete_account = {
} }
</script> </script>
<div> <section>
<div class="limit_width"> <h2>Change password</h2>
<h2>Change password</h2> <div class="highlight_dark">
<div class="highlight_dark"> <Form config={password_change}></Form>
<Form config={password_change}></Form>
</div>
<h2>Change e-mail address</h2>
<div class="highlight_dark">
<Form config={email_change}></Form>
</div>
<h2>Change name</h2>
<div class="highlight_dark">
<Form config={name_change}></Form>
</div>
<h2>Delete account</h2>
<div class="highlight_dark">
<Form config={delete_account}></Form>
</div>
</div> </div>
</div>
<style> <h2>Change e-mail address</h2>
</style> <div class="highlight_dark">
<Form config={email_change}></Form>
</div>
<h2>Change name</h2>
<div class="highlight_dark">
<Form config={name_change}></Form>
</div>
<h2>Delete account</h2>
<div class="highlight_dark">
<Form config={delete_account}></Form>
</div>
</section>

View File

@@ -56,114 +56,113 @@ onMount(() => {
}) })
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner />
</div>
{/if}
<div class="limit_width">
{#if app_name === "jdownloader"}
<h2>
Connect
<img src="/res/img/jdownloader.png" alt="JDownloader logo" class="app_icon_small"/>
JDownloader to your pixeldrain account
</h2>
<p>
To connect JDownloader to pixeldrain you need to generate an API
key and enter it in JDownloader's Account Manager.
<br/>
<strong>Do not show the generated key to anyone</strong>, it can
be used to gain access to your pixeldrain account!
</p>
{#if !api_key}
<div class="center">
<button class="button_highlight" on:click={create_key}>
<i class="icon">add</i>
Generate key
</button>
</div>
{:else}
<h3>Key created</h3>
<div class="copy_container">
<button on:click={copy_key} class="copy_button" class:button_highlight={copied}>
<i class="icon">content_copy</i>
{#if copied}
Copied!
{:else}
Copy key to clipboard
{/if}
</button>
<button on:click={toggle_show_key} class="copy_button" class:button_highlight={show_key !== ""}>
<i class="icon">visibility</i>
{#if show_key === ""}
Show key
{/if}
</button>
<input bind:value={show_key} class="copy_textarea" type="text" placeholder="Your key will show up here" disabled={show_key === ""}/>
</div>
{/if}
<p>
Paste the key in JDownloader to authenticate the app.
</p>
{:else if app_name === "sharex"}
<h2>
Connect
<img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon_small"/>
ShareX to your pixeldrain account
</h2>
<p>
ShareX is a Screen capture, file sharing and productivity tool.
Pixeldrain is supported as a custom uploader. You can <a
href="https://getsharex.com/" target="_blank">get ShareX
here</a>.
</p>
<p>
Here you can download our custom ShareX uploader which uses
pixeldrain to upload your files. This uploader is configured to
upload files to your personal pixeldrain account. <strong>Do not
share the configuration file with anyone</strong>, it contains
your account credentials.
</p>
<div class="center">
<a href="/misc/sharex/pixeldrain.com.sxcu" class="button button_highlight">
<i class="icon small">save</i>
Download ShareX Uploader
</a>
</div>
<h3>Setting pixeldrain as default uploader</h3>
<p>
Download the uploader config and choose 'Open file'
<br/>
<img src="/res/img/sharex_download.png" style="max-width: 100%;" alt=""/><br/>
Set pixeldrain.com as active uploader. Choose Yes
<br/>
<img src="/res/img/sharex_default.png" style="max-width: 100%;" alt=""/><br/>
</p>
{:else}
<h2>Connect an app to your pixeldrain account</h2>
<ul>
<li>
<button on:click={() => {app_name = "jdownloader"}}>
<img src="/res/img/jdownloader.png" alt="JDownloader logo" class="app_icon"/>
Connect JDownloader
</button>
</li>
<li>
<button on:click={() => {app_name = "sharex"}}>
<img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon"/>
Connect ShareX
</button>
</li>
</ul>
{/if}
</div> </div>
</div> {/if}
<section>
{#if app_name === "jdownloader"}
<h2>
Connect
<img src="/res/img/jdownloader.png" alt="JDownloader logo" class="app_icon_small"/>
JDownloader to your pixeldrain account
</h2>
<p>
To connect JDownloader to pixeldrain you need to generate an API
key and enter it in JDownloader's Account Manager.
<br/>
<strong>Do not show the generated key to anyone</strong>, it can
be used to gain access to your pixeldrain account!
</p>
{#if !api_key}
<div class="center">
<button class="button_highlight" on:click={create_key}>
<i class="icon">add</i>
Generate key
</button>
</div>
{:else}
<h3>Key created</h3>
<div class="copy_container">
<button on:click={copy_key} class="copy_button" class:button_highlight={copied}>
<i class="icon">content_copy</i>
{#if copied}
Copied!
{:else}
Copy key to clipboard
{/if}
</button>
<button on:click={toggle_show_key} class="copy_button" class:button_highlight={show_key !== ""}>
<i class="icon">visibility</i>
{#if show_key === ""}
Show key
{/if}
</button>
<input bind:value={show_key} class="copy_textarea" type="text" placeholder="Your key will show up here" disabled={show_key === ""}/>
</div>
{/if}
<p>
Paste the key in JDownloader to authenticate the app.
</p>
{:else if app_name === "sharex"}
<h2>
Connect
<img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon_small"/>
ShareX to your pixeldrain account
</h2>
<p>
ShareX is a Screen capture, file sharing and productivity tool.
Pixeldrain is supported as a custom uploader. You can <a
href="https://getsharex.com/" target="_blank">get ShareX
here</a>.
</p>
<p>
Here you can download our custom ShareX uploader which uses
pixeldrain to upload your files. This uploader is configured to
upload files to your personal pixeldrain account. <strong>Do not
share the configuration file with anyone</strong>, it contains
your account credentials.
</p>
<div class="center">
<a href="/misc/sharex/pixeldrain.com.sxcu" class="button button_highlight">
<i class="icon small">save</i>
Download ShareX Uploader
</a>
</div>
<h3>Setting pixeldrain as default uploader</h3>
<p>
Download the uploader config and choose 'Open file'
<br/>
<img src="/res/img/sharex_download.png" style="max-width: 100%;" alt=""/><br/>
Set pixeldrain.com as active uploader. Choose Yes
<br/>
<img src="/res/img/sharex_default.png" style="max-width: 100%;" alt=""/><br/>
</p>
{:else}
<h2>Connect an app to your pixeldrain account</h2>
<ul>
<li>
<button on:click={() => {app_name = "jdownloader"}}>
<img src="/res/img/jdownloader.png" alt="JDownloader logo" class="app_icon"/>
Connect JDownloader
</button>
</li>
<li>
<button on:click={() => {app_name = "sharex"}}>
<img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon"/>
Connect ShareX
</button>
</li>
</ul>
{/if}
</section>
<style> <style>
.spinner_container { .spinner_container {

View File

@@ -174,142 +174,142 @@ onDestroy(() => {
}) })
</script> </script>
<div> <section>
<div class="limit_width"> <h2>Account information</h2>
<h2>Account information</h2> <ul>
<ul> <li>Username: {window.user.username}</li>
<li>Username: {window.user.username}</li> <li>E-mail address: {window.user.email}</li>
<li>E-mail address: {window.user.email}</li> <li>
<li> Supporter level: {window.user.subscription.name}
Supporter level: {window.user.subscription.name} {#if window.user.subscription.type === "patreon"}
{#if window.user.subscription.type === "patreon"} (<a href="https://www.patreon.com/join/pixeldrain/checkout?edit=1">Manage subscription</a>)
(<a href="https://www.patreon.com/join/pixeldrain/checkout?edit=1">Manage subscription</a>)
{/if}
<ul>
<li>
Max file size: {formatDataVolume(window.user.subscription.file_size_limit, 3)}
</li>
{#if window.user.subscription.file_expiry_days > 0}
<li>Files expire after {window.user.subscription.file_expiry_days} days</li>
{:else}
<li>Files never expire</li>
{/if}
</ul>
</li>
{#if window.user.balance_micro_eur !== 0}
<li>
Current account balance: <Euro amount={window.user.balance_micro_eur}></Euro>
</li>
{/if} {/if}
</ul> <ul>
<li>
{#if window.user.subscription.storage_space === -1} Max file size: {formatDataVolume(window.user.subscription.file_size_limit, 3)}
Storage space used: {formatDataVolume(storage_space_used, 3)}<br/> </li>
{:else} {#if window.user.subscription.file_expiry_days > 0}
<StorageProgressBar used={storage_space_used} total={window.user.subscription.storage_space}></StorageProgressBar> <li>Files expire after {window.user.subscription.file_expiry_days} days</li>
{:else}
<li>Files never expire</li>
{/if}
</ul>
</li>
{#if window.user.balance_micro_eur !== 0}
<li>
Current account balance: <Euro amount={window.user.balance_micro_eur}></Euro>
</li>
{/if} {/if}
</ul>
{#if transfer_cap === -1} {#if window.user.subscription.storage_space === -1}
Paid transfers in the last 30 days: {formatDataVolume(transfer_used, 3)}<br/> Storage space used: {formatDataVolume(storage_space_used, 3)}<br/>
{:else} {:else}
Paid transfers: <StorageProgressBar used={storage_space_used} total={window.user.subscription.storage_space}></StorageProgressBar>
{formatDataVolume(transfer_used, 3)} {/if}
out of
{formatDataVolume(transfer_cap, 3)}
(<a href="/user/subscription">Set your transfer limit on the subscription page</a>)
<HotlinkProgressBar used={transfer_used} total={transfer_cap}></HotlinkProgressBar>
{/if}
<h3>Exports</h3> {#if transfer_cap === -1}
<div style="text-align: center;"> Paid transfers in the last 30 days: {formatDataVolume(transfer_used, 3)}<br/>
<a href="/user/export/files" class="button"> {:else}
<i class="icon">list</i> Paid transfers:
Export uploaded files to CSV {formatDataVolume(transfer_used, 3)}
</a> out of
<a href="/user/export/lists" class="button"> {formatDataVolume(transfer_cap, 3)}
<i class="icon">list</i> (<a href="/user/subscription">Set your transfer limit on the subscription page</a>)
Export created lists to CSV <HotlinkProgressBar used={transfer_used} total={transfer_cap}></HotlinkProgressBar>
</a> {/if}
</div>
<h2>Statistics</h2> <h3>Exports</h3>
<p> <div style="text-align: center;">
Here you can see how often your files are viewed, downloaded <a href="/user/export/files" class="button">
and how much bandwidth they consume. The buttons at the top <i class="icon">list</i>
can be pressed to adjust the timeframe. If you choose 'Day' Export uploaded files to CSV
the statistics will be updated periodically. No need to </a>
refresh the page. <a href="/user/export/lists" class="button">
</p> <i class="icon">list</i>
Export created lists to CSV
</a>
</div> </div>
<div class="highlight_light">
<button <h2>Statistics</h2>
on:click={() => { update_graphs(1440, 1, true) }} <p>
class:button_highlight={graph_timespan == 1440}> Here you can see how often your files are viewed, downloaded
Day (1m) and how much bandwidth they consume. The buttons at the top
</button> can be pressed to adjust the timeframe. If you choose 'Day'
<button the statistics will be updated periodically. No need to
on:click={() => { update_graphs(10080, 10, false) }} refresh the page.
class:button_highlight={graph_timespan == 10080}> </p>
Week (10m) </section>
</button>
<button <div class="highlight_light">
on:click={() => { update_graphs(20160, 60, false) }} <button
class:button_highlight={graph_timespan == 20160}> on:click={() => { update_graphs(1440, 1, true) }}
Two Weeks (1h) class:button_highlight={graph_timespan == 1440}>
</button> Day (1m)
<button </button>
on:click={() => { update_graphs(43200, 1440, false) }} <button
class:button_highlight={graph_timespan == 43200}> on:click={() => { update_graphs(10080, 10, false) }}
Month (1d) class:button_highlight={graph_timespan == 10080}>
</button> Week (10m)
<button </button>
on:click={() => { update_graphs(131400, 1440, false) }} <button
class:button_highlight={graph_timespan == 131400}> on:click={() => { update_graphs(20160, 60, false) }}
Quarter (1d) class:button_highlight={graph_timespan == 20160}>
</button> Two Weeks (1h)
<button </button>
on:click={() => { update_graphs(525600, 1440, false) }} <button
class:button_highlight={graph_timespan == 525600}> on:click={() => { update_graphs(43200, 1440, false) }}
Year (1d) class:button_highlight={graph_timespan == 43200}>
</button> Month (1d)
<button </button>
on:click={() => { update_graphs(1051200, 1440, false) }} <button
class:button_highlight={graph_timespan == 1051200}> on:click={() => { update_graphs(131400, 1440, false) }}
Two Years (1d) class:button_highlight={graph_timespan == 131400}>
</button> Quarter (1d)
<br/> </button>
Total usage from {time_start} to {time_end}<br/> <button
{formatThousands(total_views)} views, on:click={() => { update_graphs(525600, 1440, false) }}
{formatThousands(total_downloads)} downloads, class:button_highlight={graph_timespan == 525600}>
{formatDataVolume(total_bandwidth, 3)} bandwidth and Year (1d)
{formatDataVolume(total_transfer_paid, 3)} paid transfers </button>
</div> <button
<div class="limit_width"> on:click={() => { update_graphs(1051200, 1440, false) }}
<h3>Premium transfers and total bandwidth usage</h3> class:button_highlight={graph_timespan == 1051200}>
<p> Two Years (1d)
A premium transfer is when a file is downloaded using the data cap </button>
on your subscription plan. These can be files you downloaded from <br/>
other people, or other people downloading your files if you have Total usage from {time_start} to {time_end}<br/>
bandwidth sharing enabled. Bandwidth sharing can be changed on {formatThousands(total_views)} views,
<a href="/user/subscription">the subscription page</a>. {formatThousands(total_downloads)} downloads,
</p> {formatDataVolume(total_bandwidth, 3)} bandwidth and
<p> {formatDataVolume(total_transfer_paid, 3)} paid transfers
Total bandwidth usage is the combined bandwidth usage of all the
files on your account. This includes paid transfers.
</p>
</div>
<Chart bind:this={graph_bandwidth} data_type="bytes"/>
<div class="limit_width">
<h3>Views and downloads</h3>
<p>
A view is counted when someone visits the download page of one of
your files. Views are unique per user per file.
</p>
<p>
Downloads are counted when a user clicks the download button on one
of your files. It does not matter whether the download is completed
or not, only the start of the download is counted.
</p>
</div>
<Chart bind:this={graph_views_downloads} data_type="number"/>
</div> </div>
<section>
<h3>Premium transfers and total bandwidth usage</h3>
<p>
A premium transfer is when a file is downloaded using the data cap
on your subscription plan. These can be files you downloaded from
other people, or other people downloading your files if you have
bandwidth sharing enabled. Bandwidth sharing can be changed on
<a href="/user/subscription">the subscription page</a>.
</p>
<p>
Total bandwidth usage is the combined bandwidth usage of all the
files on your account. This includes paid transfers.
</p>
</section>
<Chart bind:this={graph_bandwidth} data_type="bytes"/>
<section>
<h3>Views and downloads</h3>
<p>
A view is counted when someone visits the download page of one of
your files. Views are unique per user per file.
</p>
<p>
Downloads are counted when a user clicks the download button on one
of your files. It does not matter whether the download is completed
or not, only the start of the download is counted.
</p>
</section>
<Chart bind:this={graph_views_downloads} data_type="number"/>

View File

@@ -8,29 +8,27 @@ export let used = 0
$: frac = used / total $: frac = used / total
</script> </script>
<div> <ProgressBar total={total} used={used}></ProgressBar>
<ProgressBar total={total} used={used}></ProgressBar>
{#if frac > 0.99} {#if frac > 0.99}
<div class="highlight_red"> <div class="highlight_red">
You have used all of your data cap. People can still download your You have used all of your data cap. People can still download your
files, but not directly from the API anymore. The file viewer shows files, but not directly from the API anymore. The file viewer shows
ads on your files and download speeds are limited. ads on your files and download speeds are limited.
<br/> <br/>
<a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain"> <a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain">
Upgrade options Upgrade options
</a> </a>
</div> </div>
{:else if frac > 0.8} {:else if frac > 0.8}
<div class="highlight_yellow"> <div class="highlight_yellow">
You have used {(frac*100).toFixed(0)}% of your data cap. If your You have used {(frac*100).toFixed(0)}% of your data cap. If your
data runs out people won't be able to download your files directly data runs out people won't be able to download your files directly
from the API anymore, ads will be shown on the file viewer and from the API anymore, ads will be shown on the file viewer and
transfer rates will be limited. transfer rates will be limited.
<br/> <br/>
<a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain"> <a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain">
Upgrade options Upgrade options
</a> </a>
</div> </div>
{/if} {/if}
</div>

View File

@@ -33,7 +33,7 @@ onMount(() => {
<svelte:window on:popstate={get_page} /> <svelte:window on:popstate={get_page} />
<div class="checkers inset"> <header>
<h1>Welcome home, {window.user.username}!</h1> <h1>Welcome home, {window.user.username}!</h1>
<div class="tab_bar"> <div class="tab_bar">
@@ -75,7 +75,7 @@ onMount(() => {
</a> </a>
{/if} {/if}
</div> </div>
</div> </header>
<br/> <br/>
{#if page === "home"} {#if page === "home"}

View File

@@ -7,56 +7,54 @@ export let used = 0
$: frac = used / total $: frac = used / total
</script> </script>
<div> Storage:
Storage: {formatDataVolume(used, 3)}
{formatDataVolume(used, 3)} out of
out of {formatDataVolume(total, 3)}
{formatDataVolume(total, 3)} <br/>
<br/> <ProgressBar total={total} used={used}></ProgressBar>
<ProgressBar total={total} used={used}></ProgressBar>
{#if frac > 2.0} {#if frac > 2.0}
<div class="highlight_red"> <div class="highlight_red">
<span class="warn_text">You are using more than 200% of your allowed storage space!</span> <span class="warn_text">You are using more than 200% of your allowed storage space!</span>
<p> <p>
We have started deleting your files to free up space. If you do We have started deleting your files to free up space. If you do
not want to lose any more files please upgrade to a storage plan not want to lose any more files please upgrade to a storage plan
which supports the volume of storage which you need: which supports the volume of storage which you need:
<a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain"> <a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain">
Upgrade options Upgrade options
</a> </a>
</p> </p>
</div> </div>
{:else if frac > 0.99} {:else if frac > 0.99}
<div class="highlight_red"> <div class="highlight_red">
<p> <p>
You have used all of your storage space. You won't be able to You have used all of your storage space. You won't be able to
upload new files anymore. Please upgrade to a higher support upload new files anymore. Please upgrade to a higher support
tier to continue uploading files: tier to continue uploading files:
<a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain"> <a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain">
Upgrade options Upgrade options
</a> </a>
</p> </p>
<p> <p>
Your files will not be deleted any sooner than normal at this Your files will not be deleted any sooner than normal at this
moment. When your storage usage is over 200% we will start moment. When your storage usage is over 200% we will start
deleting your files to free up the space. deleting your files to free up the space.
</p> </p>
</div> </div>
{:else if frac > 0.8} {:else if frac > 0.8}
<div class="highlight_yellow"> <div class="highlight_yellow">
<p> <p>
You have used {(frac*100).toFixed(0)}% of your You have used {(frac*100).toFixed(0)}% of your
storage space. If your storage space runs out you won't be able storage space. If your storage space runs out you won't be able
to upload new files anymore. Please upgrade to a higher support to upload new files anymore. Please upgrade to a higher support
tier to continue uploading files: tier to continue uploading files:
<a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain"> <a class="button button_highlight" href="https://www.patreon.com/join/pixeldrain">
Upgrade options Upgrade options
</a> </a>
</p> </p>
</div> </div>
{/if} {/if}
</div>
<style> <style>
.warn_text { .warn_text {

View File

@@ -74,170 +74,169 @@ onMount(load_tranfer_used)
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner /> </div>
</div> {/if}
{/if}
<div class="limit_width">
<h2>Manage subscription</h2>
{#if window.user.subscription.type !== "patreon"}
<p>
Current account balance: <Euro amount={window.user.balance_micro_eur}></Euro>
</p>
<p>
When your prepaid subscription is active you will be charged daily
based on usage. Hotlink bandwidth is charged per TB based on the
usage of the previous day. The amount charged for storage each day
is your storage usage at the end of the day multiplied by the
storage price (€4 / TB) and divided by the average number of days in
a month (30.4375). So if you have exactly 1 TB on your account you
will be charged €0.131416838 per day.
</p>
<p>
The prepaid subscription will stay active for as long as you have
credit on your account. When you reach negative balance the
subscription will automatically end. You will need a positive
balance to activate the subscription again.
</p>
<h3>Prepaid plans</h3> <section>
{#if result !== ""} <h2>Manage subscription</h2>
<div class:highlight_green={result_success} class:highlight_red={!result_success}> {#if window.user.subscription.type !== "patreon"}
{result} <p>
</div> Current account balance: <Euro amount={window.user.balance_micro_eur}></Euro>
{/if} </p>
<p>
When your prepaid subscription is active you will be charged daily
based on usage. Hotlink bandwidth is charged per TB based on the
usage of the previous day. The amount charged for storage each day
is your storage usage at the end of the day multiplied by the
storage price (€4 / TB) and divided by the average number of days in
a month (30.4375). So if you have exactly 1 TB on your account you
will be charged €0.131416838 per day.
</p>
<p>
The prepaid subscription will stay active for as long as you have
credit on your account. When you reach negative balance the
subscription will automatically end. You will need a positive
balance to activate the subscription again.
</p>
<div class="feat_table"> <h3>Prepaid plans</h3>
<div> {#if result !== ""}
<div class="feat_label" class:feat_highlight={subscription === "prepaid"}> <div class:highlight_green={result_success} class:highlight_red={!result_success}>
Prepaid<br/> {result}
{#if subscription === "prepaid"}
Currently active
{:else}
<button on:click={() => {subscription = "prepaid"; update("subscription")}}>
<i class="icon">attach_money</i>
Activate
</button>
{/if}
</div>
<div class="feat_normal round_tr">
<ul>
<li>Base price of €1 per month</li>
<li>€4 per TB per month for storage</li>
<li>€2 per TB for data transfer</li>
<li>Files never expire as long as subscription is active</li>
</ul>
</div>
</div>
<div>
<div class="feat_label" class:feat_highlight={subscription === "prepaid_temp_storage_120d"}>
120 days storage<br/>
{#if subscription === "prepaid_temp_storage_120d"}
Currently active
{:else}
<button on:click={() => {subscription = "prepaid_temp_storage_120d"; update("subscription")}}>
<i class="icon">attach_money</i>
Activate
</button>
{/if}
</div>
<div class="feat_normal">
<ul>
<li>Base price of €1 per month</li>
<li>€2 per TB per month for storage</li>
<li>€2 per TB for data transfer</li>
<li>Files expire 120 days after the last time they're viewed</li>
</ul>
</div>
</div>
<div>
<div class="feat_label" class:feat_highlight={subscription === "prepaid_temp_storage_60d"}>
60 days storage<br/>
{#if subscription === "prepaid_temp_storage_60d"}
Currently active
{:else}
<button on:click={() => {subscription = "prepaid_temp_storage_60d"; update("subscription")}}>
<i class="icon">attach_money</i>
Activate
</button>
{/if}
</div>
<div class="feat_normal">
<ul>
<li>Base price of €1 per month</li>
<li>€1 per TB per month for storage</li>
<li>€2 per TB for data transfer</li>
<li>Files expire 60 days after the last time they're viewed</li>
</ul>
</div>
</div>
<div>
<div class="feat_label" class:feat_highlight={subscription === ""}>
Free<br/>
{#if subscription === ""}
Currently active
{:else}
<button on:click={() => {subscription = ""; update("subscription")}}>
<i class="icon">money_off</i>
Activate
</button>
{/if}
</div>
<div class="feat_normal round_br">
<ul>
<li>Standard free plan, files expire after 30 days.</li>
</ul>
</div>
</div>
</div> </div>
{/if} {/if}
<h3>Bandwidth sharing</h3> <div class="feat_table">
{#if hotlinking} <div>
<button on:click={() => { hotlinking = false; update("limits") }}> <div class="feat_label" class:feat_highlight={subscription === "prepaid"}>
<i class="icon green">check</i> ON (click to turn off) Prepaid<br/>
</button> {#if subscription === "prepaid"}
{:else} Currently active
<button on:click={() => { hotlinking = true; update("limits") }}> {:else}
<i class="icon red">close</i> OFF (click to turn on) <button on:click={() => {subscription = "prepaid"; update("subscription")}}>
</button> <i class="icon">attach_money</i>
{/if} Activate
<p> </button>
When bandwidth sharing is enabled all the bandwidth that your files {/if}
use will be subtracted from your data cap. Advertisements will be </div>
disabled on the download pages for your files and download speed <div class="feat_normal round_tr">
will be unlimited. The rate limiting captcha for files is also <ul>
disabled when bandwidth sharing is on. You can directly embed your <li>Base price of €1 per month</li>
file's download link anywhere, you don't need to use the file viewer <li>€4 per TB per month for storage</li>
page. <li>€2 per TB for data transfer</li>
</p> <li>Files never expire as long as subscription is active</li>
</ul>
</div>
</div>
<div>
<div class="feat_label" class:feat_highlight={subscription === "prepaid_temp_storage_120d"}>
120 days storage<br/>
{#if subscription === "prepaid_temp_storage_120d"}
Currently active
{:else}
<button on:click={() => {subscription = "prepaid_temp_storage_120d"; update("subscription")}}>
<i class="icon">attach_money</i>
Activate
</button>
{/if}
</div>
<div class="feat_normal">
<ul>
<li>Base price of €1 per month</li>
<li>€2 per TB per month for storage</li>
<li>€2 per TB for data transfer</li>
<li>Files expire 120 days after the last time they're viewed</li>
</ul>
</div>
</div>
<div>
<div class="feat_label" class:feat_highlight={subscription === "prepaid_temp_storage_60d"}>
60 days storage<br/>
{#if subscription === "prepaid_temp_storage_60d"}
Currently active
{:else}
<button on:click={() => {subscription = "prepaid_temp_storage_60d"; update("subscription")}}>
<i class="icon">attach_money</i>
Activate
</button>
{/if}
</div>
<div class="feat_normal">
<ul>
<li>Base price of €1 per month</li>
<li>€1 per TB per month for storage</li>
<li>€2 per TB for data transfer</li>
<li>Files expire 60 days after the last time they're viewed</li>
</ul>
</div>
</div>
<div>
<div class="feat_label" class:feat_highlight={subscription === ""}>
Free<br/>
{#if subscription === ""}
Currently active
{:else}
<button on:click={() => {subscription = ""; update("subscription")}}>
<i class="icon">money_off</i>
Activate
</button>
{/if}
</div>
<div class="feat_normal round_br">
<ul>
<li>Standard free plan, files expire after 30 days.</li>
</ul>
</div>
</div>
</div>
{/if}
<h3>Bill shock limit</h3> <h3>Bandwidth sharing</h3>
<p> {#if hotlinking}
Billshock limit in gigabytes per month (1 TB = 1000 GB). Set to 0 to disable. <button on:click={() => { hotlinking = false; update("limits") }}>
</p> <i class="icon green">check</i> ON (click to turn off)
<form on:submit|preventDefault={() => {update("limits")}} class="billshock_container"> </button>
<input type="number" bind:value={transfer_cap} step="100" min="0"/> {:else}
<div style="margin: 0.5em;">GB</div> <button on:click={() => { hotlinking = true; update("limits") }}>
<button type="submit"> <i class="icon red">close</i> OFF (click to turn on)
<i class="icon">save</i> Save </button>
</button> {/if}
</form> <p>
When bandwidth sharing is enabled all the bandwidth that your files
use will be subtracted from your data cap. Advertisements will be
disabled on the download pages for your files and download speed
will be unlimited. The rate limiting captcha for files is also
disabled when bandwidth sharing is on. You can directly embed your
file's download link anywhere, you don't need to use the file viewer
page.
</p>
Bandwidth used in the last 30 days: {formatDataVolume(transfer_used, 3)}, <h3>Bill shock limit</h3>
new limit: {formatDataVolume(transfer_cap*1e9, 3)} <p>
<ProgressBar used={transfer_used} total={transfer_cap*1e9}></ProgressBar> Billshock limit in gigabytes per month (1 TB = 1000 GB). Set to 0 to disable.
<p> </p>
The billshock limit limits how much bandwidth your account can use <form on:submit|preventDefault={() => {update("limits")}} class="billshock_container">
in a 30 day window. When this limit is reached files will show ads <input type="number" bind:value={transfer_cap} step="100" min="0"/>
again and can only be downloaded from the file viewer page. This is <div style="margin: 0.5em;">GB</div>
mostly useful for prepaid plans, but it works for patreon plans too. <button type="submit">
Set to 0 to disable the limit. <i class="icon">save</i> Save
</p> </button>
</div> </form>
</div>
Bandwidth used in the last 30 days: {formatDataVolume(transfer_used, 3)},
new limit: {formatDataVolume(transfer_cap*1e9, 3)}
<ProgressBar used={transfer_used} total={transfer_cap*1e9}></ProgressBar>
<p>
The billshock limit limits how much bandwidth your account can use
in a 30 day window. When this limit is reached files will show ads
again and can only be downloaded from the file viewer page. This is
mostly useful for prepaid plans, but it works for patreon plans too.
Set to 0 to disable the limit.
</p>
</section>
<style> <style>
.spinner_container { .spinner_container {

View File

@@ -120,157 +120,156 @@ onMount(() => {
}) })
</script> </script>
<div> {#if loading}
{#if loading} <div class="spinner_container">
<div class="spinner_container"> <Spinner />
<Spinner /> </div>
</div> {/if}
{/if}
<div class="limit_width"> <section>
<h2>Deposit credits</h2> <h2>Deposit credits</h2>
<p> <p>
You can deposit credit on your pixeldrain account with Bitcoin, You can deposit credit on your pixeldrain account with Bitcoin,
Lightning network (<a Lightning network (<a
href="https://btcpay.pixeldrain.com/embed/uS2mbWjXUuaAqMh8XLjkjwi8oehFuxeBZxekMxv68LN/BTC/ln" href="https://btcpay.pixeldrain.com/embed/uS2mbWjXUuaAqMh8XLjkjwi8oehFuxeBZxekMxv68LN/BTC/ln"
target="_blank">node info</a>) and Dogecoin. You must pay the full target="_blank">node info</a>) and Dogecoin. You must pay the full
amount as stated on the invoice, else your payment will fail. amount as stated on the invoice, else your payment will fail.
</p> </p>
<p> <p>
Do note that it is not possible to withdraw coins from your Do note that it is not possible to withdraw coins from your
pixeldrain account. It's not a wallet. Any amount of money you pixeldrain account. It's not a wallet. Any amount of money you
deposit has to be used up. deposit has to be used up.
</p> </p>
<div style="text-align: center;"> <div style="text-align: center;">
Deposit amount € Deposit amount €
<input type="number" bind:value={credit_amount} min="1"/> <input type="number" bind:value={credit_amount} min="1"/>
<br/> <br/>
Pay with:<br/> Pay with:<br/>
<button on:click={() => {checkout("btc")}}> <button on:click={() => {checkout("btc")}}>
<span class="icon_unicode"></span> Bitcoin <span class="icon_unicode"></span> Bitcoin
</button> </button>
<button on:click={() => {checkout("btc_lightning")}}> <button on:click={() => {checkout("btc_lightning")}}>
<i class="icon">bolt</i> Lightning network <i class="icon">bolt</i> Lightning network
</button> </button>
<button on:click={() => {checkout("doge")}}> <button on:click={() => {checkout("doge")}}>
<span class="icon_unicode">Ð</span> Dogecoin <span class="icon_unicode">Ð</span> Dogecoin
</button> </button>
</div> </div>
<h3>Open invoices</h3>
<div class="table_scroll">
<table style="text-align: left;">
<thead>
<tr>
<td>Created</td>
<td>Amount</td>
<td>Status</td>
<td></td>
</tr>
</thead>
<tbody>
{#each invoices as row (row.id)}
{#if row.status === "New" ||
row.status === "InvoiceCreated" ||
row.status === "InvoiceProcessing" ||
show_expired
}
<tr>
<td>{formatDate(row.time, true, true, false)}</td>
<td><Euro amount={row.amount}></Euro></td>
<td>
{#if row.status === "InvoiceCreated"}
New (waiting for payment)
{:else if row.status === "InvoiceProcessing"}
Payment received, waiting for confirmations
{:else if row.status === "InvoiceSettled"}
Paid
{:else if row.status === "InvoiceExpired"}
Expired
{:else}
{row.status}
{/if}
</td>
<td>
{#if row.status === "New" || row.status === "InvoiceCreated"}
<a href={row.checkout_url} class="button button_highlight">
<i class="icon">paid</i> Pay
</a>
{/if}
</td>
</tr>
{/if}
{/each}
</tbody>
</table>
<div style="text-align: center;">
<button on:click={() => {show_expired = !show_expired}}>
{#if show_expired}
Hide
{:else}
Show
{/if}
expired and settled invoices
</button>
</div>
</div>
<h2>Transaction log</h2>
<p>
Here is a log of all transactions on your account balance.
</p>
{#each months as month}
<h3>{month.month}</h3>
<ul>
<li>Subscription charge: <Euro amount={month.total_subscription_charge}></Euro></li>
<li>Storage charge: <Euro amount={month.total_storage_charge}></Euro></li>
<li>Bandwidth charge: <Euro amount={month.total_bandwidth_charge}></Euro></li>
<li>Total charge: <Euro amount={month.total_deducted}></Euro></li>
<li>Deposited: <Euro amount={month.total_deposited}></Euro></li>
</ul>
<h3>Open invoices</h3>
<div class="table_scroll"> <div class="table_scroll">
<table style="text-align: left;"> <table style="text-align: left;">
<thead> <thead>
<tr> <tr>
<td>Created</td> <td>Time</td>
<td>Amount</td> <td>Balance</td>
<td>Status</td> <td>Subscription</td>
<td colspan="2">Storage</td>
<td colspan="2">Bandwidth</td>
<td>Deposited</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Charged</td>
<td>Charged</td>
<td>Used</td>
<td>Charged</td>
<td>Used</td>
<td></td> <td></td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each invoices as row (row.id)} {#each month.rows as row}
{#if row.status === "New" || <tr>
row.status === "InvoiceCreated" || <td>{formatDate(row.time, true, true, false)}</td>
row.status === "InvoiceProcessing" || <td><Euro amount={row.new_balance}></Euro></td>
show_expired <td><Euro amount={row.subscription_charge} precision="4"></Euro></td>
} <td><Euro amount={row.storage_charge} precision="4"></Euro></td>
<tr> <td>{formatDataVolume(row.storage_used, 3)}</td>
<td>{formatDate(row.time, true, true, false)}</td> <td><Euro amount={row.bandwidth_charge} precision="4"></Euro></td>
<td><Euro amount={row.amount}></Euro></td> <td>{formatDataVolume(row.bandwidth_used, 3)}</td>
<td> <td><Euro amount={row.deposit_amount}></Euro></td>
{#if row.status === "InvoiceCreated"} </tr>
New (waiting for payment)
{:else if row.status === "InvoiceProcessing"}
Payment received, waiting for confirmations
{:else if row.status === "InvoiceSettled"}
Paid
{:else if row.status === "InvoiceExpired"}
Expired
{:else}
{row.status}
{/if}
</td>
<td>
{#if row.status === "New" || row.status === "InvoiceCreated"}
<a href={row.checkout_url} class="button button_highlight">
<i class="icon">paid</i> Pay
</a>
{/if}
</td>
</tr>
{/if}
{/each} {/each}
</tbody> </tbody>
</table> </table>
<div style="text-align: center;">
<button on:click={() => {show_expired = !show_expired}}>
{#if show_expired}
Hide
{:else}
Show
{/if}
expired and settled invoices
</button>
</div>
</div> </div>
{/each}
</section>
<h2>Transaction log</h2>
<p>
Here is a log of all transactions on your account balance.
</p>
{#each months as month}
<h3>{month.month}</h3>
<ul>
<li>Subscription charge: <Euro amount={month.total_subscription_charge}></Euro></li>
<li>Storage charge: <Euro amount={month.total_storage_charge}></Euro></li>
<li>Bandwidth charge: <Euro amount={month.total_bandwidth_charge}></Euro></li>
<li>Total charge: <Euro amount={month.total_deducted}></Euro></li>
<li>Deposited: <Euro amount={month.total_deposited}></Euro></li>
</ul>
<div class="table_scroll">
<table style="text-align: left;">
<thead>
<tr>
<td>Time</td>
<td>Balance</td>
<td>Subscription</td>
<td colspan="2">Storage</td>
<td colspan="2">Bandwidth</td>
<td>Deposited</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Charged</td>
<td>Charged</td>
<td>Used</td>
<td>Charged</td>
<td>Used</td>
<td></td>
</tr>
</thead>
<tbody>
{#each month.rows as row}
<tr>
<td>{formatDate(row.time, true, true, false)}</td>
<td><Euro amount={row.new_balance}></Euro></td>
<td><Euro amount={row.subscription_charge} precision="4"></Euro></td>
<td><Euro amount={row.storage_charge} precision="4"></Euro></td>
<td>{formatDataVolume(row.storage_used, 3)}</td>
<td><Euro amount={row.bandwidth_charge} precision="4"></Euro></td>
<td>{formatDataVolume(row.bandwidth_used, 3)}</td>
<td><Euro amount={row.deposit_amount}></Euro></td>
</tr>
{/each}
</tbody>
</table>
</div>
{/each}
</div>
</div>
<style> <style>
.spinner_container { .spinner_container {

View File

@@ -28,10 +28,8 @@ export const toggle = () => {
.expandable { .expandable {
text-decoration: none; text-decoration: none;
background-color: var(--layer_3_color); background-color: var(--layer_3_color);
transition: box-shadow 0.5s; margin: 0.8em 0;
box-shadow: 1px 1px 6px -2px var(--shadow_color); border-radius: 6px;
margin: 1em 0;
border-radius: 8px;
overflow: hidden; overflow: hidden;
} }
.header { .header {