Adding archives

Added functionality to add archives.
This commit is contained in:
Michael Rodin 2023-10-19 13:25:59 +02:00
parent 978c634067
commit c3bd73a068
7 changed files with 212 additions and 61 deletions

View file

@ -36,10 +36,12 @@
<yjs:SolidColorFill x:Key="1" color="#FFE8EEF7"/>
<yjs:SolidColorFill x:Key="2" color="#FFB7C9E3"/>
<y:FreeNodePortLocationModelParameter x:Key="3" Ratio="0.007575757575757569,0.5"/>
<yjs:SolidColorFill x:Key="4" color="#FF663800"/>
<yjs:Stroke x:Key="5" fill="{y:GraphMLReference 4}"/>
<yjs:Arrow x:Key="6" type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}" cropLength="1"/>
<yjs:PolylineEdgeStyle x:Key="7" stroke="{y:GraphMLReference 5}" targetArrow="{y:GraphMLReference 6}"/>
<yjs:SolidColorFill x:Key="4" color="#FFCEDED3"/>
<yjs:SolidColorFill x:Key="5" color="#FFB7E3C5"/>
<yjs:SolidColorFill x:Key="6" color="#FF663800"/>
<yjs:Stroke x:Key="7" fill="{y:GraphMLReference 6}"/>
<yjs:Arrow x:Key="8" type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
<yjs:PolylineEdgeStyle x:Key="9" stroke="{y:GraphMLReference 7}" targetArrow="{y:GraphMLReference 8}"/>
</y:SharedData>
</data>
<graph id="G" edgedefault="directed">
@ -135,7 +137,6 @@
<x:Array Type="sys:Object">
<sys:String>ID</sys:String>
<sys:String>LABEL</sys:String>
<sys:String>CATEGORY</sys:String>
<sys:String>TYPE</sys:String>
</x:Array>
</x0:EntityRelationshipModel.attributes>
@ -165,7 +166,7 @@
<y:RectD X="-27.5" Y="-250" Width="130" Height="130"/>
</data>
<data key="d7">
<x0:EntityNodeStyle fill="#FFCEDED3" insetFill="#FFB7E3C5" stroke="BLACK">
<x0:EntityNodeStyle fill="{y:GraphMLReference 4}" insetFill="{y:GraphMLReference 5}" stroke="BLACK">
<x0:EntityNodeStyle.model>
<x0:EntityRelationshipModel>
<x0:EntityRelationshipModel.title>ArchLab</x0:EntityRelationshipModel.title>
@ -253,7 +254,7 @@
<node id="n6">
<data key="d0">8</data>
<data key="d5">
<y:RectD X="210.5" Y="-453" Width="130" Height="130"/>
<y:RectD X="210.5" Y="-48" Width="130" Height="130"/>
</data>
<data key="d7">
<x0:EntityNodeStyle fill="{y:GraphMLReference 1}" insetFill="{y:GraphMLReference 2}" stroke="BLACK">
@ -281,53 +282,105 @@
<y:FreeNodePortLocationModelParameter Ratio="0.5,0.9924242424242424"/>
</data>
</port>
<port name="p2">
<data key="d16">
<y:FreeNodePortLocationModelParameter Ratio="0.007575757575757569,0.5"/>
</data>
</port>
</node>
<node id="n7">
<data key="d0">9</data>
<data key="d5">
<y:RectD X="-27.5" Y="-48" Width="130" Height="130"/>
</data>
<data key="d7">
<x0:EntityNodeStyle fill="{y:GraphMLReference 4}" insetFill="{y:GraphMLReference 5}" stroke="BLACK">
<x0:EntityNodeStyle.model>
<x0:EntityRelationshipModel>
<x0:EntityRelationshipModel.title>CatLabType</x0:EntityRelationshipModel.title>
<x0:EntityRelationshipModel.attributes>
<x:Array Type="sys:Object">
<sys:String>ID</sys:String>
<sys:String>CATID</sys:String>
<sys:String>LABID</sys:String>
</x:Array>
</x0:EntityRelationshipModel.attributes>
</x0:EntityRelationshipModel>
</x0:EntityNodeStyle.model>
</x0:EntityNodeStyle>
</data>
<data key="d8">
<y:ViewState>
<y:FolderNodeState x:Key="c|view" Layout="-12.5,-235,130,130">
<y:FolderNodeState.Style>
<x0:EntityNodeStyle fill="{y:GraphMLReference 4}" insetFill="{y:GraphMLReference 5}" stroke="BLACK">
<x0:EntityNodeStyle.model>
<x0:EntityRelationshipModel>
<x0:EntityRelationshipModel.title>ArchLab</x0:EntityRelationshipModel.title>
<x0:EntityRelationshipModel.attributes>
<x:Array Type="sys:Object">
<sys:String>ID</sys:String>
<sys:String>ARCHID</sys:String>
<sys:String>LABID</sys:String>
</x:Array>
</x0:EntityRelationshipModel.attributes>
</x0:EntityRelationshipModel>
</x0:EntityNodeStyle.model>
</x0:EntityNodeStyle>
</y:FolderNodeState.Style>
</y:FolderNodeState>
</y:ViewState>
</data>
</node>
<edge id="e0" source="n0" target="n2" sourceport="p0" targetport="p0">
<data key="d10"><![CDATA[]]></data>
<data key="d13">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}" targetArrow="{y:GraphMLReference 6}">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}" targetArrow="{y:GraphMLReference 8}">
<yjs:PolylineEdgeStyle.sourceArrow>
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}" cropLength="1"/>
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
</yjs:PolylineEdgeStyle.sourceArrow>
</yjs:PolylineEdgeStyle>
</data>
</edge>
<edge id="e1" source="n0" target="n1" sourceport="p1" targetport="p0">
<data key="d13">
<y:GraphMLReference ResourceKey="7"/>
<y:GraphMLReference ResourceKey="9"/>
</data>
</edge>
<edge id="e2" source="n0" target="n4" sourceport="p2" targetport="p0">
<data key="d13">
<y:GraphMLReference ResourceKey="7"/>
<y:GraphMLReference ResourceKey="9"/>
</data>
</edge>
<edge id="e3" source="n4" target="n5" sourceport="p1" targetport="p0">
<data key="d13">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}">
<yjs:PolylineEdgeStyle.targetArrow>
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}"/>
</yjs:PolylineEdgeStyle.targetArrow>
<yjs:PolylineEdgeStyle.sourceArrow>
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}"/>
</yjs:PolylineEdgeStyle.sourceArrow>
</yjs:PolylineEdgeStyle>
</data>
</edge>
<edge id="e4" source="n6" target="n2" sourceport="p1" targetport="p1">
<data key="d13">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}">
<yjs:PolylineEdgeStyle.targetArrow>
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}"/>
</yjs:PolylineEdgeStyle.targetArrow>
<yjs:PolylineEdgeStyle.sourceArrow>
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
</yjs:PolylineEdgeStyle.sourceArrow>
</yjs:PolylineEdgeStyle>
</data>
</edge>
<edge id="e5" source="n2" target="n1" sourceport="p2" targetport="p1">
<edge id="e5" source="n6" target="n1" sourceport="p2" targetport="p1">
<data key="d13">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 5}" targetArrow="{y:GraphMLReference 6}">
<yjs:PolylineEdgeStyle stroke="{y:GraphMLReference 7}" targetArrow="{y:GraphMLReference 8}">
<yjs:PolylineEdgeStyle.sourceArrow>
<yjs:Arrow type="NONE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 4}"/>
<yjs:Arrow type="TRIANGLE" scale="0.75" stroke="#FF663800" fill="{y:GraphMLReference 6}" cropLength="1"/>
</yjs:PolylineEdgeStyle.sourceArrow>
</yjs:PolylineEdgeStyle>
</data>

View file

@ -19,7 +19,7 @@ def timectime(s):
def convsize(s):
sizes=("B","KB","MB","GB","TB")
n=0
while s > 1000:
while s >= 1000:
n+=1
s=s/1000
return str("%.2f" % s)+sizes[n]
@ -28,31 +28,48 @@ def convsize(s):
@app.route('/')
def homepage():
# try to get userdata, else logout state
print(request.base_url)
try:
logged_in,userdata=get_login_info(request.cookies.get('session'))
except Exception as e:
logged_in,userdata=False,()
#try: # TODO: CLEAN!
# # get sesskey and get info about user
# sesskey=request.cookies.get('session')
# res,userid=db.check_sesskey(sesskey)
# if not res:
# return 500
# userdata=db.get_user_info(userid)
# logged_in=True
#except Exception as e:
# logged_in=False
# userdata=()
logged_in,userdata=get_login_info(request.cookies.get('session'))
archives=db.get_n_archives()
return render_template("home.html", title="Homepage",userdata=userdata,login=logged_in,archives=archives)
@app.route('/user')
@app.route('/user/<int:userid>')
def userpage(userid:int=0):
if userid == 0:
logged_in,userdata=get_login_info(request.cookies.get('session'))
if not logged_in:
return make_response(redirect('/'))
@app.route('/add', methods=['GET','POST'])
def addpage():
# try to get userdata, else yeet to the homepage
logged_in,userdata=get_login_info(request.cookies.get('session'))
if not logged_in:
return make_response(redirect('/'))
# POST: check and add archive, show confirmation/error message at the end
if request.method == 'POST':
postdict={}
# get and save all inputs, error if one doesn't exist or is wrong type
for i,itype in [("name",str),("hash",str),("category",int),("size",float)]:
try:
postdict[i]=itype(request.form[i])
except Exception as e:
return "<h2>ERROR: All fields need to be filled and don't play with their names!</h2> Go back and try again."
try:
postdict["size"]=postdict["size"]*int(request.form['size_multiplier'])
except Exception as e:
return "<h2>ERROR: All fields need to be filled and don't play with their names!</h2> Go back and try again."
postdict["owner"]=userdata[0]
res,archid=db.add_archive(postdict)
if res:
return make_response(redirect(f"/view/{str(archid)}"))
else:
return f"<h2>ERROR: {archid}</h2> Go back and try again.", 400
# GET: return normal page
htmlcatlist=get_category_selection(False)
return render_template("add.html", title="Add Archive",categories=htmlcatlist)
@app.route('/login', methods=["GET","POST"])
def loginpage():
# POST: Process login request
@ -78,10 +95,7 @@ def loginpage():
@app.route('/search')
def searchpage():
# try to get userdata, else logout state
try:
logged_in,userdata=get_login_info(request.cookies.get('session'))
except Exception as e:
logged_in,userdata=False,()
logged_in,userdata=get_login_info(request.cookies.get('session'))
# try to set all required variables, else defaults
try:
@ -103,26 +117,18 @@ def searchpage():
count=20
archives=db.get_n_archives(sorttype,category,keywords,count)
catlist=db.get_all_categories()
htmlcatlist=[]
# parse all categories and sort them into select box
for cat in catlist:
if not cat[2]:
htmlcatlist.append((cat[0],cat[1]))
parent=cat[1]
parentid=cat[0]
for i in catlist:
if i[2] == parentid:
htmlcatlist.append((i[0],f"{parent}/{i[1]}"))
htmlcatlist=get_category_selection()
return render_template("search.html", title="Advanced Search",categories=htmlcatlist,userdata=userdata,login=logged_in,archives=archives)
## FUNCTIONS
## Checks if given sesskey is valid and returns user data
## OUTPUT: (if sesskey valid) logged_in:bool=True, userdata:tup
## (if sesskey invalid)
## OUTPUT: (if sesskey valid) logged_in:bool=True, userdata:tuple
## (if sesskey invalid) logged_in:bool=False, userdata:tuple=()
def get_login_info(sesskey:str):
if not sesskey:
return False,()
logged_in,userid=db.check_sesskey(sesskey)
if logged_in:
userdata=db.get_user_info(userid)
@ -135,6 +141,23 @@ def setcookie(name:str,value:str,lifetime:int=10000):
resp.set_cookie(name, value, max_age=lifetime)
return resp
## Gets all categories and returns them (with or without parents)
## OUTPUT: […,(ID:int,NAME:str),…]
def get_category_selection(include_parents:bool=True):
catlist=db.get_all_categories()
htmlcatlist=[]
# parse all categories and sort them into list
for cat in catlist:
if not cat[2]:
if include_parents:
htmlcatlist.append((cat[0],cat[1]))
parent=cat[1]
parentid=cat[0]
for i in catlist:
if i[2] == parentid:
htmlcatlist.append((i[0],f"{parent}/{i[1]}"))
return htmlcatlist
## API CALLS (NO THANKS)
# main driver function

View file

@ -1,7 +1,7 @@
## MAIN FUNCTIONS FILE FOR BACK-BACKEND OF FLASK
import mariadb as sql
from os import environ
import time
import time,re
## params populated with environment variables, defaults can be changed for a permanent solution
conn_params={
@ -23,7 +23,7 @@ class db:
self.cur.execute("""CREATE TABLE IF NOT EXISTS Archs(
ID int PRIMARY KEY AUTO_INCREMENT,
NAME text NOT NULL,
HASH text NOT NULL,
HASH text NOT NULL UNIQUE,
SIZE int NOT NULL,
IMPORTED int,
CATEGORY int,
@ -55,10 +55,14 @@ class db:
ARCHID int NOT NULL,
LABID int NOT NULL
);""")
self.cur.execute("""CREATE TABLE IF NOT EXISTS CatLabType(
ID int PRIMARY KEY AUTO_INCREMENT,
CATID int NOT NULL,
LABID int NOT NULL
);""")
self.cur.execute("""CREATE TABLE IF NOT EXISTS Labs(
ID int PRIMARY KEY AUTO_INCREMENT,
LABEL text NOT NULL,
CATEGORY text,
TYPE int NOT NULL
);""")
self.cur.execute("""CREATE TABLE IF NOT EXISTS LabType(
@ -99,8 +103,30 @@ class db:
self.cur.execute(f"SELECT * FROM Users WHERE ID='{userid}'")
return self.cur.fetchone()
## Checks information for errors and adds archive to the DB
## OUTPUT: (if successful) res:bool=True, ID:int
## (if unsuccessful) res:bool=False, str
def add_archive(self, archive:dict):
# Check everything for errors or malicious things
archive["hash"]=archive["hash"].upper()
if not re.match('[A-Z0-9]{40}', archive["hash"]):
return False, "Hash needs to be 40 characters in hexadecimal (SHA-1)."
if re.match('.*[^A-Za-z0-9\. _-].*', archive["name"]):
return False, "The name contains illegal characters. Allowed chars: '[A-Za-z0-9\. _-]'"
print(archive["name"])
curtime=time.time()
try:
self.cur.execute(f"INSERT INTO Archs(NAME,HASH,SIZE,IMPORTED,CATEGORY,OWNER) VALUES('{archive['name']}','{archive['hash']}',{archive['size']},{curtime},{archive['category']},{archive['owner']})")
except Exception as e: # hash needs to be unique
return False, e
self.cur.execute(f"SELECT ID FROM Archs WHERE HASH='{archive['hash']}'")
archid=self.cur.fetchone()
return True,archid[0]
## Returns all relevant information about one (1) archive
## OUTPUT: archive:tuple=(NAME:str,HASH:str,IMPORTED[UNIX]:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,UNAME:str,DNAME:str)
## OUTPUT: archive:tuple=(NAME:str,HASH:str,IMPORTED[UNIX]:int,CATEGORY,str,CATEGORY.DESCRIPTION:str,UNAME:str,DNAME:str),
## labels:array=[…,(LABEL:str,CATEGORY:str,CATDESC:str,LABTYPE:str,LABDESC:str),…]
def get_archive_info(self, hash:str):
self.cur.execute(f"""SELECT Archs.NAME,Archs.HASH,Archs.IMPORTED,Cats.CATEGORY,Cats.DESCRIPTION,Users.UNAME,Users.DNAME FROM Archs
@ -146,14 +172,14 @@ class db:
# Get all children of category (if exist) and put into query string
categories=self.get_all_categories()
catlist=[category]
catlist=[str(category)]
for i in categories:
if i[2] == int(category):
catlist.append(str(i[0]))
category="(" + ",".join(catlist) + ")"
categories="(" + ",".join(catlist) + ")"
self.cur.execute(f"""SELECT ID,NAME,SIZE,IMPORTED FROM Archs
{"WHERE 1=1" if category==0 else " WHERE CATEGORY IN " + category}
{"WHERE 1=1" if category==0 else " WHERE CATEGORY IN " + categories}
{keyword_string}
ORDER BY {sorttype} LIMIT {count};""")
archives=self.cur.fetchall()

4
flask/static/add.css Normal file
View file

@ -0,0 +1,4 @@
div.grid-container {
display: grid;
grid-template-columns: max-content max-content;
}

View file

@ -15,6 +15,16 @@ header {
display: flex;
}
header > a {
margin: auto;
height: 2em;
width: 2em;
}
a > img {
height: 2em;
width: 2em;
}
header > div#container {
flex-grow: 1;
display: flex;

34
flask/templates/add.html Normal file
View file

@ -0,0 +1,34 @@
{% extends "base.html" %}
{% block meta %}
<link rel="stylesheet" href="/static/add.css" />
{% endblock %}
{% block content %}
<form action="/add" method="post">
<div class="grid-container">
<div><b>Name: </b></div>
<div><input type="text" name="name" placeholder="Archive Name"></div>
<div><b>Hash: </b></div>
<div><input type="text" name="hash" placeholder="Hash"></div>
<div><b>Category: </b></div>
<div>
<select name="category">
{% for i in categories %}
<option value="{{i[0]}}">{{i[1]}}</option>
{% endfor %}
</select>
</div>
<div><b>Size: </b></div>
<div>
<input type="number" name="size" step="0.01" min="0" placeholder="Size">
<select name="size_multiplier">
{% for val,size in ((1,"B"),(1000,"KB"),(1000000,"MB"),(1000000000,"GB"),(1000000000000,"TB")) %}
<option value="{{val}}">{{size}}</option>
{% endfor %}
</select>
</div>
<input type="submit" value="Add Archive">
</div>
</form>
{% endblock %}

View file

@ -10,6 +10,7 @@
<body>
<header>
<a href="/"><img src="/static/favicon.ico" alt="favicon"></a>
<span id="title">{{title}}</span>
<div id="container">
<!-- BUTTONS IF USER LOGGED IN -->