mirror of
https://git.wownero.com/wownero/YellWOWPages.git
synced 2024-08-15 01:03:25 +00:00
Rewrite to Quart web-framework, refactor code.
This commit is contained in:
parent
6b300fd304
commit
67f4c34604
39 changed files with 656 additions and 980 deletions
52
.gitignore
vendored
Normal file
52
.gitignore
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
bin/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
.idea
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Rope
|
||||
.ropeproject
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
*.pot
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
19
README.md
Normal file
19
README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
## YellWOWPages
|
||||
|
||||
Wownero yellow pages web-application - lookup the WOW address of users.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
git clone gitea@git.wownero.com:muchwowmining/YellWOWPages.git
|
||||
cd YellWOWPages
|
||||
pip install -r requirements.txt
|
||||
|
||||
cp settings.py_example settings.py
|
||||
```
|
||||
|
||||
Change `settings.py` to your liking. Then run the application:
|
||||
|
||||
```bash
|
||||
python3 run.py
|
||||
```
|
5
TODO
5
TODO
|
@ -1,5 +0,0 @@
|
|||
remove Constraints.client_secret before publishing and make a backup of it!!
|
||||
|
||||
Register new application new login.wownero.com
|
||||
client_id = yellwowpages
|
||||
client url = <domain>/authenticate
|
|
@ -1,8 +0,0 @@
|
|||
from starlette.templating import Jinja2Templates
|
||||
|
||||
|
||||
class Constraints:
|
||||
templates = Jinja2Templates(directory='frontend/templates')
|
||||
client_id = ''
|
||||
client_secret = ''
|
||||
uri = 'sqlite:///users.db'
|
|
@ -1,108 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>YellWOWPages - Sex and Drugs in the metaverse</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<link rel="stylesheet" href="../../static/colors.css">
|
||||
<link rel="stylesheet" href="../../static/icon.css">
|
||||
</head>
|
||||
<style>
|
||||
html, body{
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
overflow: hidden;
|
||||
}
|
||||
a{
|
||||
color: var(--yellow);
|
||||
}
|
||||
span{
|
||||
color: var(--purple);
|
||||
}
|
||||
.nav{
|
||||
border-color: var(--yellow);
|
||||
border: 2px;
|
||||
}
|
||||
#dropdown{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
#dropdowncontent{
|
||||
display: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
background-color: var(--table-border-color);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
padding: 30px 50px;
|
||||
color: var(--yellow);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
#dropdown:hover #dropdowncontent {
|
||||
display: block;
|
||||
}
|
||||
#main{
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
form{
|
||||
height: 50px;
|
||||
}
|
||||
#footer{
|
||||
height: 12vh;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
kbd{
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<div id="dropdown">
|
||||
<i class="icon icon-menu"></i>
|
||||
<div id="dropdowncontent">
|
||||
<p>
|
||||
<a href="/login">Login</a>
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<a href="/yellwowpage">Yell<span>WOW</span>Page</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/about/api">Api</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><strong><a href="/">Yell<span>WOW</span>Pages</a></strong></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://git.wownero.com/muchwowmining/YellWOWPages"><i class="icon icon-edit"></i></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div id="main">
|
||||
<h1>About - Api</h1>
|
||||
<p>
|
||||
Search user: <code><a href="/api/user/{username}" data-tooltip="no partial search yet">/api/user/{username}</a></code>
|
||||
<br><br>
|
||||
Get all users: <code><a href="/api/all">/api/all</a></code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
2022 - ... [the future is w0w]
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,125 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>YellWOWPages - Sex and Drugs in the metaverse</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<link rel="stylesheet" href="../../static/colors.css">
|
||||
<link rel="stylesheet" href="../../static/icon.css">
|
||||
</head>
|
||||
<style>
|
||||
html, body{
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
overflow: hidden;
|
||||
}
|
||||
a{
|
||||
color: var(--yellow);
|
||||
}
|
||||
span{
|
||||
color: var(--purple);
|
||||
}
|
||||
.nav{
|
||||
border-color: var(--yellow);
|
||||
border: 2px;
|
||||
}
|
||||
#dropdown{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
#dropdowncontent{
|
||||
display: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
background-color: var(--table-border-color);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
padding: 30px 50px;
|
||||
color: var(--yellow);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
#dropdown:hover #dropdowncontent {
|
||||
display: block;
|
||||
}
|
||||
#main{
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
form{
|
||||
height: 50px;
|
||||
}
|
||||
#footer{
|
||||
height: 12vh;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
kbd{
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<div id="dropdown">
|
||||
<i class="icon icon-menu"></i>
|
||||
<div id="dropdowncontent">
|
||||
<p>
|
||||
<a href="/login">Login</a>
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<a href="/yellwowpage">Yell<span>WOW</span>Page</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/about/api">Api</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><strong><a href="/">Yell<span>WOW</span>Pages</a></strong></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://git.wownero.com/muchwowmining/YellWOWPages"><i class="icon icon-edit"></i></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div id="main">
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
Search for any Wownero <em>sub-address</em> you want by username and pay
|
||||
the world!
|
||||
<br>
|
||||
This application uses <u>Wownero's Centralized Authentication Service.</u>
|
||||
</p>
|
||||
<p>
|
||||
Other Wownero related stuff:
|
||||
<br>
|
||||
<a href="https://wownero.org/">WebSite</a>
|
||||
<br>
|
||||
<a href="https://suchwow.xyz">SuchWow</a>
|
||||
<br>
|
||||
<a href="https://git.wownero.com">Official Git</a>
|
||||
<br>
|
||||
<a href="https://discord.com/invite/ykZyAzJhDK">Discord server</a>
|
||||
</p>
|
||||
<p>
|
||||
Idea of: dsc_
|
||||
<br>
|
||||
Made by <a href="https://notmtth.xyz">NotMtth</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
2022 - ... [the future is w0w]
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,100 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Such dashboard</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<link rel="stylesheet" href="../../static/colors.css">
|
||||
<link rel="stylesheet" href="../../static/icon.css">
|
||||
</head>
|
||||
<style>
|
||||
html, body{
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
overflow: hidden;
|
||||
}
|
||||
a{
|
||||
color: var(--yellow);
|
||||
}
|
||||
span{
|
||||
color: var(--purple);
|
||||
}
|
||||
.nav{
|
||||
border-color: var(--yellow);
|
||||
border: 2px;
|
||||
}
|
||||
#dropdown{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
#dropdowncontent{
|
||||
display: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
background-color: var(--table-border-color);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
padding: 30px 50px;
|
||||
color: var(--yellow);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
#dropdown:hover #dropdowncontent {
|
||||
display: block;
|
||||
}
|
||||
#main{
|
||||
width: 100%;
|
||||
height: 85vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
@media (max-width: 800px) {}
|
||||
</style>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<div id="dropdown">
|
||||
<i class="icon icon-menu"></i>
|
||||
<div id="dropdowncontent">
|
||||
<p>
|
||||
<a href="/login">Login</a>
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<a href="/yellwowpage">Yell<span>WOW</span>Page</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/about/api">Api</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><strong><a href="/">Yell<span>WOW</span>Pages</a></strong></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://git.wownero.com/muchwowmining/YellWOWPages"><i class="icon icon-edit"></i></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div id="main">
|
||||
<article>
|
||||
{% for username, address in user_data.items() %}
|
||||
<Header>Welcome back <em>{{username}}</em>!</Header>
|
||||
Current <u>sub-address</u>: <label><mark>{{address}}</mark></label>
|
||||
<footer>
|
||||
Change <u>sub-address</u>:
|
||||
<form action="/submit_address" method="POST">
|
||||
<input type="text" name="address">
|
||||
<button data-tooltip="Be sure it's correct">Submit</button>
|
||||
</form>
|
||||
</footer>
|
||||
{% endfor %}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,126 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>YellWOWPages - Sex and Drugs in the metaverse</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<link rel="stylesheet" href="../../static/colors.css">
|
||||
<link rel="stylesheet" href="../../static/icon.css">
|
||||
</head>
|
||||
<style>
|
||||
html, body{
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
a{
|
||||
color: var(--yellow);
|
||||
}
|
||||
span{
|
||||
color: var(--purple);
|
||||
}
|
||||
.nav{
|
||||
border-color: var(--yellow);
|
||||
border: 2px;
|
||||
}
|
||||
#dropdown{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
#dropdowncontent{
|
||||
display: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
background-color: var(--table-border-color);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
padding: 30px 50px;
|
||||
color: var(--yellow);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
#dropdown:hover #dropdowncontent {
|
||||
display: block;
|
||||
}
|
||||
#main{
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
form{
|
||||
height: 80px;
|
||||
}
|
||||
#addresses{
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#addresses::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
#footer{
|
||||
height: 12vh;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
kbd{
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<div id="dropdown">
|
||||
<i class="icon icon-menu"></i>
|
||||
<div id="dropdowncontent">
|
||||
<p>
|
||||
<a href="/login">Login</a>
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<a href="/yellwowpage">Yell<span>WOW</span>Page</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/about/api">Api</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><strong><a href="/">Yell<span>WOW</span>Pages</a></strong></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://git.wownero.com/muchwowmining/YellWOWPages"><i class="icon icon-edit"></i></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div id="main">
|
||||
<form action="/search" method="GET">
|
||||
<input type="text" name="username" placeholder="Username to search">
|
||||
<label for="switch">
|
||||
<input type="checkbox" name="switch" role="switch">
|
||||
<em data-tooltip="Search address with a part of username; get 3 results">Partial</em>
|
||||
</label>
|
||||
</form>
|
||||
<div id="addresses">
|
||||
{% for username, address in user_data.items() %}
|
||||
<article>
|
||||
<header>
|
||||
<em>{{username}}</em>
|
||||
</header>
|
||||
<kbd>{{address}}</kbd>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
2022 - ... [the future is w0w]
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,132 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>YellWOWPages - Sex and Drugs in the metaverse</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<link rel="stylesheet" href="../../static/colors.css">
|
||||
<link rel="stylesheet" href="../../static/icon.css">
|
||||
</head>
|
||||
<style>
|
||||
html, body{
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
overflow: hidden;
|
||||
}
|
||||
a{
|
||||
color: var(--yellow);
|
||||
}
|
||||
span{
|
||||
color: var(--purple);
|
||||
}
|
||||
.nav{
|
||||
border-color: var(--yellow);
|
||||
border: 2px;
|
||||
}
|
||||
#dropdown{
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
#dropdowncontent{
|
||||
display: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
background-color: var(--table-border-color);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
padding: 30px 50px;
|
||||
color: var(--yellow);
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
#dropdown:hover #dropdowncontent {
|
||||
display: block;
|
||||
}
|
||||
#main{
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
form{
|
||||
height: 80px;
|
||||
}
|
||||
#addresses{
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#addresses::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
#footer{
|
||||
height: 12vh;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
kbd{
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<div id="dropdown">
|
||||
<i class="icon icon-menu"></i>
|
||||
<div id="dropdowncontent">
|
||||
<p>
|
||||
<a href="/login">Login</a>
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<a href="/yellwowpage">Yell<span>WOW</span>Page</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/about/api">Api</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><strong><a href="/">Yell<span>WOW</span>Pages</a></strong></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://git.wownero.com/muchwowmining/YellWOWPages"><i class="icon icon-edit"></i></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div id="main">
|
||||
<form action="/search" method="GET">
|
||||
<input type="text" name="username" placeholder="Username to search">
|
||||
<label for="switch">
|
||||
<input type="checkbox" name="switch" role="switch">
|
||||
<em data-tooltip="Search address with a part of username; get 3 results">Partial</em>
|
||||
</label>
|
||||
</form>
|
||||
<br>
|
||||
Result: {{user_data|length}}
|
||||
{% if not user_data|length %}
|
||||
Nothing found...
|
||||
{% else %}
|
||||
<div id="addresses">
|
||||
{% for username, address in user_data.items() %}
|
||||
<article>
|
||||
<header>
|
||||
<em>{{username}}</em>
|
||||
</header>
|
||||
<kbd>{{address}}</kbd>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
2022 - ... [the future is w0w]
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
45
main.py
45
main.py
|
@ -1,45 +0,0 @@
|
|||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
import uvicorn
|
||||
|
||||
from classes.constraints import Constraints
|
||||
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
|
||||
app = FastAPI(docs_url=None, redoc_url=None)
|
||||
|
||||
app.mount('/static', StaticFiles(directory='frontend/static'), name='static')
|
||||
|
||||
from routers import auth, static, dashboard, db, api
|
||||
|
||||
app.include_router(auth.router)
|
||||
app.include_router(dashboard.router)
|
||||
app.include_router(static.router)
|
||||
app.include_router(api.router)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@app.get('/root', response_class=HTMLResponse)
|
||||
async def root(request: Request):
|
||||
return Constraints.templates.TemplateResponse('/root/index.html', {'request': request})
|
||||
|
||||
|
||||
# shitty error handling
|
||||
@app.exception_handler(StarletteHTTPException)
|
||||
async def http_exception_handler(request, exc):
|
||||
if exc.status_code == 404:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': 'not found...',
|
||||
'url': '/root'})
|
||||
elif exc.status_code == 500:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': 'internal server error',
|
||||
'url': '/root'})
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': exc.detail,
|
||||
'url': '/root'})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
uvicorn.run(app, host='127.0.0.1', port=8080)
|
|
@ -1,30 +1,3 @@
|
|||
anyio==3.5.0
|
||||
asgiref==3.5.0
|
||||
asttokens==2.0.5
|
||||
certifi==2021.10.8
|
||||
charset-normalizer==2.0.12
|
||||
click==8.0.4
|
||||
colorama==0.4.4
|
||||
executing==0.8.3
|
||||
fastapi==0.75.0
|
||||
Flask==2.0.3
|
||||
Flask-SQLAlchemy==2.5.1
|
||||
greenlet==1.1.2
|
||||
h11==0.13.0
|
||||
icecream==2.1.2
|
||||
idna==3.3
|
||||
itsdangerous==2.1.1
|
||||
Jinja2==3.0.3
|
||||
MarkupSafe==2.1.0
|
||||
pydantic==1.9.0
|
||||
Pygments==2.11.2
|
||||
python-multipart==0.0.5
|
||||
requests==2.27.1
|
||||
six==1.16.0
|
||||
sniffio==1.2.0
|
||||
SQLAlchemy==1.4.32
|
||||
starlette==0.17.1
|
||||
typing-extensions==4.1.1
|
||||
urllib3==1.26.8
|
||||
uvicorn==0.17.5
|
||||
Werkzeug==2.0.3
|
||||
peewee
|
||||
quart
|
||||
quart_session_openid
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
from fastapi import APIRouter, Request, Form
|
||||
from fastapi.responses import RedirectResponse, HTMLResponse
|
||||
from icecream import ic
|
||||
|
||||
from classes.constraints import Constraints
|
||||
from .db import Database
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
# external api
|
||||
@router.get('/api/user/{username}')
|
||||
async def get_api(request: Request, username: str):
|
||||
if not Database.Users.get_address(username):
|
||||
return {'error': 'invalid user'}
|
||||
return Database.Users.get_address(username)
|
||||
|
||||
|
||||
@router.get('/api/all')
|
||||
async def get_api_all(request: Request):
|
||||
return Database.Users.get_all()
|
||||
|
||||
|
||||
# site search redirect
|
||||
@router.get('/search')
|
||||
async def search_api(request: Request):
|
||||
username = request.query_params['username']
|
||||
ic(request.query_params.get('switch', None) == 'on')
|
||||
if request.query_params.get('switch', None) == 'on':
|
||||
return RedirectResponse(f'/yellwowpage/matches/{username}')
|
||||
else:
|
||||
return RedirectResponse(f'/yellwowpage/user/{username}')
|
|
@ -1,68 +0,0 @@
|
|||
from fastapi import APIRouter, Request, Cookie
|
||||
from fastapi.responses import RedirectResponse, HTMLResponse
|
||||
import requests
|
||||
from icecream import ic
|
||||
import secrets
|
||||
|
||||
from classes.constraints import Constraints
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('/login')
|
||||
async def get_login(request: Request):
|
||||
state = secrets.token_hex(10)
|
||||
response = RedirectResponse(
|
||||
'https://login.wownero.com/auth/realms/master/protocol/openid-connect/auth?'f'client_id='
|
||||
f'{Constraints.client_id}&redirect_uri=https://yellow.wownero.com/authenticate&'
|
||||
f'response_type=code&state={state}')
|
||||
response.set_cookie(key='state', value=state)
|
||||
return response
|
||||
|
||||
|
||||
@router.get('/authenticate')
|
||||
async def get_auth(request: Request, state: str = Cookie(None)):
|
||||
params = request.query_params
|
||||
if state is None:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html',
|
||||
{'request': request,
|
||||
'error': '`state` security code not found...',
|
||||
'url': '/login'})
|
||||
if params['state'] != state:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html',
|
||||
{'request': request,
|
||||
'error': '`state` security code is wrong',
|
||||
'url': '/login'})
|
||||
url = "https://login.wownero.com/auth/realms/master/protocol/openid-connect/token"
|
||||
data = {
|
||||
"grant_type": "authorization_code",
|
||||
"code": params["code"],
|
||||
"redirect_uri": "http://127.0.0.1:8080/authenticate",
|
||||
"client_id": f'{Constraints.client_id}',
|
||||
"client_secret": f'{Constraints.client_secret}',
|
||||
"state": params['state']
|
||||
}
|
||||
r = requests.post(url=url, data=data)
|
||||
response = r.json()
|
||||
|
||||
if response.get('error', None) is not None:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html',
|
||||
{'request': request, 'error': r.json()['error_description'],
|
||||
'url': '/login'})
|
||||
auth_code = response.get('access_token', None)
|
||||
|
||||
if auth_code is None:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html',
|
||||
{'request': request, 'error': 'invalid auth code',
|
||||
'url': '/login'})
|
||||
response = RedirectResponse('/dashboard')
|
||||
response.set_cookie(key='auth_code', value=auth_code)
|
||||
response.delete_cookie(key='state')
|
||||
return response
|
||||
|
||||
|
||||
@router.get('/logout')
|
||||
async def get_logout():
|
||||
response = RedirectResponse('/root')
|
||||
response.delete_cookie('auth_code')
|
||||
return response
|
|
@ -1,63 +0,0 @@
|
|||
from fastapi import APIRouter, Request, Cookie, Form
|
||||
from fastapi.responses import RedirectResponse, HTMLResponse
|
||||
import requests
|
||||
from icecream import ic
|
||||
|
||||
from classes.constraints import Constraints
|
||||
from .db import Database
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('/dashboard', response_class=HTMLResponse)
|
||||
async def get_dashboard(request: Request, auth_code: str = Cookie(None)):
|
||||
if auth_code is None:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': 'not logged in',
|
||||
'url': '/login'})
|
||||
|
||||
url = "https://login.wownero.com/auth/realms/master/protocol/openid-connect/userinfo"
|
||||
response = requests.post(url, headers={"Authorization": f"Bearer {auth_code}"})
|
||||
user_profile = response.json()
|
||||
|
||||
if user_profile.get('preferred_username', None) is None:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': 'account not found...',
|
||||
'url': '/login'})
|
||||
user_name = user_profile.get('preferred_username', None)
|
||||
return Constraints.templates.TemplateResponse('/dashboard/index.html', {'request': request,
|
||||
'user_data': Database.Users.get_address(user_name)})
|
||||
|
||||
|
||||
@router.post('/submit_address')
|
||||
async def post_submit_address(request: Request, auth_code: str = Cookie(None), address: str = Form(None)):
|
||||
if auth_code is None:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': 'not logged in',
|
||||
'url': '/login'})
|
||||
if address is None:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': 'invalid address',
|
||||
'url': '/dashboard'})
|
||||
|
||||
if len(address) != 97:
|
||||
return Constraints.templates.TemplateResponse('/errors/index.html', {'request': request,
|
||||
'error': 'invalid address length',
|
||||
'url': '/dashboard'})
|
||||
|
||||
url = "https://login.wownero.com/auth/realms/master/protocol/openid-connect/userinfo"
|
||||
response = requests.post(url, headers={"Authorization": f"Bearer {auth_code}"})
|
||||
user_name = response.json().get('preferred_username', None)
|
||||
|
||||
if not Database.Users.get_address(user_name):
|
||||
new_user = Database.Users(username=user_name, address=address)
|
||||
Database.sqla.session.add(new_user)
|
||||
Database.sqla.session.commit()
|
||||
return RedirectResponse('/dashboard', status_code=303)
|
||||
|
||||
update_address = Database.Users.query.filter_by(username=user_name).first()
|
||||
update_address.address = address
|
||||
Database.sqla.session.commit()
|
||||
return RedirectResponse('/dashboard', status_code=303)
|
||||
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
from icecream import install, ic
|
||||
|
||||
from classes.constraints import Constraints
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = Constraints.uri
|
||||
db = SQLAlchemy(app, session_options={'autocommit': False})
|
||||
|
||||
|
||||
class Users(db.Model):
|
||||
__tablename__ = 'wowusers'
|
||||
username = db.Column(db.VARCHAR(32), primary_key=True)
|
||||
address = db.Column(db.CHAR(97), nullable=False)
|
||||
|
||||
@staticmethod
|
||||
def get_address(username):
|
||||
user_data = {}
|
||||
try:
|
||||
user_data.update({Users.query.filter_by(username=username).first().username:
|
||||
Users.query.filter_by(username=username).first().address})
|
||||
except AttributeError:
|
||||
return user_data
|
||||
return user_data
|
||||
|
||||
@staticmethod
|
||||
def get_all():
|
||||
users_data = {}
|
||||
for user in Users.query.all():
|
||||
users_data.update({user.username: user.address})
|
||||
return users_data
|
||||
|
||||
@staticmethod
|
||||
def get_matches(username):
|
||||
user_data = {}
|
||||
try:
|
||||
for i in range(3):
|
||||
user = Users.query.filter(Database.Users.username.like(f'%{username}%'))[i]
|
||||
user_data.update({user.username: user.address})
|
||||
except IndexError:
|
||||
return user_data
|
||||
return user_data
|
||||
|
||||
|
||||
class Database:
|
||||
Users = Users
|
||||
sqla = db
|
||||
|
||||
|
||||
db.create_all()
|
||||
ic('db done')
|
|
@ -1,2 +0,0 @@
|
|||
# stupid fastapi error handling drove me crazy for some hours, updating this asap
|
||||
# the handler is in the main file
|
|
@ -1,39 +0,0 @@
|
|||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import RedirectResponse, HTMLResponse
|
||||
from icecream import ic
|
||||
|
||||
from classes.constraints import Constraints
|
||||
from .db import Database
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('/yellwowpage')
|
||||
async def get_yellwowpage(request: Request):
|
||||
return Constraints.templates.TemplateResponse('/yellwow/index.html',
|
||||
{'request': request, 'user_data': Database.Users.get_all()})
|
||||
|
||||
|
||||
@router.get('/yellwowpage/user/{username}')
|
||||
async def get_yellwowpage(request: Request, username: str):
|
||||
return Constraints.templates.TemplateResponse('/yellwow/single_user/index.html',
|
||||
{'request': request,
|
||||
'user_data': Database.Users.get_address(username)})
|
||||
|
||||
|
||||
@router.get('/yellwowpage/matches/{username}')
|
||||
async def get_yellwowpage_matches(request: Request, username: str):
|
||||
ic(Database.Users.get_matches(username))
|
||||
return Constraints.templates.TemplateResponse('/yellwow/single_user/index.html',
|
||||
{'request': request,
|
||||
'user_data': Database.Users.get_matches(username)})
|
||||
|
||||
|
||||
@router.get('/about')
|
||||
async def get_about(request: Request):
|
||||
return Constraints.templates.TemplateResponse('/about/index.html', {'request': request})
|
||||
|
||||
|
||||
@router.get('/about/api')
|
||||
async def get_api_about(request: Request):
|
||||
return Constraints.templates.TemplateResponse('/about/api/index.html', {'request': request})
|
5
run.py
Normal file
5
run.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from yellow.factory import create_app
|
||||
import settings
|
||||
|
||||
app = create_app()
|
||||
app.run(settings.HOST, port=settings.PORT, debug=settings.DEBUG, use_reloader=False)
|
23
settings.py_example
Normal file
23
settings.py_example
Normal file
|
@ -0,0 +1,23 @@
|
|||
import os
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
def bool_env(val):
|
||||
return val is True or (isinstance(val, str) and (val.lower() == 'true' or val == '1'))
|
||||
|
||||
|
||||
DEBUG = bool_env(os.environ.get("DEBUG", False))
|
||||
HOST = os.environ.get("HOST", "127.0.0.1")
|
||||
PORT = int(os.environ.get("PORT", 8080))
|
||||
APP_SECRET = os.environ.get("APP_SECRET")
|
||||
|
||||
REDIS_URI = os.environ.get("REDIS_URI", "redis://localhost:6379")
|
||||
|
||||
DB_PATH = os.path.join(cwd, "data", "db.sqlite3")
|
||||
|
||||
OPENID_CFG = {
|
||||
"client_id": "",
|
||||
"client_secret": "",
|
||||
"configuration": "https://login.wownero.com/auth/realms/master/.well-known/openid-configuration"
|
||||
}
|
||||
|
13
yellow/__init__.py
Normal file
13
yellow/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from quart import session, abort
|
||||
|
||||
from functools import wraps
|
||||
|
||||
|
||||
def login_required(func):
|
||||
@wraps(func)
|
||||
async def wrapper(*args, **kwargs):
|
||||
user = session.get('user')
|
||||
if not isinstance(user, dict):
|
||||
abort(403)
|
||||
return await func(*args, **kwargs)
|
||||
return wrapper
|
25
yellow/api.py
Normal file
25
yellow/api.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from quart import render_template, request, redirect, url_for, jsonify, Blueprint, abort, flash, send_from_directory, current_app
|
||||
|
||||
import settings
|
||||
from yellow.models import User
|
||||
|
||||
bp_api = Blueprint('bp_api', __name__, url_prefix='/api')
|
||||
|
||||
|
||||
@bp_api.get("/")
|
||||
async def api_root():
|
||||
return await render_template('api.html')
|
||||
|
||||
|
||||
@bp_api.get('/user/')
|
||||
async def api_all():
|
||||
return jsonify([u.to_json(ignore_key='id') for u in User.select()])
|
||||
|
||||
|
||||
@bp_api.get('/user/<path:needle>')
|
||||
async def api_search(needle: str):
|
||||
try:
|
||||
return jsonify([u.to_json(ignore_key='id') for u in await User.search(needle)])
|
||||
except Exception as ex:
|
||||
current_app.logger.error(ex)
|
||||
return jsonify([])
|
28
yellow/auth.py
Normal file
28
yellow/auth.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import peewee
|
||||
from quart import session, redirect, url_for
|
||||
|
||||
from yellow.factory import openid
|
||||
from yellow.models import User
|
||||
|
||||
|
||||
@openid.after_token()
|
||||
async def handle_user_login(resp: dict):
|
||||
access_token = resp["access_token"]
|
||||
openid.verify_token(access_token)
|
||||
|
||||
user = await openid.user_info(access_token)
|
||||
username = user['preferred_username']
|
||||
uid = user['sub']
|
||||
|
||||
try:
|
||||
user = User.select().where(User.id == uid).get()
|
||||
except peewee.DoesNotExist:
|
||||
user = None
|
||||
|
||||
if not user:
|
||||
# create new user if it does not exist yet
|
||||
user = User.create(id=uid, username=username)
|
||||
|
||||
# user is now logged in
|
||||
session['user'] = user.to_json()
|
||||
return redirect(url_for('bp_routes.root'))
|
80
yellow/factory.py
Normal file
80
yellow/factory.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
import os
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from quart import Quart, url_for, jsonify, render_template, session
|
||||
from quart_session_openid import OpenID
|
||||
from quart_session import Session
|
||||
import settings
|
||||
|
||||
|
||||
app: Quart = None
|
||||
peewee = None
|
||||
cache = None
|
||||
openid: OpenID = None
|
||||
|
||||
|
||||
async def _setup_database(app: Quart):
|
||||
import peewee
|
||||
import yellow.models
|
||||
models = peewee.Model.__subclasses__()
|
||||
for m in models:
|
||||
m.create_table()
|
||||
|
||||
|
||||
async def _setup_openid(app: Quart):
|
||||
global openid
|
||||
openid = OpenID(app, **settings.OPENID_CFG)
|
||||
from yellow.auth import handle_user_login
|
||||
|
||||
|
||||
async def _setup_cache(app: Quart):
|
||||
global cache
|
||||
app.config['SESSION_TYPE'] = 'redis'
|
||||
app.config['SESSION_URI'] = settings.REDIS_URI
|
||||
Session(app)
|
||||
|
||||
|
||||
async def _setup_error_handlers(app: Quart):
|
||||
@app.errorhandler(500)
|
||||
async def page_error(e):
|
||||
return await render_template('error.html', code=500, msg="Error occurred"), 500
|
||||
|
||||
@app.errorhandler(403)
|
||||
async def page_forbidden(e):
|
||||
return await render_template('error.html', code=403, msg="Forbidden"), 403
|
||||
|
||||
@app.errorhandler(404)
|
||||
async def page_not_found(e):
|
||||
return await render_template('error.html', code=404, msg="Page not found"), 404
|
||||
|
||||
|
||||
def create_app():
|
||||
global app
|
||||
app = Quart(__name__)
|
||||
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.secret_key = settings.APP_SECRET
|
||||
|
||||
@app.context_processor
|
||||
def template_variables():
|
||||
global openid
|
||||
from yellow.models import User
|
||||
current_user = session.get('user')
|
||||
if current_user:
|
||||
current_user = User(**current_user)
|
||||
return dict(user=current_user, url_login=openid.endpoint_name_login)
|
||||
|
||||
@app.before_serving
|
||||
async def startup():
|
||||
await _setup_cache(app)
|
||||
await _setup_openid(app)
|
||||
await _setup_database(app)
|
||||
await _setup_error_handlers(app)
|
||||
|
||||
from yellow.routes import bp_routes
|
||||
from yellow.api import bp_api
|
||||
app.register_blueprint(bp_routes)
|
||||
app.register_blueprint(bp_api)
|
||||
|
||||
return app
|
37
yellow/models.py
Normal file
37
yellow/models.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
import os, re, random
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
from peewee import SqliteDatabase, SQL, ForeignKeyField
|
||||
import peewee as pw
|
||||
|
||||
import settings
|
||||
|
||||
db = SqliteDatabase(settings.DB_PATH)
|
||||
|
||||
|
||||
class User(pw.Model):
|
||||
id = pw.UUIDField(primary_key=True)
|
||||
created = pw.DateTimeField(default=datetime.now)
|
||||
username = pw.CharField(unique=True, null=False)
|
||||
address = pw.CharField(null=True)
|
||||
|
||||
@staticmethod
|
||||
async def search(needle) -> List['User']:
|
||||
needle = needle.replace("*", "")
|
||||
if len(needle) <= 2:
|
||||
raise Exception("need longer search term")
|
||||
return User.select().where(User.username % f"*{needle}*")
|
||||
|
||||
def to_json(self, ignore_key=None):
|
||||
data = {
|
||||
"id": self.id,
|
||||
"username": self.username,
|
||||
"address": self.address
|
||||
}
|
||||
if isinstance(ignore_key, str):
|
||||
data.pop(ignore_key)
|
||||
return data
|
||||
|
||||
class Meta:
|
||||
database = db
|
69
yellow/routes.py
Normal file
69
yellow/routes.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
from quart import render_template, request, redirect, url_for, jsonify, Blueprint, abort, flash, send_from_directory, session
|
||||
|
||||
from yellow import login_required
|
||||
from yellow.factory import openid
|
||||
from yellow.models import User
|
||||
|
||||
bp_routes = Blueprint('bp_routes', __name__)
|
||||
|
||||
|
||||
@bp_routes.get("/")
|
||||
async def root():
|
||||
return await render_template('index.html')
|
||||
|
||||
|
||||
@bp_routes.route("/login")
|
||||
async def login():
|
||||
return redirect(url_for(openid.endpoint_name_login))
|
||||
|
||||
|
||||
@bp_routes.route("/logout")
|
||||
@login_required
|
||||
async def logout():
|
||||
session['user'] = None
|
||||
return redirect(url_for('bp_routes.root'))
|
||||
|
||||
|
||||
@bp_routes.route("/dashboard")
|
||||
@login_required
|
||||
async def dashboard():
|
||||
return await render_template('dashboard.html')
|
||||
|
||||
|
||||
@bp_routes.post("/dashboard/address")
|
||||
@login_required
|
||||
async def dashboard_address_post():
|
||||
# get FORM POST value 'address'
|
||||
form = await request.form
|
||||
address = form.get('address')
|
||||
if len(address) != 97:
|
||||
raise Exception("Please submit a WOW address")
|
||||
|
||||
# update user
|
||||
from yellow.models import User
|
||||
user = User.select().filter(User.id == session['user']['id']).get()
|
||||
user.address = address
|
||||
user.save()
|
||||
session['user'] = user.to_json()
|
||||
|
||||
return await render_template('dashboard.html')
|
||||
|
||||
|
||||
@bp_routes.route("/search")
|
||||
async def search():
|
||||
needle = request.args.get('username')
|
||||
if needle:
|
||||
if len(needle) <= 2:
|
||||
raise Exception("Search term needs to be longer")
|
||||
|
||||
users = [u for u in await User.search(needle)]
|
||||
if users:
|
||||
return await render_template('search_results.html', users=users)
|
||||
|
||||
users = [u for u in User.select()]
|
||||
return await render_template('search.html', users=users)
|
||||
|
||||
|
||||
@bp_routes.route("/about")
|
||||
async def about():
|
||||
return await render_template('about.html')
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
49
yellow/templates/about.html
Normal file
49
yellow/templates/about.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div style="display:none">
|
||||
{% block title %}YellWOWPages - About{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
Search for any Wownero <em>address</em> you want by username and pay
|
||||
the world!
|
||||
<br>
|
||||
This application uses <u>Wownero's Centralized Authentication Service.</u>
|
||||
</p>
|
||||
<p>
|
||||
Other Wownero related stuff:
|
||||
<br>
|
||||
<a href="https://wownero.org/">WebSite</a>
|
||||
<br>
|
||||
<a href="https://suchwow.xyz">SuchWow</a>
|
||||
<br>
|
||||
<a href="https://git.wownero.com">Official Git</a>
|
||||
<br>
|
||||
<a href="https://discord.com/invite/ykZyAzJhDK">Discord server</a>
|
||||
</p>
|
||||
<p>
|
||||
Made by <a href="https://notmtth.xyz">NotMtth</a> and <code>dsc</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#main{
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
form{
|
||||
height: 50px;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
kbd{
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
36
yellow/templates/api.html
Normal file
36
yellow/templates/api.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div style="display:none">
|
||||
{% block title %}YellWOWPages - API{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<h1>API</h1>
|
||||
<p>
|
||||
Search user: <code><a href="/api/user/dsc" data-tooltip="partial search supported">/api/user/{username}</a></code>
|
||||
<br><br>
|
||||
Get all users: <code><a href="/api/user/">/api/user/</a></code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#main {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
form {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
kbd {
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
|
@ -4,10 +4,11 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>YellWOWPages - Sex and Drugs in the metaverse</title>
|
||||
<title>{% block title %}YellWOWPages - Sex and Drugs in the metaverse{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<link rel="stylesheet" href="../../static/colors.css">
|
||||
<link rel="stylesheet" href="../../static/icon.css">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='colors.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='icon.css') }}">
|
||||
</head>
|
||||
<style>
|
||||
html, body{
|
||||
|
@ -69,42 +70,14 @@
|
|||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<div id="dropdown">
|
||||
<i class="icon icon-menu"></i>
|
||||
<div id="dropdowncontent">
|
||||
<p>
|
||||
<a href="/login">Login</a>
|
||||
<a href="/dashboard">Dashboard</a>
|
||||
<a href="/yellwowpage">Yell<span>WOW</span>Page</a>
|
||||
<a href="/about">About</a>
|
||||
<a href="/about/api">Api</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://git.wownero.com/muchwowmining/YellWOWPages"><i class="icon icon-edit"></i></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div id="main">
|
||||
<main>
|
||||
<strong>Yell<span>WOW</span>Pages</strong>
|
||||
</main>
|
||||
<div>
|
||||
The first <img src="../../static/wownero.png" alt=""> addresses library -
|
||||
from the community to the community
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
2022 - ... [the future is w0w]
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
{% include 'includes/nav.html' %}
|
||||
|
||||
{% block content %} {% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
2022 - ... [the future is w0w]
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
40
yellow/templates/dashboard.html
Normal file
40
yellow/templates/dashboard.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div style="display:none">
|
||||
{% block title %}YellWOWPages - Dashboard{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<article>
|
||||
<Header>Welcome back <em>{{user.username}}</em>!</Header>
|
||||
Current <u>WOW address</u>: <label>
|
||||
{% if user.address %}
|
||||
<mark>{{user.address}}</mark>
|
||||
{% else %}
|
||||
<mark>empty</mark>
|
||||
{% endif %}
|
||||
</label>
|
||||
<footer>
|
||||
Change <u>WOW address</u>:
|
||||
<form action="{{ url_for('bp_routes.dashboard_address_post') }}" method="POST">
|
||||
<input type="text" name="address">
|
||||
<button data-tooltip="Be sure it's correct">Submit</button>
|
||||
</form>
|
||||
</footer>
|
||||
|
||||
</article>
|
||||
</div>
|
||||
<style>
|
||||
|
||||
#main {
|
||||
width: 100%;
|
||||
height: 85vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
|
@ -5,7 +5,7 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="refresh" content="3; URL={{url}}">
|
||||
<title>Such error :(</title>
|
||||
<title>Such {{code}} error :(</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
<link rel="stylesheet" href="../../static/colors.css">
|
||||
<link rel="stylesheet" href="../../static/icon.css">
|
||||
|
@ -23,7 +23,7 @@
|
|||
<body>
|
||||
<div class="container-fluid">
|
||||
<div id="main">
|
||||
<p>Error: {{error}}</p>
|
||||
<p>Error {{code}}: {{msg}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
25
yellow/templates/includes/nav.html
Normal file
25
yellow/templates/includes/nav.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<div id="dropdown">
|
||||
<i class="icon icon-menu"></i>
|
||||
<div id="dropdowncontent">
|
||||
<p>
|
||||
{% if not user %}
|
||||
<a href="{{ url_for('bp_routes.login') }}">Login</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('bp_routes.logout') }}">Logout</a>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('bp_routes.dashboard') }}">Dashboard</a>
|
||||
<a href="{{ url_for('bp_routes.search') }}">Yell<span>WOW</span>Page search</a>
|
||||
<a href="{{ url_for('bp_routes.about') }}">About</a>
|
||||
<a href="{{ url_for('bp_api.api_root') }}">Api</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="https://git.wownero.com/muchwowmining/YellWOWPages"><i class="icon icon-edit"></i></a></li>
|
||||
</ul>
|
||||
</nav>
|
17
yellow/templates/index.html
Normal file
17
yellow/templates/index.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div style="display:none">
|
||||
{% block title %}YellWOWPages - Sex and Drugs in the metaverse{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<main>
|
||||
<strong>Yell<span>WOW</span>Pages</strong>
|
||||
</main>
|
||||
<div>
|
||||
The first <img src="../../static/wownero.png" alt=""> addresses library -
|
||||
from the community to the community
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
59
yellow/templates/search.html
Normal file
59
yellow/templates/search.html
Normal file
|
@ -0,0 +1,59 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div style="display:none">
|
||||
{% block title %}YellWOWPages - Yellwow{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<form action="{{ url_for('bp_routes.search') }}" method="GET">
|
||||
<input type="text" name="username" placeholder="Search for an username...">
|
||||
</form>
|
||||
|
||||
<div id="addresses">
|
||||
{% for user in users %}
|
||||
<article>
|
||||
<header>
|
||||
<em>{{user.username}}</em>
|
||||
</header>
|
||||
<kbd>{{user.address}}</kbd>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#main {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
form {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
#addresses {
|
||||
width: 100%;
|
||||
height: 54vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#addresses::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#footer {
|
||||
height: 12vh;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
kbd {
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
60
yellow/templates/search_results.html
Normal file
60
yellow/templates/search_results.html
Normal file
|
@ -0,0 +1,60 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div style="display:none">
|
||||
{% block title %}YellWOWPages - User{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="main">
|
||||
<form action="{{ url_for('bp_routes.search') }}" method="GET">
|
||||
<input type="text" name="username" placeholder="Username to search">
|
||||
</form>
|
||||
<br>
|
||||
Result(s): {{users|length}}
|
||||
|
||||
{% if not users %}
|
||||
Nothing found...
|
||||
{% else %}
|
||||
<div id="addresses">
|
||||
{% for user in users %}
|
||||
<article>
|
||||
<header>
|
||||
<em>{{user.username}}</em>
|
||||
</header>
|
||||
<kbd>{{user.address}}</kbd>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#main{
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
form{
|
||||
height: 80px;
|
||||
}
|
||||
#addresses{
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#addresses::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
#footer{
|
||||
height: 12vh;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
kbd{
|
||||
width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in a new issue