mirror of
https://github.com/cupcakearmy/cryptgeon.git
synced 2024-12-22 08:16:28 +00:00
file upload
This commit is contained in:
parent
8eeb2a8de7
commit
8cee6579e2
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -524,6 +524,7 @@ dependencies = [
|
||||
"dotenv",
|
||||
"lazy_static",
|
||||
"memcache",
|
||||
"mime",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -21,3 +21,4 @@ bs62 = "0.1"
|
||||
memcache = "0.16"
|
||||
byte-unit = "4"
|
||||
dotenv = "0.15"
|
||||
mime = "0.3"
|
||||
|
@ -59,7 +59,7 @@ version: '3.7'
|
||||
services:
|
||||
memcached:
|
||||
image: memcached:1-alpine
|
||||
entrypoint: memcached -m 128 # Limit to 128 MB Ram, customize at free will.
|
||||
entrypoint: memcached -m 128M -I 4M # Limit to 128 MB Ram, 4M per entry, customize at free will.
|
||||
|
||||
app:
|
||||
image: cupcakearmy/cryptgeon:latest
|
||||
|
@ -100,7 +100,7 @@ fieldset {
|
||||
|
||||
.box {
|
||||
width: 100%;
|
||||
min-height: min(calc(100vh - 30rem), 30rem);
|
||||
min-height: min(calc(100vh - 30rem), 20rem);
|
||||
margin: 0;
|
||||
border: 2px solid var(--ui-bg-1);
|
||||
resize: vertical;
|
||||
@ -118,3 +118,7 @@ fieldset {
|
||||
.box:focus {
|
||||
border-color: var(--ui-clr-primary);
|
||||
}
|
||||
|
||||
.tr {
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ type CallOptions = {
|
||||
|
||||
export class PayloadToLargeError extends Error {}
|
||||
|
||||
async function call(options: CallOptions) {
|
||||
export async function call(options: CallOptions) {
|
||||
const response = await fetch('/api/' + options.url, {
|
||||
method: options.method,
|
||||
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
||||
@ -46,7 +46,7 @@ export async function create(note: Note) {
|
||||
meta: JSON.stringify(meta),
|
||||
}
|
||||
const data = await call({
|
||||
url: 'notes',
|
||||
url: 'notes/',
|
||||
method: 'post',
|
||||
body,
|
||||
})
|
||||
|
17
client/src/lib/stores/status.ts
Normal file
17
client/src/lib/stores/status.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { call } from '$lib/api'
|
||||
import { onMount } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
export type Status = {
|
||||
max_size: number
|
||||
}
|
||||
|
||||
export const status = writable<null | Status>(null)
|
||||
|
||||
export async function init() {
|
||||
const data = await call({
|
||||
url: 'status',
|
||||
method: 'get',
|
||||
})
|
||||
status.set(data)
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
import type { FileDTO } from '$lib/api'
|
||||
import { Files } from '$lib/files'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import MaxSize from './MaxSize.svelte'
|
||||
|
||||
export let label: string = ''
|
||||
let files: File[] = []
|
||||
@ -49,6 +50,8 @@
|
||||
{:else}
|
||||
<div>
|
||||
<b>No Files Selected</b>
|
||||
<br />
|
||||
<small>max: <MaxSize /></small>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
12
client/src/lib/ui/MaxSize.svelte
Normal file
12
client/src/lib/ui/MaxSize.svelte
Normal file
@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { status } from '$lib/stores/status'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
</script>
|
||||
|
||||
<span>
|
||||
{#if $status !== null}
|
||||
{prettyBytes($status.max_size, { binary: true })}
|
||||
{:else}
|
||||
loading...
|
||||
{/if}
|
||||
</span>
|
@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { Note, PayloadToLargeError } from '$lib/api'
|
||||
import { create } from '$lib/api'
|
||||
import { getKeyFromString, encrypt, Hex, getRandomBytes } from '$lib/crypto'
|
||||
|
||||
import { create, Note, PayloadToLargeError } from '$lib/api'
|
||||
import { encrypt, getKeyFromString, getRandomBytes, Hex } from '$lib/crypto'
|
||||
import Button from '$lib/ui/Button.svelte'
|
||||
import FileUpload from '$lib/ui/FileUpload.svelte'
|
||||
import MaxSize from '$lib/ui/MaxSize.svelte'
|
||||
import Switch from '$lib/ui/Switch.svelte'
|
||||
import TextArea from '$lib/ui/TextArea.svelte'
|
||||
import TextInput from '$lib/ui/TextInput.svelte'
|
||||
import { blur } from 'svelte/transition'
|
||||
|
||||
let note: Note = {
|
||||
contents: '',
|
||||
@ -36,6 +36,10 @@
|
||||
|
||||
$: note.meta.type = file ? 'file' : 'text'
|
||||
|
||||
$: if (!file) {
|
||||
note.contents = ''
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
try {
|
||||
error = null
|
||||
@ -110,8 +114,12 @@
|
||||
<Switch class="file" label="file" bind:value={file} />
|
||||
<Switch label="advanced" bind:value={advanced} />
|
||||
<div class="grow" />
|
||||
<div class="tr">
|
||||
<small>max: <MaxSize /> </small>
|
||||
<br />
|
||||
<Button type="submit" data-testid="button-create">create</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="error-text">{error}</div>
|
||||
@ -126,7 +134,8 @@
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
<div class="advanced" class:hidden={!advanced}>
|
||||
{#if advanced}
|
||||
<div transition:blur={{ duration: 250 }}>
|
||||
<br />
|
||||
<div class="fields">
|
||||
<TextInput
|
||||
@ -148,15 +157,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.fields {
|
||||
display: flex;
|
||||
}
|
||||
.spacer {
|
||||
width: 3rem;
|
||||
}
|
||||
</style>
|
||||
{/if}
|
||||
</fieldset>
|
||||
</form>
|
||||
{/if}
|
||||
@ -164,7 +165,6 @@
|
||||
<style>
|
||||
.bottom {
|
||||
display: flex;
|
||||
/* justify-content: space-between; */
|
||||
align-items: flex-end;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
@ -181,17 +181,11 @@
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.advanced {
|
||||
max-height: 14em;
|
||||
overflow: hidden;
|
||||
transition: var(--ui-anim);
|
||||
}
|
||||
|
||||
.advanced.hidden {
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.fields {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,8 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { init } from '$lib/stores/status'
|
||||
import Footer from '$lib/views/Footer.svelte'
|
||||
import Header from '$lib/views/Header.svelte'
|
||||
|
||||
import { onMount } from 'svelte'
|
||||
import '../app.css'
|
||||
|
||||
onMount(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import httpProxy from 'http-proxy'
|
||||
import http from 'http'
|
||||
import httpProxy from 'http-proxy'
|
||||
|
||||
const proxy = httpProxy.createProxyServer({})
|
||||
|
||||
|
@ -14,13 +14,6 @@ async fn main() -> std::io::Result<()> {
|
||||
dotenv().ok();
|
||||
return HttpServer::new(|| {
|
||||
App::new()
|
||||
// .configure(|cfg: &mut web::ServiceConfig| {
|
||||
// let limit_string = std::env::var("SIZE_LIMIT").unwrap_or("0.1 KiB".to_string());
|
||||
// let limit = Byte::from_str(limit_string.clone()).unwrap().get_bytes() as usize;
|
||||
// println!("SIZE_LIMIT: {}, {}", limit_string, limit);
|
||||
// let config = web::JsonConfig::default().limit(limit);
|
||||
// cfg.data(config);
|
||||
// })
|
||||
.wrap(middleware::Compress::default())
|
||||
.wrap(middleware::DefaultHeaders::default())
|
||||
.configure(size::init)
|
||||
|
@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use crate::note::{generate_id, Note, NoteInfo, NotePublic};
|
||||
use crate::size::LIMIT;
|
||||
use crate::store;
|
||||
|
||||
fn now() -> u64 {
|
||||
@ -17,7 +18,7 @@ struct NotePath {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[get("/notes/{id}")]
|
||||
#[get("/{id}")]
|
||||
async fn one(path: web::Path<NotePath>) -> impl Responder {
|
||||
let p = path.into_inner();
|
||||
let note = store::get(&p.id);
|
||||
@ -32,7 +33,7 @@ struct CreateResponse {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[post("/notes")]
|
||||
#[post("/")]
|
||||
async fn create(note: web::Json<Note>) -> impl Responder {
|
||||
let mut n = note.into_inner();
|
||||
let id = generate_id();
|
||||
@ -62,7 +63,7 @@ async fn create(note: web::Json<Note>) -> impl Responder {
|
||||
return HttpResponse::Ok().json(CreateResponse { id: id });
|
||||
}
|
||||
|
||||
#[delete("/notes/{id}")]
|
||||
#[delete("/{id}")]
|
||||
async fn delete(path: web::Path<NotePath>) -> impl Responder {
|
||||
let p = path.into_inner();
|
||||
let note = store::get(&p.id);
|
||||
@ -102,11 +103,27 @@ async fn delete(path: web::Path<NotePath>) -> impl Responder {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Status {
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
#[get("/status")]
|
||||
async fn status() -> impl Responder {
|
||||
println!("Limit: {}", *LIMIT);
|
||||
return HttpResponse::Ok().json(Status { max_size: *LIMIT });
|
||||
}
|
||||
|
||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("/api")
|
||||
.service(
|
||||
web::scope("/notes")
|
||||
.service(one)
|
||||
.service(create)
|
||||
.service(delete)
|
||||
.service(one),
|
||||
.service(status),
|
||||
)
|
||||
.service(status),
|
||||
);
|
||||
}
|
||||
|
19
src/size.rs
19
src/size.rs
@ -1,9 +1,20 @@
|
||||
use actix_web::web;
|
||||
use byte_unit::Byte;
|
||||
use mime;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref LIMIT: usize =
|
||||
Byte::from_str(std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string()))
|
||||
.unwrap()
|
||||
.get_bytes() as usize;
|
||||
}
|
||||
|
||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
let limit_string = std::env::var("SIZE_LIMIT").unwrap_or("1 KiB".to_string());
|
||||
let limit = Byte::from_str(limit_string).unwrap().get_bytes() as usize;
|
||||
let config = web::JsonConfig::default().limit(limit);
|
||||
cfg.data(config);
|
||||
println!("Limit: {}", *LIMIT);
|
||||
let json = web::JsonConfig::default().limit(*LIMIT);
|
||||
let plain = web::PayloadConfig::default()
|
||||
.limit(*LIMIT)
|
||||
.mimetype(mime::STAR_STAR);
|
||||
cfg.data(json);
|
||||
cfg.data(plain);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user