diff --git a/backend/Cargo.lock b/Cargo.lock similarity index 95% rename from backend/Cargo.lock rename to Cargo.lock index 5a8455a..9838535 100644 --- a/backend/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "anyhow" version = "1.0.42" @@ -245,6 +254,7 @@ dependencies = [ "axum", "chrono", "diesel", + "diesel-tracing", "diesel_migrations", "dotenv", "fern", @@ -261,6 +271,10 @@ dependencies = [ "tokio 1.9.0", "tokio-diesel", "tower", + "tower-http", + "tracing", + "tracing-log", + "tracing-subscriber", "url", "uuid", ] @@ -528,12 +542,25 @@ dependencies = [ "byteorder", "chrono", "diesel_derives", + "ipnetwork", + "libc", "pq-sys", "r2d2", "serde_json", "uuid", ] +[[package]] +name = "diesel-tracing" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c039d591e23293c9d9682139758ea499e2a56b27c2ef38704c8f1dc5e2044ad" +dependencies = [ + "diesel", + "ipnetwork", + "tracing", +] + [[package]] name = "diesel_derives" version = "1.4.1" @@ -1014,6 +1041,15 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +[[package]] +name = "ipnetwork" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4088d739b183546b239688ddbc79891831df421773df95e236daf7867866d355" +dependencies = [ + "serde", +] + [[package]] name = "itoa" version = "0.4.7" @@ -1079,6 +1115,15 @@ dependencies = [ "value-bag", ] +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + [[package]] name = "matches" version = "0.1.8" @@ -1586,6 +1631,15 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.6.25" @@ -1826,6 +1880,15 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "sharded-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1914,6 +1977,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + [[package]] name = "time" version = "0.1.44" @@ -2096,6 +2168,7 @@ dependencies = [ "pin-project", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -2143,6 +2216,49 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cbe87a2fa7e35900ce5de20220a582a9483a7063811defce79d7cbd59d4cfe" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + [[package]] name = "try-lock" version = "0.2.3" diff --git a/backend/Cargo.toml b/Cargo.toml similarity index 80% rename from backend/Cargo.toml rename to Cargo.toml index 712764a..03bb407 100644 --- a/backend/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ async-std = {version = "1.9.0", features = ["attributes"]} axum = {version = "0.1.1", features = ["headers"]} chrono = {version = "0.4.0", features = ["serde"]} diesel = {version = "1.4.7", features = ["postgres", "chrono", "serde_json", "r2d2", "uuidv07"]} +diesel-tracing = {version = "0.1.5", features = ["postgres"]} diesel_migrations = {version = "1.4.0"} dotenv = "0.15.0" fern = "0.6.0" @@ -29,5 +30,9 @@ serde_json = "1.0.66" tokio = {version = "1.9.0", features = ["full"]} tokio-diesel = {git = "https://github.com/mehcode/tokio-diesel", version = "0.3.0"} tower = {version = "0.4.6", features = ["full"]} +tower-http = {version = "0.1.1", features = ["trace"]} +tracing = {version = "0.1.26", features = ["log-always"]} +tracing-log = {version = "0.1.2", features = ["log-tracer"]} +tracing-subscriber = {version = "0.2.20", features = ["fmt"]} url = "2.2.2" uuid = {version = "0.8.2", features = ["serde", "v4"]} diff --git a/backend/.env.example b/backend/.env.example deleted file mode 100644 index 8c48d49..0000000 --- a/backend/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -DATABASE_URL=postgres://postgres@localhost/todo_dev -LOG_LEVEL=DEBUG -PORT=8080 \ No newline at end of file diff --git a/backend/src/endpoints.rs b/backend/src/endpoints.rs deleted file mode 100644 index 284e2cf..0000000 --- a/backend/src/endpoints.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::diesel::PgConnection; -use crate::logging::LogService; -use async_redis_session::RedisSessionStore; -use axum::{prelude::*, routing::BoxRoute, AddExtensionLayer}; -use diesel::r2d2::{ConnectionManager, Pool}; -use std::env; - -pub mod block; -pub mod discord; -pub mod user; - -// this should never get called, because the reverse -// proxy on caddy should only direct calls from /api -async fn root() -> &'static str { - "Hi" -} - -pub fn get_routes(pool: Pool>) -> BoxRoute { - let redis_url = env::var("REDIS_URL").unwrap_or(String::from("redis://localhost")); - - let client = redis::Client::open(redis_url.as_str()).expect("Could not create redis client."); - route("/", get(root)) - .nest("/discord", discord::get_routes()) - .layer(tower::layer::layer_fn(|service| LogService { service })) - .layer(AddExtensionLayer::new(RedisSessionStore::from_client( - client, - ))) - .layer(AddExtensionLayer::new(pool)) - .boxed() -} diff --git a/backend/src/logging.rs b/backend/src/logging.rs deleted file mode 100644 index 6e0f587..0000000 --- a/backend/src/logging.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::fmt; -use std::task::{Context, Poll}; -use tower::Service; - -pub struct LogService { - pub service: S, -} - -impl Service for LogService -where - S: Service, - Request: fmt::Debug, -{ - type Response = S::Response; - type Error = S::Error; - type Future = S::Future; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.service.poll_ready(cx) - } - - fn call(&mut self, request: Request) -> Self::Future { - log::debug!("request = {:?}", request); - self.service.call(request) - } -} diff --git a/backend/src/main.rs b/backend/src/main.rs deleted file mode 100644 index b85d1fa..0000000 --- a/backend/src/main.rs +++ /dev/null @@ -1,66 +0,0 @@ -use axum::prelude::*; -use std::net::SocketAddr; - -use diesel::{ - prelude::*, - r2d2::{ConnectionManager, Pool}, -}; -use dotenv::dotenv; -use std::env; -use std::str::FromStr; - -#[macro_use] -extern crate diesel; -extern crate redis; - -mod endpoints; -pub mod helpers; -pub mod logging; -pub mod migration; -pub mod models; -pub mod schema; -pub mod tests; - -#[tokio::main] -async fn main() { - dotenv().ok(); - let _ = setup_logger(); - - let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set"); - - migration::run_migrations(&db_url); - let manager = ConnectionManager::::new(&db_url); - let pool = Pool::builder() - .build(manager) - .expect("Could not build connection pool"); - - let root = route("/api", endpoints::get_routes(pool)); - - let port = env::var("PORT").unwrap_or(String::from("8000")); - let addr = SocketAddr::from(([127, 0, 0, 1], port.parse().unwrap_or(8000))); - - log::info!("started listening on {:?}", addr); - hyper::Server::bind(&addr) - .serve(root.into_make_service()) - .await - .unwrap(); -} - -fn setup_logger() -> Result<(), fern::InitError> { - let log_level = env::var("LOG_LEVEL").unwrap_or(String::from("INFO")); - fern::Dispatch::new() - .format(|out, message, record| { - out.finish(format_args!( - "{} <{}> [{}] {}", - chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), - record.file().unwrap_or(record.target()), - record.level(), - message - )) - }) - .level(log::LevelFilter::from_str(log_level.as_str()).unwrap_or(log::LevelFilter::Info)) - .chain(std::io::stdout()) - .chain(fern::log_file("latest.log")?) - .apply()?; - Ok(()) -} diff --git a/backend/default.profraw b/default.profraw similarity index 100% rename from backend/default.profraw rename to default.profraw diff --git a/backend/diesel.toml b/diesel.toml similarity index 100% rename from backend/diesel.toml rename to diesel.toml diff --git a/backend/env.sh b/env.sh similarity index 100% rename from backend/env.sh rename to env.sh diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index f220e37..0000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.DS_Store -/node_modules/ -/src/node_modules/@sapper/ -yarn-error.log -/__sapper__/ diff --git a/frontend/.prettierrc.yaml b/frontend/.prettierrc.yaml deleted file mode 100644 index 3a9fff3..0000000 --- a/frontend/.prettierrc.yaml +++ /dev/null @@ -1,16 +0,0 @@ -arrowParens: 'always' -bracketSpacing: true -endOfLine: 'lf' -htmlWhitespaceSensitivity: 'css' -insertPragma: false -jsxBracketSameLine: true -jsxSingleQuote: true -printWidth: 120 -proseWrap: 'preserve' -quoteProps: 'consistent' -requirePragma: false -semi: true -singleQuote: true -tabWidth: 2 -trailingComma: 'none' -useTabs: false diff --git a/frontend/.storybook/main.js b/frontend/.storybook/main.js deleted file mode 100644 index d3aaa5e..0000000 --- a/frontend/.storybook/main.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - "stories": [ - "../src/**/*.stories.mdx", - "../src/**/*.stories.@(js|jsx|ts|tsx|svelte)" - ], - "addons": [ - "@storybook/addon-links", - "@storybook/addon-essentials", - "@storybook/addon-svelte-csf" - ] -} \ No newline at end of file diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js deleted file mode 100644 index 48afd56..0000000 --- a/frontend/.storybook/preview.js +++ /dev/null @@ -1,9 +0,0 @@ -export const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, -} \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index 9550922..0000000 --- a/frontend/README.md +++ /dev/null @@ -1 +0,0 @@ -# Todo Frontend \ No newline at end of file diff --git a/frontend/src_old/ambient.d.ts b/frontend/src_old/ambient.d.ts deleted file mode 100644 index a26164d..0000000 --- a/frontend/src_old/ambient.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * These declarations tell TypeScript that we allow import of images, e.g. - * ``` - - - - ``` - */ -declare module "*.gif" { - const value: string; - export default value; -} - -declare module "*.jpg" { - const value: string; - export default value; -} - -declare module "*.jpeg" { - const value: string; - export default value; -} - -declare module "*.png" { - const value: string; - export default value; -} - -declare module "*.svg" { - const value: string; - export default value; -} - -declare module "*.webp" { - const value: string; - export default value; -} diff --git a/frontend/src_old/client.js b/frontend/src_old/client.js deleted file mode 100644 index cec9172..0000000 --- a/frontend/src_old/client.js +++ /dev/null @@ -1,5 +0,0 @@ -import * as sapper from '@sapper/app'; - -sapper.start({ - target: document.querySelector('#sapper') -}); \ No newline at end of file diff --git a/frontend/src_old/components/Button.svelte b/frontend/src_old/components/Button.svelte deleted file mode 100644 index ee2d74d..0000000 --- a/frontend/src_old/components/Button.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/frontend/src_old/components/Header.svelte b/frontend/src_old/components/Header.svelte deleted file mode 100644 index eeab2c2..0000000 --- a/frontend/src_old/components/Header.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -
-
-
- -

Todo

-
-
- {#if user} - user profile picture -
-
-
diff --git a/frontend/src_old/components/Image.svelte b/frontend/src_old/components/Image.svelte deleted file mode 100644 index 2aaf8c3..0000000 --- a/frontend/src_old/components/Image.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/frontend/src_old/components/Nav.svelte b/frontend/src_old/components/Nav.svelte deleted file mode 100644 index 49a94ed..0000000 --- a/frontend/src_old/components/Nav.svelte +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/frontend/src_old/components/button.css b/frontend/src_old/components/button.css deleted file mode 100644 index dc91dc7..0000000 --- a/frontend/src_old/components/button.css +++ /dev/null @@ -1,30 +0,0 @@ -.storybook-button { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 700; - border: 0; - border-radius: 3em; - cursor: pointer; - display: inline-block; - line-height: 1; -} -.storybook-button--primary { - color: white; - background-color: #1ea7fd; -} -.storybook-button--secondary { - color: #333; - background-color: transparent; - box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; -} -.storybook-button--small { - font-size: 12px; - padding: 10px 16px; -} -.storybook-button--medium { - font-size: 14px; - padding: 11px 20px; -} -.storybook-button--large { - font-size: 16px; - padding: 12px 24px; -} diff --git a/frontend/src_old/components/header.css b/frontend/src_old/components/header.css deleted file mode 100644 index acadc9e..0000000 --- a/frontend/src_old/components/header.css +++ /dev/null @@ -1,26 +0,0 @@ -.wrapper { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - padding: 15px 20px; - display: flex; - align-items: center; - justify-content: space-between; -} - -svg { - display: inline-block; - vertical-align: top; -} - -h1 { - font-weight: 900; - font-size: 20px; - line-height: 1; - margin: 6px 0 6px 10px; - display: inline-block; - vertical-align: top; -} - -button + button { - margin-left: 10px; -} diff --git a/frontend/src_old/components/image.css b/frontend/src_old/components/image.css deleted file mode 100644 index 731f906..0000000 --- a/frontend/src_old/components/image.css +++ /dev/null @@ -1,23 +0,0 @@ -.storybook-icon { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 700; - border: 0; - border-radius: 3em; - cursor: pointer; - display: inline-block; - line-height: 1; - } - - .storybook-icon--small { - height: 32px; - width: auto; - } - .storybook-icon--medium { - height: 64px; - width: auto; - } - .storybook-icon--large { - height: 128px; - width: auto; - } - \ No newline at end of file diff --git a/frontend/src_old/deprecated/[slug].json.js b/frontend/src_old/deprecated/[slug].json.js deleted file mode 100644 index 176890d..0000000 --- a/frontend/src_old/deprecated/[slug].json.js +++ /dev/null @@ -1,28 +0,0 @@ -import posts from './_posts.js'; - -const lookup = new Map(); -posts.forEach(post => { - lookup.set(post.slug, JSON.stringify(post)); -}); - -export function get(req, res, next) { - // the `slug` parameter is available because - // this file is called [slug].json.js - const { slug } = req.params; - - if (lookup.has(slug)) { - res.writeHead(200, { - 'Content-Type': 'application/json' - }); - - res.end(lookup.get(slug)); - } else { - res.writeHead(404, { - 'Content-Type': 'application/json' - }); - - res.end(JSON.stringify({ - message: `Not found` - })); - } -} diff --git a/frontend/src_old/deprecated/[slug].svelte b/frontend/src_old/deprecated/[slug].svelte deleted file mode 100644 index 84e8084..0000000 --- a/frontend/src_old/deprecated/[slug].svelte +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - {post.title} - - -

{post.title}

- -
- {@html post.html} -
diff --git a/frontend/src_old/deprecated/_posts.js b/frontend/src_old/deprecated/_posts.js deleted file mode 100644 index 7791a21..0000000 --- a/frontend/src_old/deprecated/_posts.js +++ /dev/null @@ -1,92 +0,0 @@ -// Ordinarily, you'd generate this data from markdown files in your -// repo, or fetch them from a database of some kind. But in order to -// avoid unnecessary dependencies in the starter template, and in the -// service of obviousness, we're just going to leave it here. - -// This file is called `_posts.js` rather than `posts.js`, because -// we don't want to create an `/blog/posts` route — the leading -// underscore tells Sapper not to do that. - -const posts = [ - { - title: 'What is Sapper?', - slug: 'what-is-sapper', - html: ` -

First, you have to know what Svelte is. Svelte is a UI framework with a bold new idea: rather than providing a library that you write code with (like React or Vue, for example), it's a compiler that turns your components into highly optimized vanilla JavaScript. If you haven't already read the introductory blog post, you should!

- -

Sapper is a Next.js-style framework (more on that here) built around Svelte. It makes it embarrassingly easy to create extremely high performance web apps. Out of the box, you get:

- -
    -
  • Code-splitting, dynamic imports and hot module replacement, powered by webpack
  • -
  • Server-side rendering (SSR) with client-side hydration
  • -
  • Service worker for offline support, and all the PWA bells and whistles
  • -
  • The nicest development experience you've ever had, or your money back
  • -
- -

It's implemented as Express middleware. Everything is set up and waiting for you to get started, but you keep complete control over the server, service worker, webpack config and everything else, so it's as flexible as you need it to be.

- ` - }, - - { - title: 'How to use Sapper', - slug: 'how-to-use-sapper', - html: ` -

Step one

-

Create a new project, using degit:

- -
npx degit "sveltejs/sapper-template#rollup" my-app
-			cd my-app
-			npm install # or yarn!
-			npm run dev
-			
- -

Step two

-

Go to localhost:3000. Open my-app in your editor. Edit the files in the src/routes directory or add new ones.

- -

Step three

-

...

- -

Step four

-

Resist overdone joke formats.

- ` - }, - - { - title: 'Why the name?', - slug: 'why-the-name', - html: ` -

In war, the soldiers who build bridges, repair roads, clear minefields and conduct demolitions — all under combat conditions — are known as sappers.

- -

For web developers, the stakes are generally lower than those for combat engineers. But we face our own hostile environment: underpowered devices, poor network connections, and the complexity inherent in front-end engineering. Sapper, which is short for Svelte app maker, is your courageous and dutiful ally.

- ` - }, - - { - title: 'How is Sapper different from Next.js?', - slug: 'how-is-sapper-different-from-next', - html: ` -

Next.js is a React framework from Vercel, and is the inspiration for Sapper. There are a few notable differences, however:

- -
    -
  • It's powered by Svelte instead of React, so it's faster and your apps are smaller
  • -
  • Instead of route masking, we encode route parameters in filenames. For example, the page you're looking at right now is src/routes/blog/[slug].svelte
  • -
  • As well as pages (Svelte components, which render on server or client), you can create server routes in your routes directory. These are just .js files that export functions corresponding to HTTP methods, and receive Express request and response objects as arguments. This makes it very easy to, for example, add a JSON API such as the one powering this very page
  • -
  • Links are just <a> elements, rather than framework-specific <Link> components. That means, for example, that this link right here, despite being inside a blob of HTML, works with the router as you'd expect.
  • -
- ` - }, - - { - title: 'How can I get involved?', - slug: 'how-can-i-get-involved', - html: ` -

We're so glad you asked! Come on over to the Svelte and Sapper repos, and join us in the Discord chatroom. Everyone is welcome, especially you!

- ` - } -]; - -posts.forEach(post => { - post.html = post.html.replace(/^\t{3}/gm, ''); -}); - -export default posts; diff --git a/frontend/src_old/deprecated/index.json.js b/frontend/src_old/deprecated/index.json.js deleted file mode 100644 index bfd9389..0000000 --- a/frontend/src_old/deprecated/index.json.js +++ /dev/null @@ -1,16 +0,0 @@ -import posts from './_posts.js'; - -const contents = JSON.stringify(posts.map(post => { - return { - title: post.title, - slug: post.slug - }; -})); - -export function get(req, res) { - res.writeHead(200, { - 'Content-Type': 'application/json' - }); - - res.end(contents); -} \ No newline at end of file diff --git a/frontend/src_old/deprecated/index.svelte b/frontend/src_old/deprecated/index.svelte deleted file mode 100644 index 2b7d64c..0000000 --- a/frontend/src_old/deprecated/index.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - Blog - - -

Recent posts

- - diff --git a/frontend/src_old/routes/_error.svelte b/frontend/src_old/routes/_error.svelte deleted file mode 100644 index 320e587..0000000 --- a/frontend/src_old/routes/_error.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - {status} - - -

{status}

- -

{error.message}

- -{#if dev && error.stack} -
{error.stack}
-{/if} diff --git a/frontend/src_old/routes/_layout.svelte b/frontend/src_old/routes/_layout.svelte deleted file mode 100644 index cdddc89..0000000 --- a/frontend/src_old/routes/_layout.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - - -
- -
- -
\ No newline at end of file diff --git a/frontend/src_old/routes/about.svelte b/frontend/src_old/routes/about.svelte deleted file mode 100644 index e1734b3..0000000 --- a/frontend/src_old/routes/about.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - About - - -

About this site

- -

This is the 'about' page. There's not much here.

\ No newline at end of file diff --git a/frontend/src_old/routes/index.svelte b/frontend/src_old/routes/index.svelte deleted file mode 100644 index 3ec61d3..0000000 --- a/frontend/src_old/routes/index.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - - Todo - - - diff --git a/frontend/src_old/routes/settings.svelte b/frontend/src_old/routes/settings.svelte deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src_old/server.js b/frontend/src_old/server.js deleted file mode 100644 index c77f593..0000000 --- a/frontend/src_old/server.js +++ /dev/null @@ -1,17 +0,0 @@ -import sirv from 'sirv'; -import polka from 'polka'; -import compression from 'compression'; -import * as sapper from '@sapper/server'; - -const { PORT, NODE_ENV } = process.env; -const dev = NODE_ENV === 'development'; - -polka() // You can also use Express - .use( - compression({ threshold: 0 }), - sirv('static', { dev }), - sapper.middleware() - ) - .listen(PORT, err => { - if (err) console.log('error', err); - }); diff --git a/frontend/src_old/service-worker.js b/frontend/src_old/service-worker.js deleted file mode 100644 index 02ab1d2..0000000 --- a/frontend/src_old/service-worker.js +++ /dev/null @@ -1,86 +0,0 @@ -import { timestamp, files, shell } from '@sapper/service-worker'; - -const ASSETS = `cache${timestamp}`; - -// `shell` is an array of all the files generated by the bundler, -// `files` is an array of everything in the `static` directory -const to_cache = shell.concat(files); -const staticAssets = new Set(to_cache); - -self.addEventListener('install', event => { - event.waitUntil( - caches - .open(ASSETS) - .then(cache => cache.addAll(to_cache)) - .then(() => { - self.skipWaiting(); - }) - ); -}); - -self.addEventListener('activate', event => { - event.waitUntil( - caches.keys().then(async keys => { - // delete old caches - for (const key of keys) { - if (key !== ASSETS) await caches.delete(key); - } - - self.clients.claim(); - }) - ); -}); - - -/** - * Fetch the asset from the network and store it in the cache. - * Fall back to the cache if the user is offline. - */ -async function fetchAndCache(request) { - const cache = await caches.open(`offline${timestamp}`) - - try { - const response = await fetch(request); - cache.put(request, response.clone()); - return response; - } catch (err) { - const response = await cache.match(request); - if (response) return response; - - throw err; - } -} - -self.addEventListener('fetch', event => { - if (event.request.method !== 'GET' || event.request.headers.has('range')) return; - - const url = new URL(event.request.url); - - // don't try to handle e.g. data: URIs - const isHttp = url.protocol.startsWith('http'); - const isDevServerRequest = url.hostname === self.location.hostname && url.port !== self.location.port; - const isStaticAsset = url.host === self.location.host && staticAssets.has(url.pathname); - const skipBecauseUncached = event.request.cache === 'only-if-cached' && !isStaticAsset; - - if (isHttp && !isDevServerRequest && !skipBecauseUncached) { - event.respondWith( - (async () => { - // always serve static files and bundler-generated assets from cache. - // if your application has other URLs with data that will never change, - // set this variable to true for them and they will only be fetched once. - const cachedAsset = isStaticAsset && await caches.match(event.request); - - // for pages, you might want to serve a shell `service-worker-index.html` file, - // which Sapper has generated for you. It's not right for every - // app, but if it's right for yours then uncomment this section - /* - if (!cachedAsset && url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) { - return caches.match('/service-worker-index.html'); - } - */ - - return cachedAsset || fetchAndCache(event.request); - })() - ); - } -}); diff --git a/frontend/src_old/template.html b/frontend/src_old/template.html deleted file mode 100644 index 92eb4af..0000000 --- a/frontend/src_old/template.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - %sapper.base% - - - - - - - - %sapper.scripts% - - - %sapper.styles% - - - %sapper.head% - - - -
%sapper.html%
- - diff --git a/frontend/src_old/utils/events.js b/frontend/src_old/utils/events.js deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src_old/utils/requests.js b/frontend/src_old/utils/requests.js deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src_old/utils/settings.js b/frontend/src_old/utils/settings.js deleted file mode 100644 index 83d3dbf..0000000 --- a/frontend/src_old/utils/settings.js +++ /dev/null @@ -1 +0,0 @@ -export const API_URL = 'https://dev.j4.pm/api'; diff --git a/frontend/src_old/utils/stores.js b/frontend/src_old/utils/stores.js deleted file mode 100644 index 872d1c4..0000000 --- a/frontend/src_old/utils/stores.js +++ /dev/null @@ -1,31 +0,0 @@ -import { writable } from 'svelte/store'; -import axios from 'axios'; -import { API_URL } from './settings'; - -export const discordUser = writable({}); - -export const hasSession = () => { - return getCookie('SESSION') != null; -}; - -function getCookie(name) { - if (!window.document) { - return null; - } - var dc = window.document.cookie; - var prefix = name + '='; - var begin = dc.indexOf('; ' + prefix); - if (begin == -1) { - begin = dc.indexOf(prefix); - if (begin != 0) return null; - } else { - begin += 2; - var end = window.document.cookie.indexOf(';', begin); - if (end == -1) { - end = dc.length; - } - } - // because unescape has been deprecated, replaced with decodeURI - //return unescape(dc.substring(begin + prefix.length, end)); - return decodeURI(dc.substring(begin + prefix.length, end)); -} diff --git a/frontend/src_old/utils/user.js b/frontend/src_old/utils/user.js deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/static_old/clipboard.svg b/frontend/static_old/clipboard.svg deleted file mode 100644 index 4b09b6c..0000000 --- a/frontend/static_old/clipboard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/static_old/code-brackets.svg b/frontend/static_old/code-brackets.svg deleted file mode 100644 index 73de947..0000000 --- a/frontend/static_old/code-brackets.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/code-brackets \ No newline at end of file diff --git a/frontend/static_old/colors.svg b/frontend/static_old/colors.svg deleted file mode 100644 index 17d58d5..0000000 --- a/frontend/static_old/colors.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/colors \ No newline at end of file diff --git a/frontend/static_old/comments.svg b/frontend/static_old/comments.svg deleted file mode 100644 index 6493a13..0000000 --- a/frontend/static_old/comments.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/comments \ No newline at end of file diff --git a/frontend/static_old/direction.svg b/frontend/static_old/direction.svg deleted file mode 100644 index 65676ac..0000000 --- a/frontend/static_old/direction.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/direction \ No newline at end of file diff --git a/frontend/static_old/favicon.png b/frontend/static_old/favicon.png deleted file mode 100644 index 7e6f5eb..0000000 Binary files a/frontend/static_old/favicon.png and /dev/null differ diff --git a/frontend/static_old/flow.svg b/frontend/static_old/flow.svg deleted file mode 100644 index 8ac27db..0000000 --- a/frontend/static_old/flow.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/flow \ No newline at end of file diff --git a/frontend/static_old/global.css b/frontend/static_old/global.css deleted file mode 100644 index 3566e73..0000000 --- a/frontend/static_old/global.css +++ /dev/null @@ -1,36 +0,0 @@ -body { - margin: 0; - font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; - font-size: 14px; - line-height: 1.5; - color: #333; -} - -h1, h2, h3, h4, h5, h6 { - margin: 0 0 0.5em 0; - font-weight: 400; - line-height: 1.2; -} - -h1 { - font-size: 2em; -} - -a { - color: inherit; -} - -code { - font-family: menlo, inconsolata, monospace; - font-size: calc(1em - 2px); - color: #555; - background-color: #f0f0f0; - padding: 0.2em 0.4em; - border-radius: 2px; -} - -@media (min-width: 400px) { - body { - font-size: 16px; - } -} \ No newline at end of file diff --git a/frontend/static_old/logo-192.png b/frontend/static_old/logo-192.png deleted file mode 100644 index 96fac03..0000000 Binary files a/frontend/static_old/logo-192.png and /dev/null differ diff --git a/frontend/static_old/logo-512.png b/frontend/static_old/logo-512.png deleted file mode 100644 index 9f0e764..0000000 Binary files a/frontend/static_old/logo-512.png and /dev/null differ diff --git a/frontend/static_old/manifest.json b/frontend/static_old/manifest.json deleted file mode 100644 index 8171da6..0000000 --- a/frontend/static_old/manifest.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "background_color": "#ffffff", - "theme_color": "#333333", - "name": "TODO", - "short_name": "TODO", - "display": "minimal-ui", - "start_url": "/", - "icons": [ - { - "src": "logo-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "any maskable" - }, - { - "src": "logo-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "any maskable" - } - ] -} diff --git a/frontend/static_old/plugin.svg b/frontend/static_old/plugin.svg deleted file mode 100644 index 29e5c69..0000000 --- a/frontend/static_old/plugin.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/plugin \ No newline at end of file diff --git a/frontend/static_old/repo.svg b/frontend/static_old/repo.svg deleted file mode 100644 index f386ee9..0000000 --- a/frontend/static_old/repo.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/repo \ No newline at end of file diff --git a/frontend/static_old/stackalt.svg b/frontend/static_old/stackalt.svg deleted file mode 100644 index 9b7ad27..0000000 --- a/frontend/static_old/stackalt.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/stackalt \ No newline at end of file diff --git a/backend/migrations/.gitkeep b/migrations/.gitkeep similarity index 100% rename from backend/migrations/.gitkeep rename to migrations/.gitkeep diff --git a/backend/migrations/00000000000000_diesel_initial_setup/down.sql b/migrations/00000000000000_diesel_initial_setup/down.sql similarity index 100% rename from backend/migrations/00000000000000_diesel_initial_setup/down.sql rename to migrations/00000000000000_diesel_initial_setup/down.sql diff --git a/backend/migrations/00000000000000_diesel_initial_setup/up.sql b/migrations/00000000000000_diesel_initial_setup/up.sql similarity index 100% rename from backend/migrations/00000000000000_diesel_initial_setup/up.sql rename to migrations/00000000000000_diesel_initial_setup/up.sql diff --git a/backend/migrations/2021-08-05-011028_create_user/down.sql b/migrations/2021-08-05-011028_create_user/down.sql similarity index 100% rename from backend/migrations/2021-08-05-011028_create_user/down.sql rename to migrations/2021-08-05-011028_create_user/down.sql diff --git a/backend/migrations/2021-08-05-011028_create_user/up.sql b/migrations/2021-08-05-011028_create_user/up.sql similarity index 100% rename from backend/migrations/2021-08-05-011028_create_user/up.sql rename to migrations/2021-08-05-011028_create_user/up.sql diff --git a/backend/migrations/2021-08-06-173217_create_block/down.sql b/migrations/2021-08-06-173217_create_block/down.sql similarity index 100% rename from backend/migrations/2021-08-06-173217_create_block/down.sql rename to migrations/2021-08-06-173217_create_block/down.sql diff --git a/backend/migrations/2021-08-06-173217_create_block/up.sql b/migrations/2021-08-06-173217_create_block/up.sql similarity index 100% rename from backend/migrations/2021-08-06-173217_create_block/up.sql rename to migrations/2021-08-06-173217_create_block/up.sql diff --git a/src/endpoints.rs b/src/endpoints.rs new file mode 100644 index 0000000..858ac99 --- /dev/null +++ b/src/endpoints.rs @@ -0,0 +1,15 @@ +use axum::{prelude::*, routing::BoxRoute}; + +pub mod block; +pub mod discord; +pub mod user; + +async fn hello() -> &'static str { + "Hi" +} + +pub fn get_routes() -> BoxRoute { + route("/api/hello", any(hello)) + .nest("/api/auth", discord::get_routes()) + .boxed() +} diff --git a/backend/src/endpoints/block.rs b/src/endpoints/block.rs similarity index 100% rename from backend/src/endpoints/block.rs rename to src/endpoints/block.rs diff --git a/backend/src/endpoints/discord.rs b/src/endpoints/discord.rs similarity index 89% rename from backend/src/endpoints/discord.rs rename to src/endpoints/discord.rs index 6d308d2..14b1792 100644 --- a/backend/src/endpoints/discord.rs +++ b/src/endpoints/discord.rs @@ -19,7 +19,7 @@ use std::env; static COOKIE_NAME: &str = "SESSION"; -fn oauth_client() -> BasicClient { +pub fn oauth_client() -> BasicClient { // Environment variables (* = required): // *"CLIENT_ID" "123456789123456789"; // *"CLIENT_SECRET" "rAn60Mch4ra-CTErsSf-r04utHcLienT"; @@ -39,13 +39,15 @@ fn oauth_client() -> BasicClient { let token_url = env::var("TOKEN_URL") .unwrap_or_else(|_| "https://discord.com/api/oauth2/token".to_string()); - BasicClient::new( + let client = BasicClient::new( ClientId::new(client_id), Some(ClientSecret::new(client_secret)), AuthUrl::new(auth_url).unwrap(), Some(TokenUrl::new(token_url).unwrap()), ) - .set_redirect_uri(RedirectUrl::new(redirect_url).unwrap()) + .set_redirect_uri(RedirectUrl::new(redirect_url).unwrap()); + tracing::debug!("client: {:?}", client); + client } // The user data we'll get back from Discord. @@ -81,10 +83,15 @@ async fn discord_auth(Extension(client): Extension) -> impl IntoRes // Valid user session required. If there is none, redirect to the auth page async fn protected(user: DiscordUser) -> impl IntoResponse { - format!( - "Welcome to the protected area :)\nHere's your info:\n{:?}", - user - ) + serde_json::to_string(&user).expect("could not serialize user") +} + +async fn avatar_url(user: DiscordUser) -> impl IntoResponse { + let cdn_url = env::var("CDN_URL").unwrap_or_else(|_| "https://cdn.discordapp.com".to_string()); + match user.avatar { + Some(id) => format!("{}/avatars/{}/{}.webp?size=256", cdn_url, user.id, id), + None => format!("{}/embed/avatars/0.png?size=256", cdn_url), + } } async fn logout( @@ -205,10 +212,11 @@ where pub fn get_routes() -> BoxRoute { route("/", get(index)) - .route("/login/discord", get(discord_auth)) + .route("/discord", get(discord_auth)) .route("/authorized", get(login_authorized)) .route("/protected", get(protected)) + .route("/avatar", get(avatar_url)) .route("/logout", get(logout)) - .layer(AddExtensionLayer::new(oauth_client)) + .layer(AddExtensionLayer::new(oauth_client())) .boxed() } diff --git a/backend/src/endpoints/user.rs b/src/endpoints/user.rs similarity index 100% rename from backend/src/endpoints/user.rs rename to src/endpoints/user.rs diff --git a/backend/src/helpers.rs b/src/helpers.rs similarity index 100% rename from backend/src/helpers.rs rename to src/helpers.rs diff --git a/backend/src/helpers/block.rs b/src/helpers/block.rs similarity index 79% rename from backend/src/helpers/block.rs rename to src/helpers/block.rs index 00dd3b5..55b8d72 100644 --- a/backend/src/helpers/block.rs +++ b/src/helpers/block.rs @@ -1,13 +1,14 @@ -use crate::diesel::{prelude::*, PgConnection, QueryDsl}; +use crate::diesel::{prelude::*, QueryDsl}; use crate::models::*; use crate::schema::*; use diesel::r2d2::{ConnectionManager, Pool}; +use diesel_tracing::pg::InstrumentedPgConnection; use std::error::Error; use tokio_diesel::*; use uuid::Uuid; pub async fn create_block( - pool: &Pool>, + pool: &Pool>, block: InsertableBlock, ) -> Result> { let inserted: Block = diesel::insert_into(blocks::table) @@ -18,7 +19,7 @@ pub async fn create_block( } pub async fn update_block( - pool: &Pool>, + pool: &Pool>, block: Block, ) -> Result> { use crate::schema::blocks::dsl::*; @@ -35,7 +36,7 @@ pub async fn update_block( } pub async fn find_block_by_id( - pool: &Pool>, + pool: &Pool>, block_id: Uuid, ) -> Result> { use crate::schema::blocks::dsl::*; @@ -45,7 +46,7 @@ pub async fn find_block_by_id( } pub async fn delete_block_by_id( - pool: &Pool>, + pool: &Pool>, block_id: Uuid, ) -> Result> { use crate::schema::blocks::dsl::*; diff --git a/backend/src/helpers/user.rs b/src/helpers/user.rs similarity index 77% rename from backend/src/helpers/user.rs rename to src/helpers/user.rs index abea039..3550085 100644 --- a/backend/src/helpers/user.rs +++ b/src/helpers/user.rs @@ -1,13 +1,14 @@ -use crate::diesel::{prelude::*, PgConnection, QueryDsl}; +use crate::diesel::{prelude::*, QueryDsl}; use crate::models::*; use crate::schema::*; use diesel::r2d2::{ConnectionManager, Pool}; +use diesel_tracing::pg::InstrumentedPgConnection; use std::error::Error; use tokio_diesel::*; use uuid::Uuid; pub async fn create_user( - pool: &Pool>, + pool: &Pool>, user: InsertableUser, ) -> Result> { let inserted: User = diesel::insert_into(users::table) @@ -18,7 +19,7 @@ pub async fn create_user( } pub async fn update_user( - pool: &Pool>, + pool: &Pool>, user: User, ) -> Result> { use crate::schema::users::dsl::*; @@ -30,7 +31,7 @@ pub async fn update_user( } pub async fn find_user_by_id( - pool: &Pool>, + pool: &Pool>, user_id: Uuid, ) -> Result> { use crate::schema::users::dsl::*; @@ -40,7 +41,7 @@ pub async fn find_user_by_id( } pub async fn delete_user_by_id( - pool: &Pool>, + pool: &Pool>, user_id: Uuid, ) -> Result> { use crate::schema::users::dsl::*; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..de7f0fc --- /dev/null +++ b/src/main.rs @@ -0,0 +1,88 @@ +use axum::prelude::*; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + +use async_redis_session::RedisSessionStore; +use axum::AddExtensionLayer; +use diesel::r2d2::{ConnectionManager, Pool}; +use diesel_tracing::pg::InstrumentedPgConnection; +use dotenv::dotenv; +use std::env; +use std::str::FromStr; +use tower_http::trace::TraceLayer; + +#[macro_use] +extern crate diesel; +extern crate redis; + +mod endpoints; +pub mod helpers; +pub mod migration; +pub mod models; +pub mod schema; +pub mod tests; + +#[tokio::main] +async fn main() { + dotenv().ok(); + let _ = setup_logger(); + + let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set"); + + migration::run_migrations(&db_url); + let manager = ConnectionManager::::new(&db_url); + let pool = Pool::builder() + .build(manager) + .expect("Could not build connection pool"); + + let redis_url = env::var("REDIS_URL").unwrap_or(String::from("redis://localhost")); + let redis_client = + redis::Client::open(redis_url.as_str()).expect("Could not create redis client."); + + let root = endpoints::get_routes() + .layer(TraceLayer::new_for_http()) + .layer(AddExtensionLayer::new(RedisSessionStore::from_client( + redis_client, + ))) + .layer(AddExtensionLayer::new(pool)); + + let ip = env::var("IP").unwrap_or(String::from("127.0.0.1")); + let port = env::var("PORT").unwrap_or(String::from("8000")); + let addr = SocketAddr::from(( + IpAddr::from_str(ip.as_str()).unwrap_or(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1))), + port.parse().unwrap_or(8000), + )); + + log::info!("started listening on {:?}", addr); + hyper::Server::bind(&addr) + .serve(root.into_make_service()) + .await + .unwrap(); +} + +fn setup_logger() -> () { + let log_level = env::var("LOG_LEVEL").unwrap_or(String::from("INFO")); + let subscriber = tracing_subscriber::FmtSubscriber::builder() + .with_max_level( + tracing::Level::from_str(log_level.as_str()).unwrap_or(tracing::Level::INFO), + ) + .finish(); + + tracing_log::LogTracer::init().expect("could not init log tracer"); + + tracing::subscriber::set_global_default(subscriber).expect("could not set default subscriber"); + // fern::Dispatch::new() + // .format(|out, message, record| { + // out.finish(format_args!( + // "{} <{}> [{}] {}", + // chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), + // record.file().unwrap_or(record.target()), + // record.level(), + // message + // )) + // }) + // .level(log::LevelFilter::from_str(log_level.as_str()).unwrap_or(log::LevelFilter::Info)) + // .chain(std::io::stdout()) + // .chain(fern::log_file("latest.log")?) + // .apply()?; + // Ok(()) +} diff --git a/backend/src/migration.rs b/src/migration.rs similarity index 100% rename from backend/src/migration.rs rename to src/migration.rs diff --git a/backend/src/models.rs b/src/models.rs similarity index 100% rename from backend/src/models.rs rename to src/models.rs diff --git a/backend/src/schema.rs b/src/schema.rs similarity index 100% rename from backend/src/schema.rs rename to src/schema.rs diff --git a/backend/src/tests.rs b/src/tests.rs similarity index 100% rename from backend/src/tests.rs rename to src/tests.rs diff --git a/backend/src/tests/db.rs b/src/tests/db.rs similarity index 89% rename from backend/src/tests/db.rs rename to src/tests/db.rs index ff3f569..5ee6068 100644 --- a/backend/src/tests/db.rs +++ b/src/tests/db.rs @@ -1,20 +1,19 @@ use crate::helpers::*; use crate::models::*; -use diesel::{ - r2d2::{ConnectionManager, Pool}, - PgConnection, -}; +use diesel::r2d2::{ConnectionManager, Pool}; + +use diesel_tracing::pg::InstrumentedPgConnection; use std::{error::Error, vec::Vec}; use uuid::Uuid; -async fn get_pool(url: &String) -> Pool> { - let manager = ConnectionManager::::new(url); +async fn get_pool(url: &String) -> Pool> { + let manager = ConnectionManager::::new(url); Pool::builder() .build(manager) .expect("Could not build connection pool") } -async fn user_tests(pool: &Pool>) { +async fn user_tests(pool: &Pool>) { let user = InsertableUser { discord_id: String::from("test"), }; @@ -66,7 +65,7 @@ async fn user_tests(pool: &Pool>) { } } -async fn block_tests(pool: &Pool>) { +async fn block_tests(pool: &Pool>) { let json = serde_json::from_str("[]"); let block = InsertableBlock { user_id: Uuid::new_v4(),