mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2024-12-22 08:16:28 +00:00
add password to frontend
This commit is contained in:
parent
6da28a701e
commit
fdc2722fb9
@ -92,7 +92,7 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*:disabled,
|
*:disabled,
|
||||||
*[disabled='true'] {
|
.disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,3 +126,13 @@ fieldset {
|
|||||||
.tr {
|
.tr {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid var(--ui-bg-1);
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
@ -8,48 +8,69 @@
|
|||||||
|
|
||||||
export let note: Note
|
export let note: Note
|
||||||
export let timeExpiration = false
|
export let timeExpiration = false
|
||||||
|
|
||||||
|
let customPassword = false
|
||||||
|
|
||||||
|
$: if (!customPassword) note.password = undefined
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="fields">
|
<div class="flex col">
|
||||||
<TextInput
|
<div class="flex">
|
||||||
data-testid="field-views"
|
<TextInput
|
||||||
type="number"
|
data-testid="field-views"
|
||||||
label={$t('common.views', { values: { n: 0 } })}
|
type="number"
|
||||||
bind:value={note.views}
|
label={$t('common.views', { values: { n: 0 } })}
|
||||||
disabled={timeExpiration}
|
bind:value={note.views}
|
||||||
max={$status?.max_views}
|
disabled={timeExpiration}
|
||||||
min={1}
|
max={$status?.max_views}
|
||||||
validate={(v) =>
|
min={1}
|
||||||
($status && v <= $status?.max_views && v > 0) ||
|
validate={(v) =>
|
||||||
$t('home.errors.max', { values: { n: $status?.max_views ?? 0 } })}
|
($status && v <= $status?.max_views && v > 0) ||
|
||||||
/>
|
$t('home.errors.max', { values: { n: $status?.max_views ?? 0 } })}
|
||||||
<div class="middle-switch">
|
/>
|
||||||
<Switch
|
<Switch
|
||||||
data-testid="switch-advanced-toggle"
|
data-testid="switch-advanced-toggle"
|
||||||
label={$t('common.mode')}
|
label={$t('common.mode')}
|
||||||
bind:value={timeExpiration}
|
bind:value={timeExpiration}
|
||||||
color={false}
|
color={false}
|
||||||
/>
|
/>
|
||||||
|
<TextInput
|
||||||
|
data-testid="field-expiration"
|
||||||
|
type="number"
|
||||||
|
label={$t('common.minutes', { values: { n: 0 } })}
|
||||||
|
bind:value={note.expiration}
|
||||||
|
disabled={!timeExpiration}
|
||||||
|
max={$status?.max_expiration}
|
||||||
|
validate={(v) =>
|
||||||
|
($status && v < $status?.max_expiration) ||
|
||||||
|
$t('home.errors.max', { values: { n: $status?.max_expiration ?? 0 } })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<Switch bind:value={customPassword} label={$t('home.advanced.custom_password')} />
|
||||||
|
<TextInput
|
||||||
|
type="password"
|
||||||
|
bind:value={note.password}
|
||||||
|
label={$t('common.password')}
|
||||||
|
disabled={!customPassword}
|
||||||
|
random
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{$t('home.advanced.explanation')}
|
||||||
</div>
|
</div>
|
||||||
<TextInput
|
|
||||||
data-testid="field-expiration"
|
|
||||||
type="number"
|
|
||||||
label={$t('common.minutes', { values: { n: 0 } })}
|
|
||||||
bind:value={note.expiration}
|
|
||||||
disabled={!timeExpiration}
|
|
||||||
max={$status?.max_expiration}
|
|
||||||
validate={(v) =>
|
|
||||||
($status && v < $status?.max_expiration) ||
|
|
||||||
$t('home.errors.max', { values: { n: $status?.max_expiration ?? 0 } })}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.middle-switch {
|
.flex {
|
||||||
margin: 0 1rem;
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 1rem;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fields {
|
.col {
|
||||||
display: flex;
|
gap: 1.5rem;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export type NoteResult = {
|
export type NoteResult = {
|
||||||
password: string
|
|
||||||
id: string
|
id: string
|
||||||
|
password?: string
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
export let result: NoteResult
|
export let result: NoteResult
|
||||||
|
|
||||||
$: url = `${window.location.origin}/note/${result.id}#${result.password}`
|
let url = `${window.location.origin}/note/${result.id}`
|
||||||
|
if (result.password) url += `#${result.password}`
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
|
@ -4,43 +4,35 @@
|
|||||||
export let color = true
|
export let color = true
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div {...$$restProps}>
|
<label {...$$restProps}>
|
||||||
<label class="switch">
|
<small>{label}</small>
|
||||||
<small>{label}</small>
|
<input type="checkbox" bind:checked={value} />
|
||||||
<input type="checkbox" bind:checked={value} />
|
<span class:color class="slider" />
|
||||||
<span class:color class="slider" />
|
</label>
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
label {
|
||||||
height: 3.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 4rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch input {
|
label input {
|
||||||
opacity: 0;
|
display: none;
|
||||||
width: 0;
|
}
|
||||||
height: 0;
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
position: absolute;
|
display: block;
|
||||||
|
width: 4rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
border: 2px solid var(--ui-bg-1);
|
border: 2px solid var(--ui-bg-1);
|
||||||
background-color: var(--ui-bg-0);
|
background-color: var(--ui-bg-0);
|
||||||
transition: var(--ui-anim);
|
|
||||||
transform: translateY(1.2rem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider:before {
|
.slider:before {
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<small disabled={$$restProps.disabled}>
|
<small class:disabled={$$restProps.disabled}>
|
||||||
{label}
|
{label}
|
||||||
{#if valid !== true}
|
{#if valid !== true}
|
||||||
<span class="error-text">{valid}</span>
|
<span class="error-text">{valid}</span>
|
||||||
@ -54,6 +54,7 @@
|
|||||||
label {
|
label {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
label > small {
|
label > small {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AES, Hex } from 'occulto'
|
import { AES, Hex, Bytes } from 'occulto'
|
||||||
import { t } from 'svelte-intl-precompile'
|
import { t } from 'svelte-intl-precompile'
|
||||||
import { blur } from 'svelte/transition'
|
import { blur } from 'svelte/transition'
|
||||||
|
|
||||||
@ -57,13 +57,14 @@
|
|||||||
try {
|
try {
|
||||||
loading = $t('common.encrypting')
|
loading = $t('common.encrypting')
|
||||||
|
|
||||||
const key = await AES.generateKey()
|
const derived = note.password && (await AES.derive(note.password))
|
||||||
const password = Hex.encode(key)
|
const key = derived ? derived[0] : await AES.generateKey()
|
||||||
|
|
||||||
const data: Note = {
|
const data: Note = {
|
||||||
contents: '',
|
contents: '',
|
||||||
meta: note.meta,
|
meta: note.meta,
|
||||||
}
|
}
|
||||||
|
if (derived) data.meta.derivation = derived[1]
|
||||||
if (isFile) {
|
if (isFile) {
|
||||||
if (files.length === 0) throw new EmptyContentError()
|
if (files.length === 0) throw new EmptyContentError()
|
||||||
data.contents = await Adapters.Files.encrypt(files, key)
|
data.contents = await Adapters.Files.encrypt(files, key)
|
||||||
@ -77,8 +78,8 @@
|
|||||||
loading = $t('common.uploading')
|
loading = $t('common.uploading')
|
||||||
const response = await create(data)
|
const response = await create(data)
|
||||||
result = {
|
result = {
|
||||||
password: password,
|
|
||||||
id: response.id,
|
id: response.id,
|
||||||
|
password: note.password ? undefined : Hex.encode(key),
|
||||||
}
|
}
|
||||||
notify.success($t('home.messages.note_created'))
|
notify.success($t('home.messages.note_created'))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -148,7 +149,7 @@
|
|||||||
|
|
||||||
{#if advanced}
|
{#if advanced}
|
||||||
<div transition:blur={{ duration: 250 }}>
|
<div transition:blur={{ duration: 250 }}>
|
||||||
<br />
|
<hr />
|
||||||
<AdvancedParameters bind:note bind:timeExpiration />
|
<AdvancedParameters bind:note bind:timeExpiration />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--ui-bg-0-85);
|
background-color: var(--ui-bg-0-85);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -1,30 +1,35 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Hex } from 'occulto'
|
import { AES, Hex } from 'occulto'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { t } from 'svelte-intl-precompile'
|
import { t } from 'svelte-intl-precompile'
|
||||||
|
|
||||||
import Button from '$lib/ui/Button.svelte'
|
import Button from '$lib/ui/Button.svelte'
|
||||||
import Loader from '$lib/ui/Loader.svelte'
|
import Loader from '$lib/ui/Loader.svelte'
|
||||||
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
|
import ShowNote, { type DecryptedNote } from '$lib/ui/ShowNote.svelte'
|
||||||
import { Adapters, get, info } from '@cryptgeon/shared'
|
import TextInput from '$lib/ui/TextInput.svelte'
|
||||||
|
import { Adapters, get, info, type NoteMeta } from '@cryptgeon/shared'
|
||||||
import type { PageData } from './$types'
|
import type { PageData } from './$types'
|
||||||
|
|
||||||
export let data: PageData
|
export let data: PageData
|
||||||
|
|
||||||
let id = data.id
|
let id = data.id
|
||||||
let password: string
|
let password: string | null = null
|
||||||
let note: DecryptedNote | null = null
|
let note: DecryptedNote | null = null
|
||||||
let exists = false
|
let exists = false
|
||||||
|
let meta: NoteMeta | null = null
|
||||||
|
|
||||||
let loading: string | null = null
|
let loading: string | null = null
|
||||||
let error: string | null = null
|
let error: string | null = null
|
||||||
|
|
||||||
|
$: valid = !!password?.length
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// Check if note exists
|
// Check if note exists
|
||||||
try {
|
try {
|
||||||
loading = $t('common.loading')
|
loading = $t('common.loading')
|
||||||
password = window.location.hash.slice(1)
|
password = window.location.hash.slice(1)
|
||||||
await info(id)
|
const note = await info(id)
|
||||||
|
meta = note.meta
|
||||||
exists = true
|
exists = true
|
||||||
} catch {
|
} catch {
|
||||||
exists = false
|
exists = false
|
||||||
@ -38,11 +43,18 @@
|
|||||||
*/
|
*/
|
||||||
async function show() {
|
async function show() {
|
||||||
try {
|
try {
|
||||||
|
if (!valid) {
|
||||||
|
error = $t('show.errors.no_password')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load note
|
||||||
error = null
|
error = null
|
||||||
loading = $t('common.downloading')
|
loading = $t('common.downloading')
|
||||||
const data = await get(id)
|
const data = await get(id)
|
||||||
loading = $t('common.decrypting')
|
loading = $t('common.decrypting')
|
||||||
const key = Hex.decode(password)
|
const derived = meta?.derivation && (await AES.derive(password!, meta.derivation))
|
||||||
|
const key = derived ? derived[0] : Hex.decode(password!)
|
||||||
switch (data.meta.type) {
|
switch (data.meta.type) {
|
||||||
case 'text':
|
case 'text':
|
||||||
note = {
|
note = {
|
||||||
@ -77,9 +89,18 @@
|
|||||||
<form on:submit|preventDefault={show}>
|
<form on:submit|preventDefault={show}>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<p>{$t('show.explanation')}</p>
|
<p>{$t('show.explanation')}</p>
|
||||||
<Button data-testid="show-note-button" type="submit">{$t('show.show_note')}</Button>
|
{#if meta?.derivation}
|
||||||
|
<TextInput
|
||||||
|
data-testid="show-note-password"
|
||||||
|
type="password"
|
||||||
|
bind:value={password}
|
||||||
|
label={$t('common.password')}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<Button disabled={!valid} data-testid="show-note-button" type="submit"
|
||||||
|
>{$t('show.show_note')}</Button
|
||||||
|
>
|
||||||
{#if error}
|
{#if error}
|
||||||
<br />
|
|
||||||
<p class="error-text">
|
<p class="error-text">
|
||||||
{error}
|
{error}
|
||||||
<br />
|
<br />
|
||||||
@ -97,4 +118,10 @@
|
|||||||
.loader {
|
.loader {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user