Require login for file upload

This commit is contained in:
2024-06-26 23:28:17 +02:00
parent f0db79cab1
commit 9cbf7d7c1e
9 changed files with 279 additions and 31 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

View File

@@ -163,10 +163,11 @@ const fullscreen = () => {
<IconBlock icon_href={file.icon_href}> <IconBlock icon_href={file.icon_href}>
The online video player on pixeldrain is only available if the viewer or The online video player on pixeldrain is only available while logged in
the uploader of the video has a registered pixeldrain account. You can to an account, or if the uploading user has verified their e-mail
still download the video and watch it locally on your computer without address. You can still download the video and watch it locally on your
an account. computer without an account.
<br/> <br/>
<button on:click={download}> <button on:click={download}>
<i class="icon">download</i> Download <i class="icon">download</i> Download

View File

@@ -7,12 +7,31 @@ import { formatDataVolume } from "../util/Formatting.svelte";
let button let button
let dialog let dialog
export let no_login_label = "Pixeldrain"
export let hide_name = true
export let hide_logo = false
const open = () => { const open = () => {
// Show the window so we can get the location
dialog.showModal() dialog.showModal()
let rect = button.getBoundingClientRect() const edge_offset = 5
dialog.style.top = Math.round(rect.bottom) + "px"
dialog.style.left = Math.round(rect.left) + "px" // Get the egdes of the screen, so the window does not spawn off-screen
const window_rect = dialog.getBoundingClientRect()
const max_left = window.innerWidth - window_rect.width - edge_offset
const max_top = window.innerHeight - window_rect.height - edge_offset
// Get the location of the button
const button_rect = button.getBoundingClientRect()
// Prevent the window from being glued to the edges
const min_left = Math.max(button_rect.left, edge_offset)
const min_top = Math.max(button_rect.bottom, edge_offset)
// Place the window
dialog.style.left = Math.round(Math.min(min_left, max_left)) + "px"
dialog.style.top = Math.round(Math.min(min_top, max_top)) + "px"
} }
// Close the dialog when the user clicks the background // Close the dialog when the user clicks the background
@@ -25,9 +44,11 @@ const click = e => {
<div class="wrapper"> <div class="wrapper">
<button bind:this={button} on:click={open} href="/user" class="button round" title="Menu"> <button bind:this={button} on:click={open} href="/user" class="button round" title="Menu">
<PixeldrainLogo style="height: 1.8em; width: 1.8em; margin: 2px;"></PixeldrainLogo> {#if !hide_logo}
<span class="button_username"> <PixeldrainLogo style="height: 1.8em; width: 1.8em; margin: 2px;"/>
{window.user.username === "" ? "Pixeldrain" : window.user.username} {/if}
<span class="button_username" class:hide_name>
{window.user.username === "" ? no_login_label : window.user.username}
</span> </span>
</button> </button>
</div> </div>
@@ -37,8 +58,10 @@ const click = e => {
<dialog bind:this={dialog} on:click={click}> <dialog bind:this={dialog} on:click={click}>
<div class="menu"> <div class="menu">
{#if window.user.username !== ""} {#if window.user.username !== ""}
<div class="menu_username">{window.user.username}</div>
<Button link_href="/user" icon="person" label={window.user.username} />
<div class="separator"></div> <div class="separator"></div>
<div class="stats_table"> <div class="stats_table">
<div>Subscription</div> <div>Subscription</div>
<div>{window.user.subscription.name}</div> <div>{window.user.subscription.name}</div>
@@ -54,13 +77,22 @@ const click = e => {
<div>{formatDataVolume(window.user.monthly_transfer_used, 3)}</div> <div>{formatDataVolume(window.user.monthly_transfer_used, 3)}</div>
</div> </div>
<div class="separator"></div> <div class="separator"></div>
{#if window.user.subscription.filesystem_access}
<Button link_href="/d/me" icon="folder" label="My Filesystem"/> <Button link_href="/d/me" icon="folder" label="My Filesystem"/>
{:else}
<Button link_href="/#pro" icon="star" label="Get Premium"/>
{/if}
<Button link_href="/filesystem" icon="description" label="Filesystem Guide"/> <Button link_href="/filesystem" icon="description" label="Filesystem Guide"/>
<div class="separator"></div> <div class="separator"></div>
<Button link_href="/user/filemanager#files" icon="image" label="My Files"/> <Button link_href="/user/filemanager#files" icon="image" label="My Files"/>
<Button link_href="/user/filemanager#lists" icon="photo_library" label="My Albums"/> <Button link_href="/user/filemanager#lists" icon="photo_library" label="My Albums"/>
<div class="separator"></div> <div class="separator"></div>
<Button link_href="/user" icon="person" label="Profile"/>
<Button link_href="/user/settings" icon="settings" label="Account Settings"/> <Button link_href="/user/settings" icon="settings" label="Account Settings"/>
<Button link_href="/user/subscription" icon="shopping_cart" label="Subscription"/> <Button link_href="/user/subscription" icon="shopping_cart" label="Subscription"/>
<Button link_href="/user/prepaid/transactions" icon="receipt" label="Transactions"/> <Button link_href="/user/prepaid/transactions" icon="receipt" label="Transactions"/>
@@ -108,10 +140,6 @@ dialog {
flex-direction: column; flex-direction: column;
max-width: 15em; max-width: 15em;
} }
.menu_username {
text-align: center;
font-size: 1.1em;
}
.separator { .separator {
height: 1px; height: 1px;
margin: 2px 0; margin: 2px 0;
@@ -127,7 +155,7 @@ dialog {
/* Hide username on small screen */ /* Hide username on small screen */
@media(max-width: 800px) { @media(max-width: 800px) {
.button_username { .hide_name {
display: none; display: none;
} }
} }

View File

@@ -1,21 +1,23 @@
<script> <script>
import Menu from "../filesystem/Menu.svelte";
import Footer from "../layout/Footer.svelte"; import Footer from "../layout/Footer.svelte";
import AddressReputation from "./AddressReputation.svelte"; import AddressReputation from "./AddressReputation.svelte";
import FeatureTable from "./FeatureTable.svelte"; import FeatureTable from "./FeatureTable.svelte";
import ForCreators from "./ForCreators.svelte"; import ForCreators from "./ForCreators.svelte";
import PolicyChange from "./PolicyChange.svelte"; import UploadLoginWall from "./UploadLoginWall.svelte";
import UploadWidget from "./UploadWidget.svelte";
</script> </script>
<div class="page_content"> <div class="page_content">
<header> <header class="header" style="text-align: initial">
<div class="menu_button_container">
<Menu no_login_label="Not logged in" hide_name={false} hide_logo/>
</div>
<div class="header_image_container"></div> <div class="header_image_container"></div>
<PolicyChange/>
</header> </header>
<AddressReputation/> <AddressReputation/>
<UploadWidget/> <UploadLoginWall/>
</div> </div>
<div class="page_content"> <div class="page_content">
@@ -99,18 +101,19 @@ header > h1 {
} }
.header_image_container { .header_image_container {
text-align: initial;
margin: auto; margin: auto;
height: 200px; height: 150px;
width: 750px; width: 500px;
max-width: 100%; max-width: 100%;
background-image: url("/res/img/header_orbitron_wide.webp"); background-image: url("/res/img/header_orbitron.webp");
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: contain; background-size: contain;
background-position: center; background-position: center;
} }
@media (max-width: 600px) { .menu_button_container {
.header_image_container { display: flex;
background-image: url("/res/img/header_orbitron.webp"); justify-content: end;
} padding-right: 0.5em;
} }
</style> </style>

View File

@@ -0,0 +1,60 @@
<script>
import Login from "../login/Login.svelte";
import Register from "../login/Register.svelte";
import UploadWidget from "./UploadWidget.svelte";
const finish_login = async e => {
location.reload()
}
let page = "login"
</script>
{#if window.user && window.user.username && window.user.username !== ""}
<UploadWidget/>
{:else}
<section>
<p>
Pixeldrain requires an account to upload files to. If you already
have an account use the login form below to log in to your account.
If not, use the right tab button to register a new account.
</p>
</section>
<div class="tab_bar">
<button on:click={() => page = "login"} class:button_highlight={page === "login"}>
<i class="icon small">login</i>
<span>Login</span>
</button>
<button on:click={() => page = "register"} class:button_highlight={page === "register"}>
<i class="icon small">how_to_reg</i>
<span>Register</span>
</button>
</div>
{#if page === "login"}
<section class="highlight_border">
<Login on:login={finish_login}/>
<p>
If you have lost your password, you can <a
href="password_reset">request a new password here</a>.
</p>
</section>
{:else if page === "register"}
<section class="highlight_border">
<Register on:login={finish_login}/>
</section>
{/if}
<br/>
{/if}
<style>
.tab_bar > button {
width: 40%;
max-width: 10em;
font-size: 1.3em;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,43 @@
<script>
import { createEventDispatcher } from "svelte";
import Form from "./../util/Form.svelte"
let dispatch = createEventDispatcher()
let form = {
name: "login",
fields: [
{
name: "username",
label: "Username",
type: "username",
}, {
name: "password",
label: "Password",
type: "current_password",
},
],
submit_label: `<i class="icon">send</i> Login`,
on_submit: async fields => {
const form = new FormData()
form.append("username", fields.username)
form.append("password", fields.password)
form.append("app_name", "website_login")
const resp = await fetch(
window.api_endpoint+"/user/login",
{ method: "POST", body: form }
);
if(resp.status >= 400) {
return {error_json: await resp.json()}
}
let jresp = await resp.json()
dispatch("login", {key: jresp.auth_key})
return {success: true, message: "Successfully logged in"}
},
}
</script>
<Form config={form}></Form>

View File

@@ -0,0 +1,109 @@
<script>
import { createEventDispatcher, onMount } from "svelte";
import Form from "../util/Form.svelte"
let dispatch = createEventDispatcher()
let form = {
name: "register",
fields: [
{
name: "username",
label: "Username",
type: "username",
description: "Used for logging into your account",
}, {
name: "email",
label: "E-mail address",
type: "email",
description: "Your e-mail address is only used for recovering lost passwords",
}, {
name: "password",
label: "Password",
type: "new_password",
}, {
name: "password2",
label: "Password verification",
type: "new_password",
description: "You need to enter your password twice so we " +
"can verify that no typing errors were made, which would " +
"prevent you from logging into your new account"
},
],
submit_label: `<i class="icon">send</i> Register`,
on_submit: async fields => {
if (fields.password !== fields.password2) {
return {
error_json: {
value: "password_verification_failed",
message: "Password verification failed. Please enter the same " +
"password in both password fields"
},
}
}
const form = new FormData()
form.append("username", fields.username)
form.append("email", fields.email)
form.append("password", fields.password)
if (fields.recaptcha_response) {
console.log("resp", fields.recaptcha_response)
form.append("recaptcha_response", fields.recaptcha_response)
}
const resp = await fetch(
window.api_endpoint+"/user/register",
{ method: "POST", body: form }
);
if(resp.status >= 400) {
return {error_json: await resp.json()}
}
// Register successful, now we will try logging in with the same
// credentials
const login_form = new FormData()
login_form.append("username", fields.username)
login_form.append("password", fields.password)
login_form.append("app_name", "website_login")
const login_resp = await fetch(
window.api_endpoint+"/user/login",
{ method: "POST", body: form }
);
if(login_resp.status >= 400) {
return {error_json: await login_resp.json()}
}
let jresp = await login_resp.json()
dispatch("login", {key: jresp.auth_key})
return {success: true, message: "Successfully registered a new account"}
},
}
onMount(async () => {
const resp = await fetch(window.api_endpoint+"/misc/recaptcha");
if(resp.status >= 400) {
alert("Failed to get captcha key: " + await resp.text())
return
}
let jresp = await resp.json()
form.fields.push({
name: "recaptcha_response",
label: "Captcha",
type: "captcha",
captcha_site_key: jresp.site_key,
description: "The reCaptcha test verifies that you " +
"are not an evil robot that is trying to flood the " +
"website with fake accounts. Please click the white box " +
"to prove that you're not a robot"
})
form.fields = form.fields
})
</script>
<Form config={form}></Form>

View File

@@ -34,6 +34,8 @@ let submit_result = {
messages: null, messages: null,
} }
let form_elem
let submit = async (event) => { let submit = async (event) => {
loading = true loading = true
event.preventDefault() event.preventDefault()
@@ -49,6 +51,8 @@ let submit = async (event) => {
} }
} else if (field.type === "description") { } else if (field.type === "description") {
field_values[field.name] = "" field_values[field.name] = ""
} else if (field.type === "captcha") {
field_values[field.name] = form_elem.getElementsByClassName("g-recaptcha-response")[0].value
} else { } else {
field_values[field.name] = field.binding.value field_values[field.name] = field.binding.value
} }
@@ -107,7 +111,7 @@ let handle_errors = (response) => {
} }
</script> </script>
<form method="POST" on:submit={submit}> <form method="POST" on:submit={submit} bind:this={form_elem}>
{#if submitted} {#if submitted}
{#if submit_result.messages} {#if submit_result.messages}
<div id="submit_result" class:highlight_green={submit_result.success} class:highlight_red={!submit_result.success}> <div id="submit_result" class:highlight_green={submit_result.success} class:highlight_red={!submit_result.success}>