From cd43dd3dc61d7fed8f10aede9fb9b03da72def38 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Wed, 22 Nov 2023 13:00:04 +1100 Subject: [PATCH] feat: Add initial tribes --- db.py | 138 +++++++++++++++++++- main.py | 256 +++++++++++++++++++++++++++++++++++++- template.py | 2 + templates/account.html | 2 +- templates/edit_tribe.html | 102 +++++++++++++++ templates/index.html | 6 + templates/new_tribe.html | 100 +++++++++++++++ templates/tribe.html | 104 ++++++++++++++++ templates/tribe_view.html | 63 ++++++++++ 9 files changed, 762 insertions(+), 11 deletions(-) create mode 100644 templates/edit_tribe.html create mode 100644 templates/new_tribe.html create mode 100644 templates/tribe.html create mode 100644 templates/tribe_view.html diff --git a/db.py b/db.py index cdddf66..5c2bf7c 100644 --- a/db.py +++ b/db.py @@ -14,9 +14,6 @@ dbargs = { 'database':os.getenv('DB_NAME') } - - - def check_tables(): connection = mysql.connector.connect(**dbargs) cursor = connection.cursor() @@ -38,6 +35,15 @@ def check_tables(): PRIMARY KEY (id) ) """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS tribes ( + id INT(11) NOT NULL AUTO_INCREMENT, + tribe VARCHAR(255) NOT NULL, + data JSON, + owner VARCHAR(255) NOT NULL, + PRIMARY KEY (id) + ) + """) cursor.close() connection.close() print("Checked tables") @@ -220,4 +226,128 @@ def get_random_sites(): for site in data: names.append(site[1]) - return names \ No newline at end of file + return names + +def get_tribes(): + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + cursor.execute(""" + SELECT * FROM tribes + """) + data = cursor.fetchall() + cursor.close() + connection.close() + + tribes = [] + for tribe in data: + tribes.append(tribe[1]) + + return tribes + +def get_user_tribes(user): + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + cursor.execute(""" + SELECT * FROM tribes WHERE owner = %s + """, (user,)) + data = cursor.fetchall() + cursor.close() + connection.close() + + # Also check members + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + cursor.execute(""" + SELECT * FROM tribes + """) + data2 = cursor.fetchall() + cursor.close() + connection.close() + + + for tribe in data2: + tribe = json.loads(tribe[2]) + if user in tribe['members']: + data.append(tribe) + + + return len(data) + +def get_user_owned_tribe(user): + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + cursor.execute(""" + SELECT * FROM tribes WHERE owner = %s + """, (user,)) + data = cursor.fetchall() + cursor.close() + connection.close() + + return data + +def create_tribe(tribe,owner): + # Get users' tribes + if (get_user_tribes(owner) > 0): + return False + + + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + data = { + "data": "", + "members": [ + owner + ] + } + insert_query = "INSERT INTO tribes (data,tribe,owner) VALUES (%s,%s,%s)" + cursor.execute(insert_query, (json.dumps(data), tribe, owner)) + connection.commit() + cursor.close() + connection.close() + return True + +def get_tribe_data_raw(tribe): + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + cursor.execute(""" + SELECT * FROM tribes WHERE tribe = %s + """, (tribe,)) + data = cursor.fetchall() + cursor.close() + connection.close() + + if len(data) == 0: + return False + + return json.loads(data[0][2]) + +def check_tribe_owner(tribe,owner): + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + cursor.execute(""" + SELECT * FROM tribes WHERE tribe = %s + """, (tribe,)) + data = cursor.fetchall() + cursor.close() + connection.close() + + if len(data) == 0: + return False + + if data[0][3] == owner: + return True + else: + return False + +def update_tribe_data_raw(tribe,data): + connection = mysql.connector.connect(**dbargs) + cursor = connection.cursor() + # Update json object + data = json.loads(data) + + update_query = "UPDATE tribes SET data = %s WHERE tribe = %s" + cursor.execute(update_query, (json.dumps(data), tribe)) + + connection.commit() + cursor.close() + connection.close() \ No newline at end of file diff --git a/main.py b/main.py index eb7fd8e..2068066 100644 --- a/main.py +++ b/main.py @@ -65,7 +65,10 @@ def index(): for site in random_sites_names: random_sites += "" + site + "." +CITY_DOMAIN+ "
" - + tribes = db.get_tribes() + tribesHTML = "" + for tribe in tribes: + tribesHTML += "" + tribe + "
" if 'token' in request.cookies: @@ -77,8 +80,8 @@ def index(): resp = make_response(redirect('/')) resp.set_cookie('token', '', expires=0) return resp - return render_template('index.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites) - return render_template('index.html',account="Login",account_link="login",account_link_name="Login",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites) + return render_template('index.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites,tribes=tribesHTML) + return render_template('index.html',account="Login",account_link="login",account_link_name="Login",CITY_DOMAIN=CITY_DOMAIN,random_sites=random_sites,tribes=tribesHTML) @app.route('/signup', methods=['POST']) def signup(): @@ -329,6 +332,232 @@ def avatar_clear(): return redirect('/edit') +@app.route('/tribe') +def tribe_list(): + tribes = db.get_tribes() + tribesHTML = "" + for tribe in tribes: + tribesHTML += "" + tribe + "
" + + # Add create link if user is logged in + if 'token' in request.cookies: + token = request.cookies['token'] + # Verify token + user = accounts.validate_token(token) + if user: + tribesHTML += "

Create a tribe" + return render_template('tribe.html',account="Account",account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,tribes=tribesHTML) + + return render_template('tribe.html',account="Login",account_link="login",account_link_name="Login",CITY_DOMAIN=CITY_DOMAIN,tribes=tribesHTML) + +@app.route('/new_tribe') +def new_tribe(): + if 'token' not in request.cookies: + return redirect('/login') + + token = request.cookies['token'] + if not accounts.validate_token(token): + return error('Sorry we had an issue verifying your account') + # Verify token + user = accounts.validate_token(token) + if not user: + # Remove cookie + resp = make_response(redirect('/login')) + resp.set_cookie('token', '', expires=0) + return resp + + return render_template('new_tribe.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN) + +@app.route('/new_tribe', methods=['POST']) +def create_tribe(): + token = request.cookies['token'] + if not accounts.validate_token(token): + return error('Sorry we had an issue verifying your account') + # Verify token + user = accounts.validate_token(token) + if not user: + # Remove cookie + resp = make_response(redirect('/login')) + resp.set_cookie('token', '', expires=0) + return resp + + tribe = request.form['tribe'].strip().lower() + if not re.match("^[a-z0-9]*$", tribe): + return error('Sorry tribe can only contain lowercase letters and numbers') + + if len(tribe) < 3: + return error('Sorry tribe must be at least 3 characters long') + if len(tribe) > 255: + return error('Sorry tribe must be less than 255 characters long') + + if db.create_tribe(tribe,user['domain']): + return redirect('/tribe/' + tribe) + else: + return error('Sorry you already have a tribe') + +@app.route('/tribe/') +def tribe(tribe): + tribe = tribe.lower() + if not re.match("^[a-z0-9]*$", tribe): + return error('Sorry we couldn\'t find that tribe') + + data = db.get_tribe_data_raw(tribe) + if data == None: + return error('Sorry we couldn\'t find that tribe') + + html = "" + if 'data' in data: + html = data['data'].encode('utf-8').decode('unicode-escape') + html = html.replace("\n","
") + + tribe = tribe.capitalize() + + members_html = "" + members = data['members'] + for member in members: + members_html += "" + member + "." +CITY_DOMAIN+ "
" + + edit = "" + + # Add edit link if user is logged in + if 'token' in request.cookies: + token = request.cookies['token'] + # Verify token + user = accounts.validate_token(token) + if user: + if db.check_tribe_owner(tribe,user['domain']): + edit = "Edit tribe" + elif user['domain'] not in members: + edit = "Join tribe" + + return render_template('tribe_view.html',tribe=tribe,data=html,edit=edit,members=members_html) + +@app.route('/join_tribe/') +def join_tribe(tribe): + tribe = tribe.lower() + if not re.match("^[a-z0-9]*$", tribe): + return error('Sorry we couldn\'t find that tribe') + + data = db.get_tribe_data_raw(tribe) + if data == None: + return error('Sorry we couldn\'t find that tribe') + + # Verify token + token = request.cookies['token'] + if not accounts.validate_token(token): + # Remove cookie + resp = make_response(redirect('/login')) + resp.set_cookie('token', '', expires=0) + return resp + user = accounts.validate_token(token) + if not user: + # Remove cookie + resp = make_response(redirect('/login')) + resp.set_cookie('token', '', expires=0) + return resp + + members = data['members'] + if user['domain'] in members: + return error('Sorry you are already a member of this tribe') + + members.append(user['domain']) + data['members'] = members + # Convert to json + data = json.dumps(data) + db.update_tribe_data_raw(tribe,data) + return redirect('/tribe/' + tribe) + + +@app.route('/edit_tribe') +def edit_tribe_view(): + token = request.cookies['token'] + if not accounts.validate_token(token): + return error('Sorry we had an issue verifying your account') + # Verify token + user = accounts.validate_token(token) + if not user: + # Remove cookie + resp = make_response(redirect('/')) + resp.set_cookie('token', '', expires=0) + return resp + + tribeData = db.get_user_owned_tribe(user['domain']) + if len(tribeData) == 0: + return error('Sorry you don\'t own a tribe') + + tribe = tribeData[0][1] + data = tribeData[0][2] + data = json.loads(data) + html = data['data'].encode('utf-8').decode('unicode-escape') + members = data['members'] + members_html = "" + for member in members: + members_html += "" + member + "." +CITY_DOMAIN+ " (Remove)
" + + return render_template('edit_tribe.html',account=user['email'],account_link="account",account_link_name="Account",CITY_DOMAIN=CITY_DOMAIN,tribe=tribe,data=html,members=members_html) + +@app.route('/edit_tribe', methods=['POST']) +def edit_tribe(): + token = request.cookies['token'] + if not accounts.validate_token(token): + return error('Sorry we had an issue verifying your account') + # Verify token + user = accounts.validate_token(token) + if not user: + # Remove cookie + resp = make_response(redirect('/')) + resp.set_cookie('token', '', expires=0) + return resp + + tribeData = db.get_user_owned_tribe(user['domain']) + if len(tribeData) == 0: + return error('Sorry you don\'t own a tribe') + + tribe = tribeData[0][1] + data = tribeData[0][2] + data = json.loads(data) + data['data'] = request.form['data'].strip() + + # Convert to json + data = json.dumps(data) + db.update_tribe_data_raw(tribe,data) + return redirect('/edit_tribe') + +@app.route('/remove_member') +def remove_member(): + token = request.cookies['token'] + if not accounts.validate_token(token): + return error('Sorry we had an issue verifying your account') + + member = request.args.get('member') + # Verify token + user = accounts.validate_token(token) + if not user: + # Remove cookie + resp = make_response(redirect('/')) + resp.set_cookie('token', '', expires=0) + return resp + + tribeData = db.get_user_owned_tribe(user['domain']) + if len(tribeData) == 0: + return error('Sorry you don\'t own a tribe') + + # Verify member isn't owner + if member == user['domain']: + return error('Sorry you can\'t remove yourself from your own tribe') + + tribe = tribeData[0][1] + data = tribeData[0][2] + data = json.loads(data) + members = data['members'] + if member in members: + members.remove(member) + data['members'] = members + # Convert to json + data = json.dumps(data) + db.update_tribe_data_raw(tribe,data) + return redirect('/edit_tribe') + @app.route('/') def catch_all(path): account = "Login" @@ -336,6 +565,9 @@ def catch_all(path): account_link_name = "Login" site = "Null" domain = "" + tribe_title = "Join a tribe" + tribe_link = "tribe" + if 'domain' in request.args: domain = request.args.get('domain') if 'token' in request.cookies: @@ -351,6 +583,12 @@ def catch_all(path): account_link = "account" account_link_name = "Account" site = user['domain'] + "." + CITY_DOMAIN + # Check if user owns tribe + tribeData = db.get_user_owned_tribe(user['domain']) + if len(tribeData) > 0: + tribe_title = "Edit your tribe" + tribe_link = "edit_tribe" + elif path != "signup" and path != "login" and path != "empty_site": return redirect('/') @@ -360,11 +598,17 @@ def catch_all(path): # If file exists, load it if os.path.isfile('templates/' + path): - return render_template(path,account=account,account_link=account_link,account_link_name=account_link_name,site=site,CITY_DOMAIN=CITY_DOMAIN,domain=domain) + return render_template(path,account=account,account_link=account_link, + account_link_name=account_link_name,site=site, + CITY_DOMAIN=CITY_DOMAIN,domain=domain, + tribe_title=tribe_title,tribe_link=tribe_link) # Try with .html if os.path.isfile('templates/' + path + '.html'): - return render_template(path + '.html',account=account,account_link=account_link,account_link_name=account_link_name,site=site,CITY_DOMAIN=CITY_DOMAIN,domain=domain) + return render_template(path + '.html',account=account,account_link=account_link, + account_link_name=account_link_name,site=site, + CITY_DOMAIN=CITY_DOMAIN,domain=domain, + tribe_title=tribe_title,tribe_link=tribe_link) return redirect('/') # 404 catch all @@ -382,4 +626,4 @@ def update_random_sites(): if __name__ == '__main__': db.check_tables() - app.run(debug=False, port=5000, host='0.0.0.0') \ No newline at end of file + app.run(debug=True, port=5000, host='0.0.0.0') \ No newline at end of file diff --git a/template.py b/template.py index 673a7df..0a9c773 100644 --- a/template.py +++ b/template.py @@ -7,6 +7,8 @@ import os app = Flask(__name__) template = "city" data = "

Test site

This is a test site

" +# data = "

Nathan.Woodburn/

This is fully customizable!

You can even add custom css rules like below.
Try hovering over this image

\"My\"\"
" + db_object = { 'avatar': "https://woodburn.au/favicon.png", 'hnschat':"nathan.woodburn", diff --git a/templates/account.html b/templates/account.html index 6f10464..ec47c9d 100644 --- a/templates/account.html +++ b/templates/account.html @@ -41,7 +41,7 @@

Welcome to ShakeCities

Congratulations your site is ready for use.

- +
diff --git a/templates/edit_tribe.html b/templates/edit_tribe.html new file mode 100644 index 0000000..9131085 --- /dev/null +++ b/templates/edit_tribe.html @@ -0,0 +1,102 @@ + + + + + + + ShakeCities | Tribes + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+

Edit {{tribe}} tribe

+
+

{{members|safe}}

+
+
+
+
+
+
+
+

Contact

+ +
+
+

Donate

+

hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y

+
+
+
+
+

Contact

+ +
+
+

Donate

+

hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y

+
+
+
+
+
+
+
    +
  • Copyright © ShakeCities 2023
  • +
  • +
  • +
+
+
+ + + + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index ed9e503..ddb4051 100644 --- a/templates/index.html +++ b/templates/index.html @@ -57,6 +57,12 @@

{{random_sites|safe}}

+
+
+

Tribes

+

{{tribes|safe}}

+
+
diff --git a/templates/new_tribe.html b/templates/new_tribe.html new file mode 100644 index 0000000..973b036 --- /dev/null +++ b/templates/new_tribe.html @@ -0,0 +1,100 @@ + + + + + + + ShakeCities | Tribes + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+

Create a tribe

+
+
+
+
+
+
+
+

Contact

+ +
+
+

Donate

+

hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y

+
+
+
+
+

Contact

+ +
+
+

Donate

+

hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y

+
+
+
+
+
+
+
    +
  • Copyright © ShakeCities 2023
  • +
  • +
  • +
+
+
+ + + + + \ No newline at end of file diff --git a/templates/tribe.html b/templates/tribe.html new file mode 100644 index 0000000..6385ef2 --- /dev/null +++ b/templates/tribe.html @@ -0,0 +1,104 @@ + + + + + + + ShakeCities | Tribes + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+

About Tribes

+

ShakeCities is a site offering free personalizable websites. This is a perfect way to start your journey into Handshake domains! Step into a new experience where Handshake domains become instant sites, and where everyone can create their own unique decentralized page.
Secured with HTTPS powered by DANE (DNS-based Authentication of Named Entities).
But that's not all – ShakeCities goes beyond, allowing you to seamlessly integrate crypto addresses into your site, providing an easy way to get paid. Simply send crypto to @yourname.{{CITY_DOMAIN}}
Your ShakeCity domain can be used to chat on HNSChat or to authenticate on any site using Varo auth
Join us in building a decentralized, secure, and innovative internet landscape. Embrace the future with ShakeCities – where Handshake domains meet simplicity.

+
+
+

Find a tribe

+

{{tribes|safe}}

+
+
+
+
+
+
+

Contact

+ +
+
+

Donate

+

hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y

+
+
+
+
+

Contact

+ +
+
+

Donate

+

hs1qfqy9ewc3g4p06d4m5j8zm74qzhan7rxu7n8u9y

+
+
+
+
+
+
+
    +
  • Copyright © ShakeCities 2023
  • +
  • +
  • +
+
+
+ + + + + \ No newline at end of file diff --git a/templates/tribe_view.html b/templates/tribe_view.html new file mode 100644 index 0000000..115c3aa --- /dev/null +++ b/templates/tribe_view.html @@ -0,0 +1,63 @@ + + + + + + + ShakeCities + + + + + + + + + + + + + + + + + + + + + + +

{{tribe}}

+
+

+ {{data|safe}} +

+
+
+

Tribe members

+

+ {{members|safe}} +

+
+ + + +
+
+

{{edit|safe}}

+
+ + + +
+
+
+ +
+

Powered by ShakeCities

+
+ + + + + \ No newline at end of file