Initial Commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.venv/
|
||||
__pycache__/
|
||||
helpers/
|
||||
config.json
|
||||
192
app.py
Normal file
192
app.py
Normal file
@@ -0,0 +1,192 @@
|
||||
import json
|
||||
from hashlib import scrypt
|
||||
from os import urandom
|
||||
from hmac import compare_digest
|
||||
|
||||
from flask import Flask, render_template, request, redirect, url_for, session, abort
|
||||
from flask_pymongo import PyMongo
|
||||
from bson import ObjectId
|
||||
import bson.json_util as bson
|
||||
|
||||
SCRYPT_PARAMS = {"n": 16384, "r": 8, "p": 1}
|
||||
|
||||
app = Flask(__name__)
|
||||
with open("./config.json", "r", encoding="utf-8") as file:
|
||||
config = json.load(file)
|
||||
app.config["MONGO_URI"] = f"mongodb://{config['db']}/akasha"
|
||||
app.config["SECRET_KEY"] = config["fsk"]
|
||||
mongo = PyMongo(
|
||||
app,
|
||||
username="index",
|
||||
password=config["key"],
|
||||
authSource="akasha",
|
||||
authMechanism="SCRAM-SHA-256",
|
||||
)
|
||||
del config
|
||||
if mongo.db is None:
|
||||
raise Exception("Unable to connect to database.")
|
||||
db = mongo.db
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def inject_data():
|
||||
ddata = db.domains.find_one({"id": request.endpoint})
|
||||
return dict(hue=ddata["quad"])
|
||||
|
||||
|
||||
@app.before_request
|
||||
def apply_checks():
|
||||
username = session.get("username")
|
||||
if request.endpoint is "static":
|
||||
return
|
||||
ddata = db.domains.find_one_or_404({"id": request.endpoint})
|
||||
udata = db.users.find_one({"id": username}) if username else None
|
||||
if not can_access(ddata, udata):
|
||||
abort(401)
|
||||
|
||||
|
||||
def can_access(ddata, udata) -> bool:
|
||||
if udata:
|
||||
if ddata["quad"] not in udata["quad"]:
|
||||
return False
|
||||
if (not ddata["public"]) and ddata["id"] not in udata["perm"]:
|
||||
return False
|
||||
else:
|
||||
if ddata["quad"] != "ade":
|
||||
return False
|
||||
if not ddata["public"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
db.welcomes.find_one({"id": "index_ade"})
|
||||
return redirect(url_for("index_ade"))
|
||||
|
||||
|
||||
@app.route("/ade")
|
||||
def index_ade():
|
||||
return render_main()
|
||||
|
||||
|
||||
@app.route("/bea")
|
||||
def index_bea():
|
||||
return render_main()
|
||||
|
||||
|
||||
@app.route("/cam")
|
||||
def index_cam():
|
||||
return render_main()
|
||||
|
||||
|
||||
@app.route("/des")
|
||||
def index_des():
|
||||
return render_main()
|
||||
|
||||
|
||||
def render_main():
|
||||
session["main"] = request.endpoint
|
||||
wdata = db.welcomes.find_one({"id": request.endpoint})
|
||||
dsdata = db.domains.find({"cat": {"$ne": None}})
|
||||
udata = (
|
||||
db.users.find_one({"id": session["username"]}) if session["username"] else None
|
||||
)
|
||||
results = {"ade": {}, "bea": {}, "cam": {}, "des": {}}
|
||||
for ddata in dsdata:
|
||||
if not can_access(ddata, udata):
|
||||
continue
|
||||
if ddata["cat"] not in results[ddata["quad"]]:
|
||||
results[ddata["quad"]][ddata["cat"]] = {}
|
||||
results[ddata["quad"]][ddata["cat"]][ddata["name"]] = ddata["id"]
|
||||
return render_template(
|
||||
"index.html",
|
||||
wdata=wdata,
|
||||
domains=results,
|
||||
quad=(request.endpoint or "ade")[-3:],
|
||||
)
|
||||
|
||||
|
||||
@app.post("/login")
|
||||
def login():
|
||||
username = request.form.get("username")
|
||||
if username:
|
||||
password = request.form.get("password", "")
|
||||
udata = db.users.find_one({"id": username})
|
||||
if not udata:
|
||||
abort(401)
|
||||
return
|
||||
hash = udata["hash"]
|
||||
salt = udata["salt"]
|
||||
computed = scrypt(password.encode("utf-8"), salt=salt, **SCRYPT_PARAMS)
|
||||
if not compare_digest(hash, computed):
|
||||
abort(401)
|
||||
return
|
||||
session["username"] = username
|
||||
else:
|
||||
session["username"] = None
|
||||
return redirect(url_for(session.get("main", "index_ade")))
|
||||
|
||||
|
||||
@app.route("/search")
|
||||
def search():
|
||||
stype = request.args.get("search-type")
|
||||
query = request.args.get("search")
|
||||
print(stype, query)
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/database", methods=["GET", "POST"])
|
||||
def database():
|
||||
cnames = db.list_collection_names()
|
||||
results = []
|
||||
collection = None
|
||||
if request.method == "POST":
|
||||
collection = request.form.get("collection")
|
||||
if not collection:
|
||||
abort(400)
|
||||
query = request.form.get("query")
|
||||
if not query or not query.strip():
|
||||
query = "{}"
|
||||
try:
|
||||
query = json.loads(query)
|
||||
except:
|
||||
abort(400)
|
||||
results = db[collection].find(query, {"_id": 1, "id": 1})
|
||||
return render_template(
|
||||
"database.html", cnames=cnames, collection=collection, results=results
|
||||
)
|
||||
|
||||
|
||||
@app.route("/database/<collection>/<oid>", methods=["GET", "POST"])
|
||||
def database_edit(collection, oid):
|
||||
if request.method == "POST":
|
||||
document = request.form.get("document")
|
||||
if not document:
|
||||
abort(400)
|
||||
document = bson.loads(document)
|
||||
if document and "_id" in document:
|
||||
del document["_id"]
|
||||
try:
|
||||
if not document:
|
||||
db[collection].delete_one({"_id": ObjectId(oid)})
|
||||
print("DELETED")
|
||||
elif oid:
|
||||
db[collection].replace_one({"_id": ObjectId(oid)}, document)
|
||||
print("REPLACED")
|
||||
else:
|
||||
db[collection].insert_one(document)
|
||||
print("INSERTED")
|
||||
except:
|
||||
abort(500)
|
||||
return redirect(url_for("database"))
|
||||
result = db[collection].find_one_or_404({"_id": ObjectId(oid)})
|
||||
name = result["id"]
|
||||
document = bson.dumps(result, indent=4)
|
||||
return render_template(
|
||||
"database_edit.html",
|
||||
collection=collection,
|
||||
oid=oid,
|
||||
name=name,
|
||||
document=document,
|
||||
)
|
||||
47
static/database.css
Normal file
47
static/database.css
Normal file
@@ -0,0 +1,47 @@
|
||||
main {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.25em;
|
||||
margin: 0 2.5em;
|
||||
align-items: end;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 1;
|
||||
|
||||
#query,
|
||||
#object {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#query-box,
|
||||
#object-box {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#doc-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#document {
|
||||
text-wrap: nowrap;
|
||||
margin: 0 0.5em 0.5em 0.5em;
|
||||
flex-grow: 1;
|
||||
height: 80%;
|
||||
font-family: monospace, monospace;
|
||||
}
|
||||
10
static/icon.svg
Normal file
10
static/icon.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg version="1.1"
|
||||
width="42" height="42"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<polyline points="21,42 31,32 21,22 11,32"/>
|
||||
<polyline points="32,31 42,21 32,11 22,21"/>
|
||||
<polyline points="21,20 31,10 21,0 11,10"/>
|
||||
<polyline points="10,31 20,21 10,11 0,21"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 290 B |
22
static/icon_long.svg
Normal file
22
static/icon_long.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<svg version="1.1"
|
||||
width="196" height="46"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<polyline points="23,46 173,46 196,23 173,0 23,0 0,23" fill="#FFFFFF" class="icon-bg"/>
|
||||
<polyline points="23,44 33,34 23,24 13,34" fill="#000000"/>
|
||||
<polyline points="34,33 44,23 34,13 24,23" fill="#000000"/>
|
||||
<polyline points="23,22 33,12 23,2 13,12" fill="#000000"/>
|
||||
<polyline points="12,33 22,23 12,13 2,23" fill="#000000"/>
|
||||
<text x="54" y="22" fill="#000000">INDEX</text>
|
||||
|
||||
<style>
|
||||
<![CDATA[
|
||||
text {
|
||||
dominant-baseline: central;
|
||||
letter-spacing: 0.5ch;
|
||||
font: 28px Arial, sans-serif;
|
||||
}
|
||||
]]>
|
||||
</style>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 669 B |
153
static/index.css
Normal file
153
static/index.css
Normal file
@@ -0,0 +1,153 @@
|
||||
#outer {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
nav#sidebar {
|
||||
flex-basis: calc(196px + 1em);
|
||||
background-color: var(--color-bg1);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
div {
|
||||
background-color: var(--color-bg2);
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 1em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
flex-grow: 1;
|
||||
margin: 0 2.5em;
|
||||
}
|
||||
|
||||
nav#sector {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1em;
|
||||
|
||||
>a {
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
background-color: var(--color-bg1);
|
||||
color: var(--color);
|
||||
text-align: center;
|
||||
margin-left: 1lh;
|
||||
font-weight: bold;
|
||||
font-size: 1.5em;
|
||||
height: 1lh;
|
||||
}
|
||||
|
||||
>a::before {
|
||||
position: absolute;
|
||||
left: -1lh;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 1lh;
|
||||
aspect-ratio: 1/1;
|
||||
background-color: var(--color-bg1);
|
||||
clip-path: polygon(100% 0, 100% 100%, 0 100%);
|
||||
transform: scale(1.005);
|
||||
}
|
||||
}
|
||||
|
||||
hr#sectorbar {
|
||||
border: none;
|
||||
height: 1em;
|
||||
background-color: var(--color-bg1);
|
||||
border-bottom: 1px solid var(--color);
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
#contents {
|
||||
background-color: var(--color-bg2);
|
||||
}
|
||||
|
||||
#main-split {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5em;
|
||||
|
||||
>div {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
|
||||
>div {
|
||||
border: 1px solid var(--color);
|
||||
background-color: var(--color-bg2);
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: justify;
|
||||
padding: 0 0.25em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
i {
|
||||
display: inline-block;
|
||||
padding: 0 0.25em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
}
|
||||
|
||||
#other>div {
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
|
||||
#news>h2 {
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
#welcome>p {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
|
||||
#login>form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 0.25em;
|
||||
gap: 0.25em;
|
||||
|
||||
label {
|
||||
margin-bottom: -0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
#main-split {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#outer {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
margin-top: 2em;
|
||||
}
|
||||
}
|
||||
21
static/static.svg
Normal file
21
static/static.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
xmlns:xlink='http://www.w3.org/1999/xlink'
|
||||
width='300' height='300'>
|
||||
|
||||
<filter id='n' x='0' y='0'>
|
||||
<feTurbulence
|
||||
type="fractalNoise"
|
||||
baseFrequency="0.35"
|
||||
stitchTiles="stitch"/>
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="0.3 0.3 0.3 0 0
|
||||
0.3 0.3 0.3 0 0
|
||||
0.3 0.3 0.3 0 0
|
||||
0 0 0 1 0" />
|
||||
</filter>
|
||||
|
||||
<rect width='300' height='300' fill='#fff'/>
|
||||
<rect width='300' height='300' filter="url(#n)" opacity='1.0'/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 634 B |
202
static/style.css
Normal file
202
static/style.css
Normal file
@@ -0,0 +1,202 @@
|
||||
* {
|
||||
--color: oklch(0.4 0.25 var(--hue));
|
||||
--color-max: oklch(0.8 0.25 var(--hue));
|
||||
--color-bg1: oklch(0.8 0.15 var(--hue));
|
||||
--color-bg2: oklch(1.0 0.05 var(--hue));
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: white;
|
||||
color: black;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
--hue: 0;
|
||||
--ade: 130;
|
||||
--bea: 242;
|
||||
--cam: 350;
|
||||
--des: 73;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select,
|
||||
output {
|
||||
border: 1px solid var(--color);
|
||||
background-color: var(--color-bg2);
|
||||
color: var(--color);
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-variant: inherit;
|
||||
padding: 0.1em 0.25em;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: disclosure-closed;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 0.75em;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-variant: small-caps;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
background-color: var(--color);
|
||||
color: white;
|
||||
width: fit-content;
|
||||
padding: 0 0.25em;
|
||||
position: relative;
|
||||
height: 1lh;
|
||||
}
|
||||
|
||||
h2::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -1lh;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
background-color: var(--color);
|
||||
clip-path: polygon(100% 0, 0 0, 0 100%);
|
||||
transform: scale(1.005);
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding: 0 0.25em;
|
||||
font-variant: small-caps;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px dashed var(--color);
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 5em;
|
||||
color: var(--color);
|
||||
font-variant: small-caps;
|
||||
|
||||
>a {
|
||||
background-color: var(--color-bg1);
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.icon-bg {
|
||||
fill: var(--color);
|
||||
}
|
||||
|
||||
.icon-txt {
|
||||
dominant-baseline: central;
|
||||
letter-spacing: 0.5ch;
|
||||
font: 28px Arial, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
>div {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
>* {
|
||||
flex-grow: 1;
|
||||
align-content: center;
|
||||
flex-basis: 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#header-top {
|
||||
background-color: var(--color-bg1);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
min-height: 2.5em;
|
||||
|
||||
>div {
|
||||
margin: 0.25em 0;
|
||||
padding-right: 1em;
|
||||
background-color: var(--color-bg2);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
gap: 1em;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#header-bottom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: 2.5em;
|
||||
|
||||
>form {
|
||||
flex-grow: 1;
|
||||
margin-top: 0.25em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.25em;
|
||||
justify-content: center;
|
||||
|
||||
>* {
|
||||
height: fit-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
background-color: var(--color-bg1);
|
||||
clip-path: polygon(100% 0, 0 0, 0 100%);
|
||||
transform: scale(1.005);
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
header {
|
||||
height: fit-content;
|
||||
flex-direction: column;
|
||||
|
||||
#header-top>div>a {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#header-top>div>a:first-of-type {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#searchform {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
48
templates/base.html
Normal file
48
templates/base.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="--hue: var(--{{ hue }});">
|
||||
|
||||
<head>
|
||||
<title>Icolotl Index</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/">
|
||||
<svg version=" 1.1" width="196" height="46" xmlns="http://www.w3.org/2000/svg">
|
||||
<polyline points="23,46 173,46 196,23 173,0 23,0 0,23" fill="#FFFFFF" class="icon-bg" />
|
||||
<polyline points="23,44 33,34 23,24 13,34" fill="var(--color-max)" style="--hue: var(--des);" />
|
||||
<polyline points="34,33 44,23 34,13 24,23" fill="var(--color-max)" style="--hue: var(--bea);" />
|
||||
<polyline points="23,22 33,12 23,2 13,12" fill="var(--color-max)" style="--hue: var(--ade);" />
|
||||
<polyline points="12,33 22,23 12,13 2,23" fill="var(--color-max)" style="--hue: var(--cam);" />
|
||||
<text x="54" y="22" fill="#FFFFFF" class="icon-txt">INDEX</text>
|
||||
</svg>
|
||||
</a>
|
||||
<div>
|
||||
<div id="header-top">
|
||||
<div>
|
||||
<span class="spacer"></span>
|
||||
<a href="/account">Account</a>
|
||||
<a>Console</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="header-bottom">
|
||||
<span class="spacer"></span>
|
||||
<form action="{{ url_for('search') }}" method="get" id="searchform">
|
||||
<label for="search-type">Search Type:</label>
|
||||
<select name="search-type" id="search-type">
|
||||
<option value="classic">Classic</option>
|
||||
<option value="oid">Object ID</option>
|
||||
</select>
|
||||
<input type="search" name="search" id="search">
|
||||
<input type="submit" value="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{% block content %}{% endblock %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
35
templates/database.html
Normal file
35
templates/database.html
Normal file
@@ -0,0 +1,35 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='database.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
<form method="post">
|
||||
<div>
|
||||
<label for="collection">Collection:</label>
|
||||
<input id="collection" name="collection" list="cnames">
|
||||
<datalist id="cnames">
|
||||
{% for cname in cnames %}
|
||||
<option value="{{ cname }}">{{ cname }}</option>
|
||||
{% endfor %}
|
||||
</datalist>
|
||||
</div>
|
||||
<div id="query-box">
|
||||
<label for="query">Query:</label>
|
||||
<input id="query" name="query">
|
||||
</div>
|
||||
<input type="submit" value="Submit Query">
|
||||
</form>
|
||||
<hr>
|
||||
{% if results %}
|
||||
<ul>
|
||||
{% for result in results %}
|
||||
<li><a href="{{ url_for('database_edit', collection=collection, oid=result['_id']) }}">{{ result["id"] }} ({{
|
||||
result["_id"] }})</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
25
templates/database_edit.html
Normal file
25
templates/database_edit.html
Normal file
@@ -0,0 +1,25 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='database.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
<form id="edit-form" method="post">
|
||||
<div>
|
||||
<label for="collection">Collection:</label>
|
||||
<output id="collection">{{ collection }}</output>
|
||||
</div>
|
||||
<div id="object-box">
|
||||
<label for="object">Document:</label>
|
||||
<output id="object">{{ name }} ({{ oid }})</output>
|
||||
</div>
|
||||
<input type="submit" value="Save Changes">
|
||||
</form>
|
||||
<hr>
|
||||
<div id="doc-box">
|
||||
<textarea form="edit-form" id="document" name="document">{{ document }}</textarea>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
81
templates/index.html
Normal file
81
templates/index.html
Normal file
@@ -0,0 +1,81 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="outer">
|
||||
<nav id="sidebar">
|
||||
{% set quads = {"ade":"♢", "bea":"♡", "cam":"♧", "des":"♤"} %}
|
||||
{% for quad in quads %}
|
||||
{% if domains[quad] %}
|
||||
<div style="--hue: var(--{{ quad }});">
|
||||
<h2>{{ quads[quad] }}</h2>
|
||||
<ul>
|
||||
{% for category in domains[quad] %}
|
||||
{% for domain in domains[quad][category] %}
|
||||
<li><a href="{{ url_for(domains[quad][category][domain]) }}">{{ domain }}</a></li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</nav>
|
||||
<main>
|
||||
<nav id="sector">
|
||||
<a href="/ade" style="--hue: 130;">♢</a>
|
||||
<a href="/bea" style="--hue: 242;">♡</a>
|
||||
<a href="/cam" style="--hue: 350;">♧</a>
|
||||
<a href="/des" style="--hue: 73;">♤</a>
|
||||
</nav>
|
||||
<hr id="sectorbar">
|
||||
<div id="main-split">
|
||||
<div id="apps">
|
||||
{% for category in domains[quad] %}
|
||||
<div>
|
||||
<h2>{{ category }}</h2>
|
||||
<ul>
|
||||
{% for domain in domains[quad][category] %}
|
||||
<li><a href="{{ url_for(domains[quad][category][domain]) }}">{{ domain }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div id="other">
|
||||
<div id="welcome">
|
||||
<h2>{{ wdata["header"] }}</h2>
|
||||
<p>{{ wdata["text"] }}</p>
|
||||
<i>For assistance, please contact the Sector Administrator at <a
|
||||
href="mailto:{{ wdata['email'] }}@icolotl.com">{{ wdata['email'] }}@icolotl.com</a>.</i>
|
||||
</div>
|
||||
<div id="login">
|
||||
<h2>Index Unified Login</h2>
|
||||
<form method="post" action="{{ url_for('login') }}">
|
||||
{% if session["username"] %}
|
||||
<span>Welcome, <b>{{ session["username"] }}</b>.</span>
|
||||
<input type="submit" value="Log Out">
|
||||
{% else %}
|
||||
<label for="username">Username:</label>
|
||||
<input id="username" name="username" autocomplete="username">
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" autocomplete="current-password">
|
||||
<input type="submit" value="Log In">
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
<div id="news">
|
||||
<h2>News</h2>
|
||||
<h3>Test1</h3>
|
||||
<p>Foo, Bar, Baz</p>
|
||||
<hr>
|
||||
<h3>Test2</h3>
|
||||
<p>Quux, Quuux, Quuuux</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user