WIP
test / test (push) Failing after 38s Details

This commit is contained in:
Luca Bilke 2024-06-26 19:51:18 +02:00
parent 8d25de682a
commit 1b22ce6d22
No known key found for this signature in database
GPG Key ID: C9E851809C1A5BDE
9 changed files with 135 additions and 72 deletions

View File

@ -8,27 +8,10 @@ gleam test # Run the tests
gleam shell # Run an Erlang shell gleam shell # Run an Erlang shell
``` ```
## Flow
### Upload
take file upload
-> get hash of file
-> test against CSAM hash list
-> generate UUID
-> point UUID at hash of file
-> save file if hash doesn't exist
-> return UUID
### Download
take file UUID
-> search for UUID in table and get hash
-> return file if hash exists
## TODO ## TODO
- [] Handle file upload - [X] Handle link GET
- [] Handle file download - [X] Handle link POST
- [] Testing against CSAM hash list - [] Handle file POST
- [] Logging IPs (up and down) of detected CSAM uploads - [] Handle file GET
- [] Web Frontend

View File

@ -1,8 +1,13 @@
import envoy import envoy
import gleam/int import gleam/int
import gleam/pgo
import gleam/result.{unwrap} import gleam/result.{unwrap}
import wisp import wisp
pub type Context {
Context(db: pgo.Connection, config: Config)
}
pub type Config { pub type Config {
Config( Config(
port: Int, port: Int,

View File

@ -1,12 +1,12 @@
import app/config.{type Config} import app/config.{type Context}
import app/web.{type Context} import app/web
import app/web/file import app/web/file
import app/web/link import app/web/link
import app/web/root import app/web/root
import wisp.{type Request, type Response} import wisp.{type Request, type Response}
pub fn handle_request(req: Request, config: Config) -> Response { pub fn handle_request(req: Request, ctx: Context) -> Response {
use req: Request, ctx: Context <- web.middleware(req, config) use req: Request <- web.middleware(req)
case wisp.path_segments(req) { case wisp.path_segments(req) {
["f", ..] -> file.handle_request(req, ctx) ["f", ..] -> file.handle_request(req, ctx)

View File

@ -1,5 +1,3 @@
import app/config.{type Config}
import gleam/pgo
import wisp.{type Request, type Response} import wisp.{type Request, type Response}
pub const html_head = " pub const html_head = "
@ -18,27 +16,13 @@ pub const html_tail = "
</html> </html>
" "
pub type Context {
Context(db: pgo.Connection, config: Config)
}
pub fn middleware( pub fn middleware(
req: Request, req: Request,
config: Config, handle_request: fn(Request) -> Response,
handle_request: fn(Request, Context) -> Response,
) -> Response { ) -> Response {
let req = wisp.method_override(req) let req = wisp.method_override(req)
use <- wisp.log_request(req) use <- wisp.log_request(req)
use <- wisp.rescue_crashes use <- wisp.rescue_crashes
use req <- wisp.handle_head(req) use req <- wisp.handle_head(req)
let db = handle_request(req)
pgo.connect(
pgo.Config(
..pgo.default_config(),
host: config.postgres_host,
database: config.postgres_db,
pool_size: config.postgres_pool_size,
),
)
handle_request(req, Context(db, config))
} }

View File

@ -1,43 +1,49 @@
import app/web.{type Context} import app/config.{type Context}
import gleam/dynamic
import gleam/http.{Get, Post} import gleam/http.{Get, Post}
import gleam/list import gleam/list
import gleam/pgo
import gleam/result
import gleam/string import gleam/string
import wisp.{type Request, type Response} import wisp.{type Request, type Response}
const schema = " const schema = "
CREATE TABLE IF NOT EXISTS `FileReference` ( CREATE TABLE IF NOT EXISTS \"File\" (
nanoid TEXT <<key>> nanoid VARCHAR(21) PRIMARY KEY,
PathID INT md5_hash BYTEA
);
CREATE TABLE IF NOT EXISTS `FilePath` (
PathID INT <<key>>
md5_hash BLOB
banned BOOLEAN
); );
" "
type File { pub type File {
Reference(md5_hash: BitArray, nanoid: String) File(md5_hash: BitArray, nanoid: String)
Path(md5_hash: String, banned: Bool)
} }
fn hash_to_path(hash: String) -> List(String) { pub fn prepare_database(ctx: Context) -> Result(Nil, pgo.QueryError) {
pgo.execute(schema, ctx.db, [], dynamic.dynamic)
|> result.map(fn(_) { Nil })
}
pub fn hash_to_path(hash: String) -> List(String) {
list.new() list.new()
|> list.append([hash |> string.slice(0, 2)]) |> list.append([hash |> string.slice(0, 2)])
|> list.append([hash |> string.slice(2, 2)]) |> list.append([hash |> string.slice(2, 2)])
|> list.append([hash |> string.slice(4, { hash |> string.length } - 4)]) |> list.append([hash |> string.slice(4, { hash |> string.length } - 4)])
} }
pub fn handle_request(req: Request, ctx: Context) -> Response { fn handle_nano_id(req: Request, nano_id: String, ct: Context) -> Response {
todo
}
fn serve_file(req, uuid: String) -> Response {
use <- wisp.require_method(req, Get) use <- wisp.require_method(req, Get)
todo as "implement serve_file" todo as "implement serve_file"
} }
fn receive_file(req: Request) -> Response { fn handle_root(req: Request, ctx: Context) -> Response {
use <- wisp.require_method(req, Post) use <- wisp.require_method(req, Post)
todo as "implement receive_file" todo as "implement receive_file"
} }
pub fn handle_request(req: Request, ctx: Context) -> Response {
case wisp.path_segments(req) {
["f"] -> handle_root(req, ctx)
["f", nano_id] -> handle_nano_id(req, nano_id, ctx)
_ -> wisp.not_found()
}
}

View File

@ -1,4 +1,4 @@
import app/web.{type Context} import app/config.{type Context}
import gleam/dynamic import gleam/dynamic
import gleam/http.{Get, Post} import gleam/http.{Get, Post}
import gleam/int import gleam/int

View File

@ -1,14 +1,33 @@
import app/config import app/config.{type Config, type Context, Context}
import app/router import app/router
import gleam/erlang/process import gleam/erlang/process
import gleam/pgo
import mist import mist
import wisp import wisp
pub fn main() { fn with_context(config: Config, next: fn(Context) -> Nil) -> Nil {
wisp.configure_logger() let db =
let config = config.load_config_from_env() pgo.connect(
pgo.Config(
..pgo.default_config(),
host: config.postgres_host,
database: config.postgres_db,
pool_size: config.postgres_pool_size,
),
)
let ctx = Context(db: db, config: config)
let entrypoint = router.handle_request(_, config) next(ctx)
ctx.db |> pgo.disconnect
}
pub fn main() {
let config = config.load_config_from_env()
use ctx: Context <- with_context(config)
wisp.configure_logger()
let entrypoint = router.handle_request(_, ctx)
let assert Ok(_) = let assert Ok(_) =
wisp.mist_handler(entrypoint, config.secret_key_base) wisp.mist_handler(entrypoint, config.secret_key_base)
|> mist.new |> mist.new

View File

@ -0,0 +1,68 @@
import app/config.{type Context, Config, Context}
import app/web/file
import gleam/dynamic
import gleam/option.{None, Some}
import gleam/pgo
import gleam/string
import gleeunit/should
const test_config = Config(
port: 8080,
max_bytes: 52_428_800,
url_root: "http://localhost",
secret_key_base: "cDv6ikrODZKjKbwSBBwspXInc61MYpCzQ8My9gW2QpQg3Y3lT7IJ5RJlzRN5HZK0",
postgres_db: "dumptruck",
postgres_user: "dumptruck",
postgres_password: "dumptruck",
postgres_host: "localhost",
postgres_pool_size: 15,
postgres_port: 5432,
)
fn with_context(testcase: fn(Context) -> t) -> t {
let ctx =
Context(
db: pgo.Config(
..pgo.default_config(),
database: test_config.postgres_db,
password: test_config.postgres_password |> Some,
user: test_config.postgres_user,
host: test_config.postgres_host,
pool_size: test_config.postgres_pool_size,
port: 5432,
)
|> pgo.connect,
config: test_config,
)
case file.prepare_database(ctx) {
Ok(_) -> {
let ret = testcase(ctx)
ctx.db |> pgo.disconnect
ret
}
Error(e) -> {
e |> string.inspect |> panic
}
}
}
pub fn null_test() {
use ctx <- with_context()
pgo.execute(
"select $1",
ctx.db,
[pgo.null()],
dynamic.element(0, dynamic.optional(dynamic.int)),
)
|> should.equal(Ok(pgo.Returned(count: 1, rows: [None])))
}
pub fn hash_to_path_test() {
"f7b478961451483b8c251c788750d5ef"
|> file.hash_to_path
|> should.equal(["f7", "b4", "78961451483b8c251c788750d5ef"])
}

View File

@ -1,8 +1,6 @@
import app/config.{Config} import app/config.{type Context, Config, Context}
import app/web.{type Context, Context}
import app/web/link.{Link} import app/web/link.{Link}
import gleam/dynamic import gleam/dynamic
import gleam/io
import gleam/option.{None, Some} import gleam/option.{None, Some}
import gleam/pgo import gleam/pgo
import gleam/result import gleam/result