🏡 index : old_projects/url_shortener.git

author yaqubroli <walchuk2018@icloud.com> 2022-12-22 22:46:14.0 -08:00:00
committer yaqubroli <walchuk2018@icloud.com> 2022-12-22 22:46:14.0 -08:00:00
commit
805ff8cb1d1ba77e9e722fd8d792657bf74afdba [patch]
tree
a48c64e67044870917bbcbd8a07233daa3d812ee
parent
891973f5325e741988bc79daa6b0bf7ffde1cfc0
download
805ff8cb1d1ba77e9e722fd8d792657bf74afdba.tar.gz

Rebuilt templating engine, rebuilt how counts are



Diff

 html/index.html                        |  26 ++++++++------------------
 html/url.html                          |   2 +-
 src/database.rs                        |  48 +++++++++++++++++++++++++++++++++++++++++++-----
 src/endpoints.rs                       |  77 ++++++++++++++++++++++++++++++++---------------------------------------------
 src/templating.rs                      | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 target/.rustc_info.json                |   2 +-
 html/static/7800.svg                   |  89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 html/static/HKGroteskWide-SemiBold.otf |   0 
 html/static/paste.html                 |  20 ++++++++++++++++++++
 html/static/style.css                  | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 html/static/url.html                   |  20 ++++++++++++++++++++
 target/debug/url_shortener             |   0 
 12 files changed, 420 insertions(+), 89 deletions(-)

diff --git a/html/index.html b/html/index.html
index e7fd072..6b6e323 100644
--- a/html/index.html
+++ a/html/index.html
@@ -1,25 +1,15 @@
<!DOCTYPE html>
<html>
<head>
    <title>URL submission form</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="static/style.css" />
<link rel="stylesheet" href="static/style.css">
<title>7800.io</title> 
</head>
<body>
    <h1>URL shortener</h1>
    <form action="/" method="post">
        <input type="hidden" name="content_type" value="Url" />
        <label for="content">URL:</label>
        <input type="text" name="content" id="url" />
        <input type="submit" value="Shorten" />
    </form>
    <hr>
    <h1>Pastebin</h1>
    <form action="/" method="post">
        <input type="hidden" name="content_type" value="Pastebin" />
        <label for="content">Paste:</label>
        <textarea name="content" id="paste" rows="10" cols="50"></textarea>
        <input type="submit" value="Paste" />
    </form>
    <h1>7800.io</h1>
    <main>
        <img src="static/7800.svg" alt="7800.io" id="logo">
        <p>7800.io is the future host of services that provide dynamically generated content.</p>
        <small>In the meantime, use this domain as a <a href="static/url.html">URL shortener</a> or as a <a>pastebin</a>.</small>
    </main>
</body>
</html>
diff --git a/html/url.html b/html/url.html
index 61ed301..8e26b47 100644
--- a/html/url.html
+++ a/html/url.html
@@ -7,6 +7,6 @@
</head>
<body>
    <h1>URL shortened successfully</h1>
    <p>Your url is accessible at {{domain}}/{{shortened}}</p>
    <p>Your url is accessible at {domain}/{shortened}</p>
</body>
</html>
diff --git a/src/database.rs b/src/database.rs
index 7b63c96..50fe4ef 100644
--- a/src/database.rs
+++ a/src/database.rs
@@ -1,6 +1,6 @@
use actix_settings::BasicSettings;
use serde::{Serialize, Deserialize};
use crate::{settings, shortener};
use crate::{settings, shortener, url};

use mysql::prelude::*;
use mysql::*;
@@ -35,6 +35,27 @@
    }
}

#[derive(Debug, Clone)]
pub struct Count {
    pub count: u64,
    pub content_type: ContentType
}

impl From<Count> for u64 {
    fn from(item: Count) -> Self {
        item.count
    }
}

impl From<u64> for Count {
    fn from(item: u64) -> Self {
        Count {
            count: item,
            content_type: ContentType::All
        }
    }
}

#[derive(Debug)]
pub struct RetrievedUrl {
    pub url: String,
@@ -120,20 +141,26 @@
}

// Description: This function takes in a connection and a ContentType, and counts the number of entries with that content_type
pub async fn count_entries (connection: &mut PooledConn, content_type: ContentType) -> u64 {
pub async fn count_entries (connection: &mut PooledConn, content_type: ContentType) -> Count {
    // if content_type is ContentType::All, then we don't need to filter by content_type
    if content_type == ContentType::All {
        let mut result = connection.exec_iter("SELECT COUNT(*) FROM entries", ()).unwrap();
        let row = result.next().unwrap();
        let count = row.unwrap().get::<u64, _>("COUNT(*)").unwrap();
        return count;
        return Count {
            count,
            content_type
        };
    }
    let mut result = connection.exec_iter("SELECT COUNT(*) FROM entries WHERE content_type = :content_type", params! {
        "content_type" => content_type as u8
        "content_type" => content_type.clone() as u8
    }).unwrap();
    let row = result.next().unwrap();
    let count = row.unwrap().get::<u64, _>("COUNT(*)").unwrap();
    count
    Count {
        count,
        content_type
    }
}

// Description: This function takes in a connection and a shortened url and returns a RetrievedEntry struct, where `content` is the content column and `success` is true if the shortened url exists in the database
@@ -141,7 +168,7 @@
    let mut result = connection.exec_iter("SELECT content, content_type FROM entries WHERE shortened = :shortened", params! {
        "shortened" => shortened
    }).unwrap();
    let row = result.next().unwrap();
    let row = result.next().expect("No entry found");
    let content = row.as_ref().unwrap().get::<String, _>("content").unwrap();
    let content_type = ContentType::from(row.as_ref().unwrap().get::<u8, _>("content_type").unwrap());
    RetrievedEntry {
@@ -153,7 +180,7 @@

// Description: This function takes in a connection, a `content` string, and a content_type u8 and returns a SubmittedEntry struct, where `shortened` is the shortened url column and `success` is true if the shortened url does not exist in the database.
pub async fn submit_entry (connection: &mut PooledConn, content: &str, content_type: &ContentType) -> SubmittedEntry {
    let shortened = shortener::base64(count_entries(connection, ContentType::All).await);
    let shortened = shortener::base64(count_entries(connection, ContentType::All).await.count);
    let row = connection.exec_iter("SELECT shortened FROM entries WHERE shortened = :shortened", params! {
        "shortened" => shortened.clone()
    }).unwrap().next();
@@ -164,7 +191,12 @@
        }
    } else {
        connection.exec_drop("INSERT INTO entries (content, shortened, content_type) VALUES (:content, :shortened, :content_type)", params! {
            "content" => content,
            "content" => 
                if content_type == &ContentType::Url {
                    url::format_url(content.to_string())
                } else {
                    content.to_string()
                },
            "shortened" => shortened.clone(),
            "content_type" => u8::from(content_type.clone())
        }).unwrap();
diff --git a/src/endpoints.rs b/src/endpoints.rs
index 452959c..6a807e4 100644
--- a/src/endpoints.rs
+++ a/src/endpoints.rs
@@ -1,9 +1,8 @@
use std::path::PathBuf;
use actix_files::NamedFile;
use actix_web::{web, HttpResponse, Responder};
use actix_web::{web, HttpResponse, Responder, HttpRequest};
use serde::{Deserialize, Serialize};

use crate::{database, shortener, url, templating, full_path};
use crate::{database, templating, full_path};
use crate::database::ContentType;

#[derive(Deserialize, Serialize, Debug)]
@@ -12,27 +11,21 @@
    content_type: ContentType
}

pub async fn static_file(path: web::Path<String>, app_data: web::Data<crate::AppData>) -> impl Responder {
pub async fn static_file(path: web::Path<String>, req: HttpRequest, app_data: web::Data<crate::AppData>) -> impl Responder {
    let path_string = path.into_inner();
    println!("Accessing file {:?}", path_string);
    let body = if app_data.config.application.html.template_static && path_string.ends_with(".html") {
        templating::read_and_apply_templates(
            full_path::get_full_path(&app_data, &path_string, true),
            templating::TemplateSchema {
                content: "".to_string(),
                shortened: "".to_string(),
                domain: app_data.config.application.html.domain.clone(),
                count: if app_data.config.application.html.count {
                    database::count_entries(&mut app_data.database.get_conn().unwrap(), ContentType::All).await.to_string()
                } else {
                    "".to_string()
                }
            }
    if app_data.config.application.html.template_static && path_string.ends_with(".html") {
        HttpResponse::Ok().body(
            templating::read_and_apply_templates(
                full_path::get_full_path(&app_data, &path_string, true),
                app_data.clone(),
                &mut templating::TemplateSchema::create_null_schema()
            ).await
        )
    } else {
        std::fs::read_to_string(full_path::get_full_path(&app_data, &path_string, true)).unwrap()
    };
    HttpResponse::Ok().body(body)
        // get the file we want to access as stream
        actix_files::NamedFile::open_async(full_path::get_full_path(&app_data, &path_string, true)).await.unwrap().into_response(&req)
    }
}

pub async fn index(app_data: web::Data<crate::AppData>) -> impl Responder {
@@ -45,17 +38,9 @@
                    "index.html", 
                    false
                ),
                templating::TemplateSchema {
                    content: "".to_string(),
                    shortened: "".to_string(),
                    domain: app_data.config.application.html.domain.clone(),
                    count: if app_data.config.application.html.count {
                        database::count_entries(&mut app_data.database.get_conn().unwrap(), ContentType::All).await.to_string()
                    } else {
                        "".to_string()
                    }
                }
            )
                app_data,
                &mut templating::TemplateSchema::create_null_schema()
            ).await
        )
    } else {
        HttpResponse::Ok().body(
@@ -84,13 +69,15 @@
                                "paste.html"
                            }, 
                            false),
                        templating::TemplateSchema {
                            content: form.content.clone(),
                            shortened: submitted_entry.shortened.clone(),
                            domain: app_data.config.application.html.domain.clone(),
                            count: count.to_string()
                        }
                    )
                        app_data.clone(),
                        &mut templating::TemplateSchema::new(
                            (
                                form.content.clone(),
                                submitted_entry.shortened.clone(),
                                count
                            )
                        )
                    ).await
                );
            } else {
                // If the content type is a URL, tell them the shortened URL, otherwise tell them the paste ID
@@ -124,17 +111,9 @@
            if app_data.config.application.html.template {
                templating::read_and_apply_templates(
                    full_path::get_full_path(&app_data, "404.html", false),
                    templating::TemplateSchema {
                        content: "".to_string(),
                        shortened: "".to_string(),
                        domain: app_data.config.application.html.domain.clone(),
                        count: if app_data.config.application.html.count {
                            database::count_entries(&mut app_data.database.get_conn().unwrap(), ContentType::All).await.to_string()
                        } else {
                            "".to_string()
                        }
                    }
                )
                    app_data.clone(),
                    &mut templating::TemplateSchema::create_null_schema()
                ).await
            } else {
                std::fs::read_to_string(PathBuf::from(format!("static/404.html"))).unwrap()
            }
diff --git a/src/templating.rs b/src/templating.rs
index 9536232..96610bd 100644
--- a/src/templating.rs
+++ a/src/templating.rs
@@ -1,22 +1,127 @@
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use actix_web::web;
use crate::{AppData, database};
use crate::database::Count;


#[derive(Debug, Clone)]
pub struct TemplateSchema {
    pub content: String,
    pub shortened: String,
    pub domain: String,
    pub count: String
    pub count: Vec<Count>
}

pub trait IntoTemplateSchema {
    fn into(self) -> TemplateSchema;
}

impl IntoTemplateSchema for TemplateSchema {
    fn into(self) -> TemplateSchema {
        self
    }
}

impl IntoTemplateSchema for (String, String) {
    fn into(self) -> TemplateSchema {
        TemplateSchema {
            content: self.0,
            shortened: self.1,
            count: Vec::new()
        }
    }
}

impl IntoTemplateSchema for (String, String, Vec<Count>) {
    fn into(self) -> TemplateSchema {
        TemplateSchema {
            content: self.0,
            shortened: self.1,
            count: self.2
        }
    }
}

pub fn read_and_apply_templates(path: PathBuf, schema: TemplateSchema) -> String {
impl IntoTemplateSchema for (String, String, Count) {
    fn into(self) -> TemplateSchema {
        TemplateSchema {
            content: self.0,
            shortened: self.1,
            count: vec![self.2]
        }
    }
}

impl TemplateSchema {
    pub fn new<T: IntoTemplateSchema>(to_schema: T) -> TemplateSchema {
        to_schema.into()
    }
    pub fn create_null_schema() -> TemplateSchema {
        TemplateSchema {
            content: "".to_string(),
            shortened: "".to_string(),
            count: Vec::new()
        }
    }
}

async fn get_count_and_update_schema (schema: &mut TemplateSchema, app_data: &web::Data<AppData>) {
    let mut connection = app_data.database.get_conn().unwrap();
    let count = database::count_entries(&mut connection, database::ContentType::All).await;
    schema.count.push(count);
}

pub async fn get_necessary_value(key: String, key_value: u8, app_data: web::Data<AppData>, schema: &mut TemplateSchema) -> String {
    // create a vector of (ContentType, i64) tuples
    match key.as_str() {
        "content" => 
            schema.content.as_str().to_string(),
        "shortened" => 
            schema.shortened.as_str().to_string(),
        "domain" => 
            app_data.config.application.html.domain.clone(),
        "count" => 
            match schema.count.iter().find(|&x| x.content_type == key_value.into()) {
                Some(count) => count.count.to_string(),
                None => {
                    get_count_and_update_schema(schema, &app_data).await;
                    schema.count.iter().find(|&x| x.content_type == key_value.into()).unwrap().count.to_string()
                }
            },
        _ => panic!("Invalid key")
    }
}

pub async fn read_and_apply_templates(path: PathBuf, app_data: web::Data<AppData>, schema: &mut TemplateSchema) -> String {
    // read the file into a string
    let mut file = File::open(path).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    // Hardcoded templates, will change this if/when the amount of templates increases
    contents
        .replace("{{content}}", &schema.content)
        .replace("{{shortened}}", &schema.shortened)
        .replace("{{domain}}", &schema.domain)
        .replace("{{count}}", &schema.count)
}
    // replace all instances of {key} with the value, and return the string
    let mut new_contents = String::new();
    let mut key = String::new();
    let mut key_value = 0;
    let mut in_key = false;
    for c in contents.chars() {
        if c == '{' {
            in_key = true;
        } else if c == '}' {
            in_key = false;
            let value = get_necessary_value(key.clone(), key_value, app_data.clone(), schema).await;
            new_contents.push_str(value.as_str());
            key = String::new();
            key_value = 0;
        } else if in_key {
            if c == ':' {
                key_value = key.parse::<u8>().unwrap();
                key = String::new();
            } else {
                key.push(c);
            }
        } else {
            new_contents.push(c);
        }
    } 
    new_contents
}
diff --git a/target/.rustc_info.json b/target/.rustc_info.json
index 7928b5a..84d1174 100644
--- a/target/.rustc_info.json
+++ a/target/.rustc_info.json
@@ -1,1 +1,1 @@
{"rustc_fingerprint":13098977256995595289,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.65.0\nbinary: rustc\ncommit-hash: unknown\ncommit-date: unknown\nhost: aarch64-apple-darwin\nrelease: 1.65.0\nLLVM version: 15.0.0\n","stderr":""},"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/opt/homebrew/Cellar/rust/1.65.0\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""}},"successes":{}}
{"rustc_fingerprint":13098977256995595289,"outputs":{"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/opt/homebrew/Cellar/rust/1.65.0\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.65.0\nbinary: rustc\ncommit-hash: unknown\ncommit-date: unknown\nhost: aarch64-apple-darwin\nrelease: 1.65.0\nLLVM version: 15.0.0\n","stderr":""},"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""}},"successes":{}}
diff --git a/html/static/7800.svg b/html/static/7800.svg
new file mode 100644
index 0000000..354c087 100644
--- /dev/null
+++ a/html/static/7800.svg
@@ -1,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 25.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg

   width="3415.6"
   height="776.4"
   version="1.1"
   viewBox="0 0 3415.6 776.4"
   xml:space="preserve"
   id="svg31"
   sodipodi:docname="7800.svg"
   inkscape:version="1.2 (dc2aeda, 2022-05-15)"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg"><defs

   id="defs35" /><sodipodi:namedview

   id="namedview33"
   pagecolor="#ffffff"
   bordercolor="#666666"
   borderopacity="1.0"
   inkscape:showpageshadow="2"
   inkscape:pageopacity="0.0"
   inkscape:pagecheckerboard="0"
   inkscape:deskcolor="#d1d1d1"
   showgrid="false"
   showguides="false"
   inkscape:zoom="0.24645277"
   inkscape:cx="1091.487"
   inkscape:cy="355.0376"
   inkscape:window-width="1728"
   inkscape:window-height="1051"
   inkscape:window-x="0"
   inkscape:window-y="38"
   inkscape:window-maximized="1"
   inkscape:current-layer="svg31" />
<style

   type="text/css"
   id="style9">
	.st0{fill:#FFFFFF;}
</style>
<g

   transform="matrix(0.99999988,0,0,0.9994848,-441.33547,-3.2881357)"
   id="g29">
	
		
			<path

   class="st0"
   d="m 2221.4356,129.18983 c -59.1,-82.299999 -146,-125.8999994 -251.1,-125.8999994 -105.2,0 -192,43.5000004 -251.1,125.8999994 -49.7,69.3 -77.1,162.4 -77.1,262.2 0,193.2 101.5,388.2 328.2,388.2 226.7,0 328.2,-195 328.2,-388.2 0,-99.8 -27.3,-192.9 -77.1,-262.2 z m -251.1,420.6 c -79.1,0 -87.7,-107.4 -87.7,-159.2 0,-51.3 8.7,-157.4 87.7,-157.4 79,0 87.7,106.1 87.7,157.4 -0.1,51.8 -8.7,159.2 -87.7,159.2 z"
   id="path11" />
			<path

   class="st0"
   d="m 2877.8356,129.18983 c -59.1,-82.299999 -145.9,-125.8999994 -251.1,-125.8999994 -105.2,0 -192,43.5000004 -251.1,125.8999994 -49.7,69.3 -77.1,162.4 -77.1,262.2 0,193.2 101.5,388.2 328.2,388.2 226.7,0 328.2,-195 328.2,-388.2 0.1,-99.8 -27.2,-192.9 -77.1,-262.2 0.1,0 0,0 0,0 z m -251.1,420.6 c -79.1,0 -87.7,-107.4 -87.7,-159.2 0,-51.3 8.7,-157.4 87.7,-157.4 79,0 87.7,106.1 87.7,157.4 0,51.8 -8.7,159.2 -87.7,159.2 z"
   id="path13" />
			<polygon

   class="st0"
   points="1663.6,760.3 1988.3,14 1326.3,14 1326.3,252.5 1633.4,252.5 1387.7,760.3 "
   id="polygon15"
   transform="translate(-884.96443,3.2898306)" />
			<path

   class="st0"
   d="m 1496.8356,372.28983 c 93.1,-34.3 155.8,-97.9 155.8,-170.9 0,-109.399999 -140.4,-197.9999994 -313.7,-197.9999994 -173.3,0 -313.7,88.7000004 -313.7,197.9999994 0,73 62.7,136.6 155.8,170.9 -102.5,36.8 -172.1,108.3 -172.1,190.5 0,119.8 147.8,216.9 330,216.9 182.3,0 330,-97.1 330,-216.9 0,-82.3 -69.6,-153.7 -172.1,-190.5 z m -157.9,-208.7 c 35.4,0 64,28.7 64,64 0,35.3 -28.7,64 -64,64 -35.4,0 -64,-28.7 -64,-64 0,-35.3 28.6,-64 64,-64 z m 0,439.2 c -42.3,0 -76.6,-34.3 -76.6,-76.6 0,-42.3 34.3,-76.6 76.6,-76.6 42.3,0 76.6,34.3 76.6,76.6 0,42.3 -34.3,76.6 -76.6,76.6 z"
   id="path17" />
		
	
	
		
			
			
			
			
			
			
		
	
</g>
<g

   aria-label=".io"
   id="text345"
   style="font-size:1024px;line-height:1.25;letter-spacing:-60px;stroke-width:5.33333;fill:#ffffff"><path

     d="m 2485.2292,708 q 0,-28 20.5,-48.5 20.5,-20.5 49,-20.5 28.5,0 49,20.5 20.5,20.5 20.5,49 0,29 -20.5,49.5 -20,20 -49,20 -29.5,0 -49.5,-20 -20,-20 -20,-50 z"
     style="font-family:Futura;-inkscape-font-specification:Futura;fill:#ffffff"
     id="path499" /><path

     d="m 2836.2292,277 v 487 h -112.5 V 277 Z m -129.5,-202.5 q 0,-29.5 21.5,-51 21.5,-21.5 51.5,-21.5 30.5,0 52,21.5 21.5,21 21.5,51.5 0,30.5 -21.5,52 -21,21.5 -51.5,21.5 -30.5,0 -52,-21.5 -21.5,-21.5 -21.5,-52.5 z"
     style="font-family:Futura;-inkscape-font-specification:Futura;fill:#ffffff"
     id="path501" /><path

     d="m 2895.7292,517 q 0,-105.5 75.5,-179.5 75.5,-74 184,-74 109,0 185,74.5 75,74.5 75,183 0,109.5 -75.5,183.5 -76,73.5 -186.5,73.5 -109.5,0 -183.5,-75 -74,-74 -74,-186 z m 115,2 q 0,73 39,115.5 40,43 105.5,43 66,0 105.5,-42.5 39.5,-42.5 39.5,-113.5 0,-71 -39.5,-113.5 -40,-43 -105.5,-43 -64.5,0 -104.5,43 -40,43 -40,111 z"
     style="font-family:Futura;-inkscape-font-specification:Futura;fill:#ffffff"
     id="path503" /></g></svg>
diff --git a/html/static/HKGroteskWide-SemiBold.otf b/html/static/HKGroteskWide-SemiBold.otf
new file mode 100644
index 0000000000000000000000000000000000000000..8a96f8caa67a95d8f3fbc8258275aa40c7b79ff5 100644
Binary files /dev/null and a/html/static/HKGroteskWide-SemiBold.otf differdiff --git a/html/static/paste.html b/html/static/paste.html
new file mode 100644
index 0000000..a77a42e 100644
--- /dev/null
+++ a/html/static/paste.html
@@ -1,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/style.css">
<title>7800.io: Pastebin</title> 
</head>
<body>
    <h1>Pastebin</h1>
    <main>
        <div id="ui">
            <p>New paste</p>
            <form action="/" method="post">
                <input type="hidden" name="content_type" value="Paste">
                <textarea name="content" placeholder="Paste" class="text"></textarea>
                <input type="submit" value="Paste" class="button">
            </form>
        </div>
    </main>
</body>
</html>
diff --git a/html/static/style.css b/html/static/style.css
index 359f792..5665cf2 100644
--- a/html/static/style.css
+++ a/html/static/style.css
@@ -1,5 +1,101 @@
@keyframes fadeInDownward {
    0% {
        opacity: 0;
        transform: translateY(-30px);
    }
    100% {
        opacity: 1;
        transform: translateY(0);
    }
}

@font-face {
    font-family: "HK Grotesk";
    src: url("/static/HKGroteskWide-SemiBold.otf");
}

html {
    height: 100%;
    background: linear-gradient(#650b0b, #ab1616);
}

h1 {
    display: none;
}

main {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

main * {
    animation-name: fadeInDownward;
    animation-duration: 1s;
}

body {
    font-family: sans-serif;
    background-color: #f0f0f0;
    font-family: "HK Grotesk";
    font-size: 1.5em;
    text-transform: uppercase;
    text-align: center;
    color: white;
}

#logo {
    width: 25em;
    margin-bottom: 2.5em;
}

/* if viewport is iphone sized */
@media screen and (max-width: 375px) {
    #logo {
        max-width: 75%;
        min-width: 10em;
    }
}

a {
    color: white;
    text-decoration: none;
    border-bottom: 2px solid white;
}

/* make #ui a rounded black rectangle with lots of padding */
#ui {
    display: inline-block;
    padding: 1em 2em;
    background: black;
    border-radius: 1em;
}

/* make .text, which is an input text field, use the same font as the rest of the website. make it a black box with a thick white outline */
.text {
    display: block;
    font-family: monospace;
    color: white;
    background: black;
    border: 2px dashed white;
    padding: 0.5em 1em;
    margin: 1em 0;
}

/* make .button a black button with thick white outline that uses the same font as the rest of the website */
.button {
    font-family: "HK Grotesk";
    text-transform: uppercase;
    text-align: center;
    color: black;
    background: white;
    border: none;
    padding: 0.5em 1em;
    /* make the button a rounded rectangle */
    border-radius: 1em;
}

/* make textarea a reasonable size by default */
textarea {
    width: 20em;
    height: 10em;
}
diff --git a/html/static/url.html b/html/static/url.html
new file mode 100644
index 0000000..4f9c413 100644
--- /dev/null
+++ a/html/static/url.html
@@ -1,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/style.css">
<title>7800.io: URL Shortener</title> 
</head>
<body>
    <h1>URL Shortener</h1>
    <main>
        <div id="ui">
            <p>Shorten a URL</p>
            <form action="/" method="post">
                <input type="hidden" name="content_type" value="Url">
                <input type="text" name="content" placeholder="URL" class="text">
                <input type="submit" value="Shorten" class="button">
            </form>
        </div>
    </main>
</body>
</html>
diff --git a/target/debug/url_shortener b/target/debug/url_shortener
index 810b445b86457400f02a7c03324665984f33364c..93b43235902d088f703d90c27b6673e0f2da9704 100755
Binary files a/target/debug/url_shortener and a/target/debug/url_shortener differ