This commit is contained in:
Tyler Murphy 2023-02-13 22:41:09 -05:00
parent df95f2a01c
commit b6fbeb5124
16 changed files with 179 additions and 37 deletions

View file

@ -2,11 +2,22 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XSSBook - Not Found</title>
<meta name="author" content="Tyler Murphy">
<meta name="description" content="404 Page">
<meta property="og:title" content="xssbook">
<meta property="og:site_name" content="xssbook.com">
<meta property="og:description" content="404 Page">
<meta itemprop="name" content="xssbook">
<meta itemprop="description" content="404 Page">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/css/404.css">
<link rel="stylesheet" href="/css/header.css">
<script src="/js/main.js"></script>
<title>XSSBook - Not Found</title>
</head>
<body>
<div id="header">

View file

@ -2,11 +2,22 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XSSBook - Admin Panel</title>
<meta name="author" content="Tyler Murphy">
<meta name="description" content="Admin Panel">
<meta property="og:title" content="xssbook">
<meta property="og:site_name" content="xssbook.com">
<meta property="og:description" content="Admin Panel">
<meta itemprop="name" content="xssbook">
<meta itemprop="description" content="Admin Panel">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/css/header.css">
<link rel="stylesheet" href="/css/admin.css">
<script src="/js/admin.js" type="module"></script>
<title>XSSBook - Admin Panel</title>
</head>
<body>
<div id="header">

View file

@ -28,16 +28,19 @@ body {
@font-face {
font-family: facebook;
src: url("../fonts/facebook.otf") format("opentype");
font-display: swap;
}
@font-face {
font-family: sfpro;
src: url("../fonts/sfpro.otf") format("opentype");
font-display: swap;
}
@font-face {
font-family: sfprobold;
src: url("../fonts/sfprobold.otf") format("opentype");
font-display: swap;
}
.logo {
@ -322,14 +325,15 @@ form {
width: 100%;
}
#load {
#load, .cload {
width: 100%;
display: flex;
justify-content: center;
padding-bottom: 20px;
cursor: pointer;
}
#load a:hover {
#load span:hover, .cload span:hover {
border-bottom: var(--medium) 1px solid;
}

View file

@ -2,10 +2,23 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XSSBook - Home</title>
<meta name="author" content="Tyler Murphy">
<meta name="description" content="Home">
<meta property="og:title" content="xssbook">
<meta property="og:site_name" content="xssbook.com">
<meta property="og:description" content="Home">
<meta itemprop="name" content="xssbook">
<meta itemprop="description" content="Home">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/css/header.css">
<link rel="stylesheet" href="/css/home.css">
<title>XSSBook - Home</title>
<script type="module" src="/js/home.js"></script>
</head>
<body>

View file

@ -7,23 +7,23 @@ export function header(home, people, user_id) {
return [
div({id: 'header'},
span({class: 'logo'},
a({href: '/'},
a({href: '/', 'aria-label': 'xssbook.com'},
parse('xssbook')
)
),
div({class: 'buttons'},
a({id: 'home', class: home ? 'selected' : '', href: 'home'},
a({id: 'home', class: home ? 'selected' : '', href: '/home', 'aria-label': 'xssbook home page'},
svg({viewBox: '0 0 28 28', fill: 'currentColor', height: '28', width: '28'},
path({d: "M25.825 12.29C25.824 12.289 25.823 12.288 25.821 12.286L15.027 2.937C14.752 2.675 14.392 2.527 13.989 2.521 13.608 2.527 13.248 2.675 13.001 2.912L2.175 12.29C1.756 12.658 1.629 13.245 1.868 13.759 2.079 14.215 2.567 14.479 3.069 14.479L5 14.479 5 23.729C5 24.695 5.784 25.479 6.75 25.479L11 25.479C11.552 25.479 12 25.031 12 24.479L12 18.309C12 18.126 12.148 17.979 12.33 17.979L15.67 17.979C15.852 17.979 16 18.126 16 18.309L16 24.479C16 25.031 16.448 25.479 17 25.479L21.25 25.479C22.217 25.479 23 24.695 23 23.729L23 14.479 24.931 14.479C25.433 14.479 25.921 14.215 26.132 13.759 26.371 13.245 26.244 12.658 25.825 12.29"})
)
),
a({id: 'people', class: people ? 'selected' : '', href: 'people'},
a({id: 'people', class: people ? 'selected' : '', href: '/people', 'aria-label': 'xssbook people page'},
svg({viewBox: '0 0 28 28', fill: 'currentColor', height: '28', width: '28'},
path({d: "M10.5 4.5c-2.272 0-2.75 1.768-2.75 3.25C7.75 9.542 8.983 11 10.5 11s2.75-1.458 2.75-3.25c0-1.482-.478-3.25-2.75-3.25zm0 8c-2.344 0-4.25-2.131-4.25-4.75C6.25 4.776 7.839 3 10.5 3s4.25 1.776 4.25 4.75c0 2.619-1.906 4.75-4.25 4.75zm9.5-6c-1.41 0-2.125.841-2.125 2.5 0 1.378.953 2.5 2.125 2.5 1.172 0 2.125-1.122 2.125-2.5 0-1.659-.715-2.5-2.125-2.5zm0 6.5c-1.999 0-3.625-1.794-3.625-4 0-2.467 1.389-4 3.625-4 2.236 0 3.625 1.533 3.625 4 0 2.206-1.626 4-3.625 4zm4.622 8a.887.887 0 00.878-.894c0-2.54-2.043-4.606-4.555-4.606h-1.86c-.643 0-1.265.148-1.844.413a6.226 6.226 0 011.76 4.336V21h5.621zm-7.122.562v-1.313a4.755 4.755 0 00-4.749-4.749H8.25A4.755 4.755 0 003.5 20.249v1.313c0 .518.421.938.937.938h12.125c.517 0 .938-.42.938-.938zM20.945 14C24.285 14 27 16.739 27 20.106a2.388 2.388 0 01-2.378 2.394h-5.81a2.44 2.44 0 01-2.25 1.5H4.437A2.44 2.44 0 012 21.562v-1.313A6.256 6.256 0 018.25 14h4.501a6.2 6.2 0 013.218.902A5.932 5.932 0 0119.084 14h1.861z"})
)
)
),
a({class: 'pfp', id: 'profile', href: 'profile'},
a({class: 'pfp', id: 'profile', href: '/profile', 'aria-label': 'your xssbook profile'},
user_id === undefined ? parse('') : pfp(user_id)
)
),
@ -49,7 +49,7 @@ export function parsePost(post, users, self) {
return (
div({class: 'post', postid: post.post_id},
div({class: 'postheader'},
a({class: 'pfp', href: `/profile?id=${author.user_id}`},
a({class: 'pfp', href: `/profile?id=${author.user_id}`, 'aria-label': 'Post author profile like'},
pfp(author.user_id)
),
div({class: 'postname'},
@ -110,7 +110,18 @@ export function parsePost(post, users, self) {
parse('Like')
)
),
span({onclick: () => this.parentElement.parentElement.getElementsByClassName('newcomment')[0].focus()},
span({onclick: (event) => {
var post = event.target;
while(post.parentElement) {
post = post.parentElement
if (post.getAttribute('postid')) {
break;
}
}
post.getElementsByClassName('comments')[0].getElementsByClassName('newcomment')[0].focus()
}},
i({class: 'icons comm'}),
span({class: 'bold'},
parse('Comment')
@ -122,8 +133,8 @@ export function parsePost(post, users, self) {
div({class: 'comment commentsubmit', style: 'margin-top: 0'}),
...comments,
comments.length > 0 ?
div({id: 'load', class: 'load', style: 'justify-content: inherit; margin-left: 3.5em; font-size: .9em; margin-bottom: -.5em;'},
a({class: 'blod gtext', onclick: async (event) => {
div({class: 'cload', style: 'justify-content: inherit; margin-left: 3.5em; font-size: .9em; margin-bottom: -.5em;'},
span({class: 'blod gtext', onclick: async (event) => {
page++;
@ -148,7 +159,7 @@ export function parsePost(post, users, self) {
)
: parse(''),
div({class: 'comment commentsubmit'},
a({class: 'pfp', href: 'profile'},
a({class: 'pfp', href: '/profile', 'aria-label': 'Your profile link'},
pfp(self.user_id)
),
form({onsubmit: async (event) => {
@ -173,7 +184,7 @@ export function parsePost(post, users, self) {
let comments = post.getElementsByClassName('comments')[0]
let load = comments.getElementsByClassName('load')[0];
let load = comments.getElementsByClassName('cload')[0];
if (load == undefined) {
load = comments.lastChild
}
@ -186,7 +197,7 @@ export function parsePost(post, users, self) {
event.target.elements.text.value = ''
}},
input({type: 'text', name: 'text', placeholder: 'Write a comment', id: 'newcomment', class: 'newcomment'})
input({type: 'text', name: 'text', placeholder: 'Write a comment', class: 'newcomment'})
)
)
)
@ -202,7 +213,7 @@ export function parseComment(comment, users) {
return (
div({class: 'comment'},
a({class: 'pfp'},
a({class: 'pfp', href: `/profile?id=${comment.user_id}`, 'aria-label': 'Comment author profile link'},
pfp(comment.user_id)
),
span({},
@ -223,7 +234,7 @@ export function parseComment(comment, users) {
export function parseUser(user) {
return (
a({class: 'person', href: `/profile?id=${user.user_id}`},
a({class: 'person', href: `/profile?id=${user.user_id}`, 'aria-label': 'User profile link'},
div({class: 'profile'},
pfp(user.user_id)
),

View file

@ -34,7 +34,7 @@ function render() {
...header(true, false, data.self.user_id),
div({id: 'create'},
div({class: 'create'},
a({class: 'pfp', href: 'profile'},
a({class: 'pfp', href: '/profile'},
pfp(data.self.user_id)
),
button({class: 'pfp'},
@ -55,7 +55,7 @@ function render() {
),
div({class: 'fullline'}),
div({class: 'postheader'},
a({class: 'pfp', style: 'cursor: auto'},
a({class: 'pfp', style: 'cursor: auto', href: '/profile'},
pfp(data.self.user_id)
),
div({class: 'postname'},
@ -75,7 +75,7 @@ function render() {
)
),
div({id: 'load'},
a({class: 'blod gtext', onclick: async () => {
span({class: 'blod gtext', onclick: async () => {
const posts = await load()
data.posts.push(... posts)

View file

@ -65,8 +65,9 @@ export function form(attrs, ...children) {
return createElement("form", attrs, ...children)
}
export function img(attrs, ...children) {
export function img(alt, attrs, ...children) {
attrs['onerror'] = (event) => event.target.remove()
attrs['alt'] = alt
return createElement("img", attrs, ...children)
}
@ -98,12 +99,16 @@ export function parse(html) {
return document.createRange().createContextualFragment(html);
}
export function pfpl(id) {
}
export function pfp(id) {
return img({src: `/image/avatar?user_id=${id}`})
return img('pfp', {src: `/image/avatar?user_id=${id}`})
}
export function banner(id) {
return img({src: `/image/banner?user_id=${id}`})
return img('banner', {src: `/image/banner?user_id=${id}`})
}
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',

View file

@ -1,4 +1,4 @@
import { div, body, a, parse } from './main.js'
import { div, body, span, parse } from './main.js'
import { loadself, loaduserspage } from './api.js'
import { header, parseUser } from './components.js'
@ -11,7 +11,7 @@ function render() {
...data.users.map(u => parseUser(u))
),
div({id: 'load'},
a({class: 'bold gtext', onclick: async () => {
span({class: 'bold gtext', onclick: async () => {
let users = await load()
let el = document.getElementById("users")

View file

@ -2,10 +2,23 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XSSBook - Login</title>
<meta name="author" content="Tyler Murphy">
<meta name="description" content="Login">
<meta property="og:title" content="xssbook">
<meta property="og:site_name" content="xssbook.com">
<meta property="og:description" content="Login">
<meta itemprop="name" content="xssbook">
<meta itemprop="description" content="Login">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/css/login.css">
<script src="/js/login.js" type="module"></script>
<title>XSSBook - Login</title>
</head>
<body>
<div class="login">
@ -18,7 +31,7 @@
<input type="password" name="pass" id="pass" placeholder="Password">
<span class="error ctext"></span>
<button class="primary login-button bold" value="1" name="login" type="submit" id="login" onclick="window.onlogin()">Log In</button>
<a class="btext ctext">Forgot Password?</a>
<a class="btext ctext" href="/forgot">Forgot Password?</a>
<div class="line"></div>
<button class="success newacc" onclick="document.getElementById('popup').classList.remove('hidden')">Create new account</button>
</div>

View file

@ -2,11 +2,24 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XSSBook - People</title>
<meta name="author" content="Tyler Murphy">
<meta name="description" content="People">
<meta property="og:title" content="xssbook">
<meta property="og:site_name" content="xssbook.com">
<meta property="og:description" content="People">
<meta itemprop="name" content="xssbook">
<meta itemprop="description" content="People">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/css/header.css">
<link rel="stylesheet" href="/css/people.css">
<script src="/js/people.js" type="module"></script>
<title>XSSBook - People</title>
</head>
<body>
</body>

View file

@ -1,13 +1,27 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XSSBook - Profile</title>
<meta name="author" content="Tyler Murphy">
<meta name="description" content="Profile">
<meta property="og:title" content="xssbook">
<meta property="og:site_name" content="xssbook.com">
<meta property="og:description" content="Profile">
<meta itemprop="name" content="xssbook">
<meta itemprop="description" content="Profile">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/css/header.css">
<link rel="stylesheet" href="/css/profile.css">
<link rel="stylesheet" href="/css/home.css">
<script src="/js/profile.js" type="module"></script>
<title>XSSBook - Profile</title>
</head>
<body>
</body>

9
public/robots.txt Normal file
View file

@ -0,0 +1,9 @@
User-agent: Googlebot
Disallow: /api
User-agent: Googlebot
User-agent: AdsBot-Google
Disallow: /api
User-agent: *
Disallow: /api

View file

@ -31,6 +31,10 @@ pub async fn favicon() -> Response {
super::serve("/favicon.ico").await
}
pub async fn robots() -> Response {
super::serve("/robots.txt").await
}
#[derive(Deserialize)]
pub struct AvatarRequest {
user_id: u64,

View file

@ -35,6 +35,7 @@ pub fn router() -> Router {
Router::new()
.nest("/", pages::router())
.route("/favicon.ico", get(file::favicon))
.route("/robots.txt", get(file::robots))
.route("/js/*path", get(file::js))
.route("/css/*path", get(file::css))
.route("/fonts/*path", get(file::fonts))
@ -71,7 +72,7 @@ pub async fn serve(path: &str) -> Response {
res.headers_mut().insert(
HeaderName::from_static("cache-control"),
HeaderValue::from_static("public max-age=300"),
HeaderValue::from_static("max-age=300"),
);
res.into_response()

View file

@ -1,13 +1,12 @@
use axum::{
response::{IntoResponse, Redirect, Response},
routing::get,
Router,
routing::get, Router
};
use crate::{
public::console,
types::{
extract::{AuthorizedUser, Log},
extract::{AuthorizedUser, Log, UserAgent},
http::ResponseCode,
},
};
@ -58,6 +57,15 @@ async fn wordpress(_: Log) -> Response {
ResponseCode::ImATeapot.text("Hello i am a teapot owo")
}
async fn forgot(UserAgent(agent): UserAgent, _: Log) -> Response {
if agent.starts_with("curl") {
return super::serve("/404.html").await
}
Redirect::to("https://www.youtube.com/watch?v=dQw4w9WgXcQ").into_response()
}
pub fn router() -> Router {
Router::new()
.route("/", get(root))
@ -69,4 +77,5 @@ pub fn router() -> Router {
.route("/wp-admin", get(wordpress))
.route("/admin", get(admin))
.route("/docs", get(api))
.route("/forgot", get(forgot))
}

View file

@ -7,7 +7,7 @@ use axum::{
async_trait,
body::HttpBody,
extract::{ConnectInfo, FromRequest, FromRequestParts},
http::{request::Parts, Request},
http::{request::Parts, Request, header::USER_AGENT},
response::Response,
BoxError, RequestExt,
};
@ -236,6 +236,30 @@ pub trait Check {
}
}
pub struct UserAgent(pub String);
#[async_trait]
impl<S> FromRequestParts<S> for UserAgent
where
S: Send + Sync,
{
type Rejection = Response;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self> {
let agent = parts.headers.get(USER_AGENT);
let Some(agent) = agent else {
return Err(ResponseCode::BadRequest.text("Bad Request"));
};
let Ok(agent) = agent.to_str() else {
return Err(ResponseCode::BadRequest.text("Bad Request"));
};
Ok(UserAgent(agent.to_string()))
}
}
async fn read_body<S, B>(mut req: Request<B>, state: &S) -> Result<Vec<u8>>
where
B: HttpBody + Sync + Send + 'static,