bruh
This commit is contained in:
33
frontend/cont.html
Normal file
33
frontend/cont.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Home — NoPorn</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div>
|
||||
<div class="brand">Fax, no printer — <span style="font-weight:400">NoPorn</span></div>
|
||||
<div class="meta">
|
||||
This page is server-side! It will work if you have JS disabled.
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<a href="/">Home</a>
|
||||
<a href="/login">Log in</a>
|
||||
<a href="/register">Register</a>
|
||||
<a href="/publish/post" class="button">Publish</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
<div class="card">
|
||||
<!--ReplaceWithContent-->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
61
frontend/index.html
Normal file
61
frontend/index.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Home — NoPorn</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div>
|
||||
<div class="brand">Fax, no printer — <span style="font-weight:400">NoPorn</span></div>
|
||||
<div class="meta">We don't allow porn! Go to another site for that.</div>
|
||||
<div class="meta">
|
||||
What you can do here:<br>
|
||||
- Post Stuff<br>
|
||||
- Search<br>
|
||||
- Explore<br>
|
||||
- Comment
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<a href="/">Home</a>
|
||||
<a href="/login">Log in</a>
|
||||
<a href="/register">Register</a>
|
||||
<a href="/publish/post" class="button">Publish</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Open a post by ID 🔎</h3>
|
||||
<form id="open-post-form" onsubmit="event.preventDefault(); location.href='/post/'+document.getElementById('open-id').value.trim();">
|
||||
<div class="form-row">
|
||||
<label for="open-id">Post ID</label>
|
||||
<input id="open-id" type="text" placeholder="e.g. 1234-abcd" />
|
||||
</div>
|
||||
<button class="button" type="submit">Open post</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Quick links</h3>
|
||||
<ul>
|
||||
<li><a href="/publish/post">Create / Publish a post</a></li>
|
||||
<li><a href="/login">Log in</a> — to attach session and publish.</li>
|
||||
<li>
|
||||
<a href="/ssr/">
|
||||
Server-side pages
|
||||
</a> — for miscellaneous stuff
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
38
frontend/login.html
Normal file
38
frontend/login.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Login — NoPorn</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="brand">NoPorn — Login</div>
|
||||
<div class="nav"><a href="/">Home</a> <a href="/register">Register</a></div>
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<div class="card">
|
||||
<form id="login-form">
|
||||
<div class="form-row">
|
||||
<label>Username</label>
|
||||
<input name="username" type="text" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Password</label>
|
||||
<input name="password" type="password" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<button class="button" type="submit">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
24
frontend/post.html
Normal file
24
frontend/post.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Post — NoPorn</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="brand">Post</div>
|
||||
<div class="nav"><a href="/">Home</a></div>
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<div id="post-container"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
49
frontend/publish_post.html
Normal file
49
frontend/publish_post.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Publish Post — NoPorn</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="brand">Publish a post</div>
|
||||
<div class="nav">
|
||||
<a href="/">Home</a>
|
||||
<a href="#" id="logout-btn" class="button" style="background:#b23">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<div class="card">
|
||||
<form id="publish-form" enctype="multipart/form-data">
|
||||
<div class="form-row">
|
||||
<label>Title</label>
|
||||
<input name="title" type="text" required />
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Tags — comma separated</label>
|
||||
<input name="tags" type="text" placeholder="cats, summer, vacation" />
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>File</label>
|
||||
<input name="file" type="file" accept="image/*,video/*,application/pdf" required />
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<button class="button" type="submit">Publish</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="meta">Note — this page requires you to be logged in. If you are redirected to login, sign in and come back. ✨</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
37
frontend/register.html
Normal file
37
frontend/register.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Register — NoPorn</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="brand">NoPorn — Register</div>
|
||||
<div class="nav"><a href="/">Home</a> <a href="/login">Login</a></div>
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<div class="card">
|
||||
<form id="register-form">
|
||||
<div class="form-row">
|
||||
<label>Username</label>
|
||||
<input name="username" type="text" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Password</label>
|
||||
<input name="password" type="password" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<button class="button" type="submit">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
250
frontend/static/main.js
Normal file
250
frontend/static/main.js
Normal file
@@ -0,0 +1,250 @@
|
||||
// main.js - shared helpers and page handlers
|
||||
const API = {
|
||||
login: '/api/login',
|
||||
register: '/api/register',
|
||||
logout: '/api/logout',
|
||||
publish: '/api/publish/post',
|
||||
create: '/api/create_post',
|
||||
getPost: (id) => `/api/post/${id}`,
|
||||
getPostContent: (id) => `/api/post_content/${id}`,
|
||||
getUser: (id) => `/api/getUser/${id}`
|
||||
};
|
||||
|
||||
function fetchJSON(url, opts = {}) {
|
||||
opts.credentials = 'include'; // include session cookie
|
||||
opts.headers = opts.headers || {};
|
||||
// if sending JSON set header, else for FormData don't set
|
||||
if (opts.body && !(opts.body instanceof FormData) && !opts.headers['Content-Type']) {
|
||||
opts.headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
return fetch(url, opts).then(async res => {
|
||||
const ct = res.headers.get('content-type') || '';
|
||||
if (ct.includes('application/json')) {
|
||||
const j = await res.json();
|
||||
if (!res.ok) throw j;
|
||||
return j;
|
||||
} else {
|
||||
if (!res.ok) {
|
||||
const txt = await res.text();
|
||||
throw { error: txt || 'Request failed', status: res.status };
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fetchFORM(url, opts = {}) { // Force form submission
|
||||
opts.credentials = 'include'; // include session cookie
|
||||
opts.headers = opts.headers || {};
|
||||
// if sending JSON set header, else for FormData don't set
|
||||
if (opts.body && !(opts.body instanceof FormData) && !opts.headers['Content-Type']) {
|
||||
opts.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
return fetch(url, opts).then(async res => {
|
||||
const ct = res.headers.get('content-type') || '';
|
||||
if (ct.includes('application/json')) {
|
||||
const j = await res.json();
|
||||
if (!res.ok) throw j;
|
||||
return j;
|
||||
} else {
|
||||
if (!res.ok) {
|
||||
const txt = await res.text();
|
||||
throw { error: txt || 'Request failed', status: res.status };
|
||||
}
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function showMessage(container, msg, ok = true) {
|
||||
container.innerHTML = `<div class="message ${ok ? 'ok' : 'error'}">${escapeHtml(msg)}</div>`;
|
||||
setTimeout(()=> {
|
||||
// optionally fade out after 6s
|
||||
// container.innerHTML='';
|
||||
}, 6000);
|
||||
}
|
||||
|
||||
function escapeHtml(s){
|
||||
return String(s).replace(/[&<>"']/g, c=>({
|
||||
'&':'&','<':'<','>':'>','"':'"',"'":'''
|
||||
}[c]));
|
||||
}
|
||||
|
||||
/* -------- Page helpers -------- */
|
||||
|
||||
async function handleLoginForm(form, messageContainer) {
|
||||
const data = new FormData(form);
|
||||
const payload = new URLSearchParams();
|
||||
for (const [k,v] of data.entries()) payload.append(k,v);
|
||||
try {
|
||||
const res = await fetchFORM(API.login, { method:'POST', body: payload });
|
||||
showMessage(messageContainer, res.message || 'Logged in!', true);
|
||||
// redirect to publish page after short success
|
||||
setTimeout(()=> window.location = '/', 600);
|
||||
} catch(err) {
|
||||
showMessage(messageContainer, err.error || JSON.stringify(err), false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRegisterForm(form, messageContainer) {
|
||||
const data = new FormData(form);
|
||||
const payload = new URLSearchParams();
|
||||
for (const [k,v] of data.entries()) payload.append(k,v);
|
||||
try {
|
||||
const res = await fetchFORM(API.register, { method:'POST', body: payload });
|
||||
showMessage(messageContainer, res.message || 'Registered!', true);
|
||||
setTimeout(()=> window.location = '/', 600);
|
||||
} catch(err) {
|
||||
showMessage(messageContainer, err.error || JSON.stringify(err), false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePublishForm(form, messageContainer) {
|
||||
const fd = new FormData();
|
||||
const title = form.querySelector('[name=title]').value.trim();
|
||||
const tagsRaw = form.querySelector('[name=tags]').value.trim();
|
||||
const fileEl = form.querySelector('[name=file]');
|
||||
if (!title) return showMessage(messageContainer, 'Title required', false);
|
||||
if (!fileEl.files || fileEl.files.length === 0) return showMessage(messageContainer, 'Pick a file', false);
|
||||
fd.append('title', title);
|
||||
// split tags by comma and append each tag as separate 'tags' field so backend getlist works
|
||||
const tags = tagsRaw ? tagsRaw.split(',').map(t=>t.trim()).filter(Boolean) : [];
|
||||
for (const t of tags) fd.append('tags', t);
|
||||
fd.append('file', fileEl.files[0]);
|
||||
|
||||
try {
|
||||
// use publish endpoint — it requires session cookie
|
||||
const res = await fetchFORM(API.publish, { method:'POST', body: fd });
|
||||
showMessage(messageContainer, res.message || 'Published!', true);
|
||||
// Redirect to new post page
|
||||
if (res.post_id) setTimeout(()=> window.location = `/post/${res.post_id}`, 600);
|
||||
} catch(err) {
|
||||
showMessage(messageContainer, err.error || JSON.stringify(err), false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout(button, msgContainer) {
|
||||
try {
|
||||
const res = await fetchJSON(API.logout, { method:'POST' });
|
||||
showMessage(msgContainer, res.message || 'Logged out', true);
|
||||
setTimeout(()=> window.location = '/', 500);
|
||||
} catch(err) {
|
||||
showMessage(msgContainer, err.error || 'Logout failed', false);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------- Pages: simple renderers -------- */
|
||||
|
||||
async function renderPostPage() {
|
||||
const message = document.getElementById('message');
|
||||
const id = window.location.pathname.split('/').pop();
|
||||
try {
|
||||
const meta = await fetchJSON(API.getPost(id));
|
||||
const container = document.getElementById('post-container');
|
||||
container.innerHTML = `
|
||||
<div class="card">
|
||||
<h2>${escapeHtml(meta.title)}</h2>
|
||||
<div class="meta">Post ID — ${escapeHtml(meta.id)} • Created: ${escapeHtml(meta.date_created || '')}</div>
|
||||
<div id="tags" class="tags"></div>
|
||||
<div id="media"></div>
|
||||
<div id="creator" class="meta" style="margin-top:10px"></div>
|
||||
</div>
|
||||
`;
|
||||
// tags
|
||||
const tagsWrap = document.getElementById('tags');
|
||||
(meta.tags||[]).forEach(t=> {
|
||||
const el = document.createElement('div'); el.className='tag'; el.textContent = t; tagsWrap.appendChild(el);
|
||||
});
|
||||
|
||||
// show content depending on content_type
|
||||
const media = document.getElementById('media');
|
||||
const ctype = (meta.content_type||'').toLowerCase();
|
||||
const contentUrl = API.getPostContent(meta.id);
|
||||
const imageExts = ['png','jpg','jpeg','gif','webp','bmp','tiff'];
|
||||
const videoExts = ['mp4','webm','mov','avi','mkv'];
|
||||
if (imageExts.includes(ctype)) {
|
||||
media.innerHTML = `<img class="preview" src="${contentUrl}" alt="image">`;
|
||||
} else if (videoExts.includes(ctype)) {
|
||||
media.innerHTML = `<video class="preview" controls src="${contentUrl}">Your browser can't play this video.</video>`;
|
||||
}
|
||||
else if (ctype === 'pdf') { // embed pdf
|
||||
media.innerHTML = `<iframe style="height:80vh;width:60%" src="${contentUrl}"></iframe>`;
|
||||
}
|
||||
else if (ctype === 'txt') {
|
||||
// fetch text content and display in <pre>
|
||||
try {
|
||||
const res = await fetchJSON(contentUrl);
|
||||
const txt = await res.text();
|
||||
media.innerHTML = `<pre style="white-space:pre-wrap;word-break:break-all;">${escapeHtml(txt)}</pre>`;
|
||||
} catch(e) {
|
||||
media.innerHTML = `<div class="message error">Could not load text content</div>`;
|
||||
}
|
||||
} else {
|
||||
// fallback - provide download link
|
||||
media.innerHTML = `<a class="button" href="${contentUrl}">Download content</a>`;
|
||||
}
|
||||
|
||||
// fetch creator username
|
||||
if (meta.creator_id) {
|
||||
try {
|
||||
const u = await fetchJSON(API.getUser(meta.creator_id));
|
||||
document.getElementById('creator').innerHTML = `By <strong>${escapeHtml(u.username)}</strong> — <a href="/user/${u.id}">View profile</a>`;
|
||||
} catch(e){
|
||||
document.getElementById('creator').innerHTML = `By user ${escapeHtml(meta.creator_id)}`;
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
showMessage(message, err.error || 'Could not load post', false);
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function renderUserPage() {
|
||||
const uid = window.location.pathname.split('/').pop();
|
||||
const container = document.getElementById('user-container');
|
||||
try {
|
||||
const u = await fetchJSON(API.getUser(uid));
|
||||
container.innerHTML = `
|
||||
<div class="card">
|
||||
<h2>${escapeHtml(u.username)}</h2>
|
||||
<div class="meta">User ID — ${escapeHtml(u.id)}</div>
|
||||
<div style="margin-top:12px">Profiles are currently incapable of showing posts. Please search for their name at <a href="/ssr/post">/ssr/post</a></div>
|
||||
</div>
|
||||
`;
|
||||
} catch(err){
|
||||
container.innerHTML = `<div class="card"><div class="message error">User not found</div></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional: attach simple behaviors if present on page */
|
||||
document.addEventListener('DOMContentLoaded', ()=> {
|
||||
// attach simple form bindings if forms exist
|
||||
const loginForm = document.getElementById('login-form');
|
||||
if (loginForm) {
|
||||
const msg = document.getElementById('message');
|
||||
loginForm.addEventListener('submit', (e)=>{ e.preventDefault(); handleLoginForm(loginForm, msg); });
|
||||
}
|
||||
|
||||
const registerForm = document.getElementById('register-form');
|
||||
if (registerForm) {
|
||||
const msg = document.getElementById('message');
|
||||
registerForm.addEventListener('submit', (e)=>{ e.preventDefault(); handleRegisterForm(registerForm, msg); });
|
||||
}
|
||||
|
||||
const publishForm = document.getElementById('publish-form');
|
||||
if (publishForm) {
|
||||
const msg = document.getElementById('message');
|
||||
publishForm.addEventListener('submit', (e)=>{ e.preventDefault(); handlePublishForm(publishForm, msg); });
|
||||
}
|
||||
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
if (logoutBtn) {
|
||||
const msg = document.getElementById('message');
|
||||
logoutBtn.addEventListener('click', ()=> handleLogout(logoutBtn, msg));
|
||||
}
|
||||
|
||||
// page-specific render hooks
|
||||
if (document.getElementById('post-container')) renderPostPage();
|
||||
if (document.getElementById('user-container')) renderUserPage();
|
||||
});
|
||||
83
frontend/static/style.css
Normal file
83
frontend/static/style.css
Normal file
@@ -0,0 +1,83 @@
|
||||
:root{
|
||||
--max-width:900px;
|
||||
--accent:#1565c0;
|
||||
--accent-low:#0b3566;
|
||||
--muted:#999;
|
||||
--card-bg:#303030;
|
||||
--page-bg:#202020;
|
||||
--radius:10px;
|
||||
--pad:18px;
|
||||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
||||
}
|
||||
|
||||
*{box-sizing:border-box}
|
||||
html,body{height:100%}
|
||||
body{
|
||||
margin:0;
|
||||
background:var(--page-bg);
|
||||
color:#fff;
|
||||
-webkit-font-smoothing:antialiased;
|
||||
}
|
||||
|
||||
.container{
|
||||
max-width:var(--max-width);
|
||||
margin:28px auto;
|
||||
padding:12px;
|
||||
}
|
||||
|
||||
.header{
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
gap:12px;
|
||||
margin-bottom:18px;
|
||||
}
|
||||
.brand{font-weight:700; font-size:1.3rem; color:var(--accent)}
|
||||
a{margin-left:10px; text-decoration:none; color:var(--accent)}
|
||||
|
||||
.card{
|
||||
background:var(--card-bg);
|
||||
border-radius:var(--radius);
|
||||
padding:var(--pad);
|
||||
box-shadow:0 6px 18px rgba(20,30,60,0.06);
|
||||
margin-bottom:14px;
|
||||
}
|
||||
|
||||
.form-row{margin-bottom:12px}
|
||||
label{display:block; font-weight:600; margin-bottom:6px}
|
||||
input[type="text"], input[type="password"], textarea, input[type="file"]{
|
||||
width:100%;
|
||||
padding:10px;
|
||||
border-radius:8px;
|
||||
border:1px solid #e0e6ef;
|
||||
font-size:0.975rem;
|
||||
}
|
||||
|
||||
.button{
|
||||
display:inline-block;
|
||||
padding:10px 14px;
|
||||
border-radius:8px;
|
||||
background:var(--accent);
|
||||
color:white !important;
|
||||
text-decoration:none;
|
||||
border:none;
|
||||
cursor:pointer;
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
.message{padding:8px 10px; border-radius:8px; margin-bottom:10px}
|
||||
.message.error{background:#ffecec; color:#900}
|
||||
.message.ok{background:#e8fff0; color:#064}
|
||||
|
||||
.meta{color:var(--muted); font-size:0.95rem}
|
||||
.tags{display:flex; gap:8px; flex-wrap:wrap; margin-top:8px}
|
||||
.tag{background:#eef6ff;color:var(--accent);padding:6px 8px;border-radius:999px;font-weight:600;font-size:0.85rem}
|
||||
|
||||
.preview{max-width:100%; border-radius:8px; margin-top:12px}
|
||||
.footer{color:var(--muted); font-size:0.9rem; margin-top:22px}
|
||||
|
||||
/* small responsive */
|
||||
@media (max-width:520px){
|
||||
.header{flex-direction:column; align-items:flex-start; gap:8px}
|
||||
.brand{font-size:1.1rem}
|
||||
}
|
||||
24
frontend/user.html
Normal file
24
frontend/user.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>User — NoPorn</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="brand">User profile</div>
|
||||
<div class="nav"><a href="/">Home</a></div>
|
||||
</div>
|
||||
|
||||
<div id="message"></div>
|
||||
|
||||
<div id="user-container"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user