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

View File

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

View File

@@ -7,12 +7,31 @@ import { formatDataVolume } from "../util/Formatting.svelte";
let button
let dialog
export let no_login_label = "Pixeldrain"
export let hide_name = true
export let hide_logo = false
const open = () => {
// Show the window so we can get the location
dialog.showModal()
let rect = button.getBoundingClientRect()
dialog.style.top = Math.round(rect.bottom) + "px"
dialog.style.left = Math.round(rect.left) + "px"
const edge_offset = 5
// 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
@@ -25,9 +44,11 @@ const click = e => {
<div class="wrapper">
<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>
<span class="button_username">
{window.user.username === "" ? "Pixeldrain" : window.user.username}
{#if !hide_logo}
<PixeldrainLogo style="height: 1.8em; width: 1.8em; margin: 2px;"/>
{/if}
<span class="button_username" class:hide_name>
{window.user.username === "" ? no_login_label : window.user.username}
</span>
</button>
</div>
@@ -37,8 +58,10 @@ const click = e => {
<dialog bind:this={dialog} on:click={click}>
<div class="menu">
{#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="stats_table">
<div>Subscription</div>
<div>{window.user.subscription.name}</div>
@@ -54,13 +77,22 @@ const click = e => {
<div>{formatDataVolume(window.user.monthly_transfer_used, 3)}</div>
</div>
<div class="separator"></div>
<Button link_href="/d/me" icon="folder" label="My Filesystem"/>
{#if window.user.subscription.filesystem_access}
<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"/>
<div class="separator"></div>
<Button link_href="/user/filemanager#files" icon="image" label="My Files"/>
<Button link_href="/user/filemanager#lists" icon="photo_library" label="My Albums"/>
<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/subscription" icon="shopping_cart" label="Subscription"/>
<Button link_href="/user/prepaid/transactions" icon="receipt" label="Transactions"/>
@@ -108,10 +140,6 @@ dialog {
flex-direction: column;
max-width: 15em;
}
.menu_username {
text-align: center;
font-size: 1.1em;
}
.separator {
height: 1px;
margin: 2px 0;
@@ -127,7 +155,7 @@ dialog {
/* Hide username on small screen */
@media(max-width: 800px) {
.button_username {
.hide_name {
display: none;
}
}

View File

@@ -1,21 +1,23 @@
<script>
import Menu from "../filesystem/Menu.svelte";
import Footer from "../layout/Footer.svelte";
import AddressReputation from "./AddressReputation.svelte";
import FeatureTable from "./FeatureTable.svelte";
import ForCreators from "./ForCreators.svelte";
import PolicyChange from "./PolicyChange.svelte";
import UploadWidget from "./UploadWidget.svelte";
import UploadLoginWall from "./UploadLoginWall.svelte";
</script>
<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>
<PolicyChange/>
</header>
<AddressReputation/>
<UploadWidget/>
<UploadLoginWall/>
</div>
<div class="page_content">
@@ -99,18 +101,19 @@ header > h1 {
}
.header_image_container {
text-align: initial;
margin: auto;
height: 200px;
width: 750px;
height: 150px;
width: 500px;
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-size: contain;
background-position: center;
}
@media (max-width: 600px) {
.header_image_container {
background-image: url("/res/img/header_orbitron.webp");
}
.menu_button_container {
display: flex;
justify-content: end;
padding-right: 0.5em;
}
</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,
}
let form_elem
let submit = async (event) => {
loading = true
event.preventDefault()
@@ -49,6 +51,8 @@ let submit = async (event) => {
}
} else if (field.type === "description") {
field_values[field.name] = ""
} else if (field.type === "captcha") {
field_values[field.name] = form_elem.getElementsByClassName("g-recaptcha-response")[0].value
} else {
field_values[field.name] = field.binding.value
}
@@ -107,7 +111,7 @@ let handle_errors = (response) => {
}
</script>
<form method="POST" on:submit={submit}>
<form method="POST" on:submit={submit} bind:this={form_elem}>
{#if submitted}
{#if submit_result.messages}
<div id="submit_result" class:highlight_green={submit_result.success} class:highlight_red={!submit_result.success}>