From ff34dc024add346dd540a36704c294f98a2c568f Mon Sep 17 00:00:00 2001 From: hornet Date: Mon, 14 Oct 2024 21:17:09 +0500 Subject: [PATCH] bug fixes, CSS update, admin dashboard functions, extended UI, update README.md --- README.md | 15 +++- app.py | 54 +++++++++++-- static/css/styles.css | 172 +++++++++++++++++++++++++++++++++++++++--- templates/admin.html | 62 +++++++++++++-- templates/board.html | 36 ++++++--- templates/index.html | 9 +++ templates/login.html | 22 +++--- 7 files changed, 322 insertions(+), 48 deletions(-) mode change 100644 => 100755 static/css/styles.css diff --git a/README.md b/README.md index 58adaa4..3c9e7d6 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,14 @@ imageboard written in python(flask) wirechan is an anonymous(optional registration) imageboard written in Flask framework using MongoDB as a database. It's designed to be used as a way of communication for various topics without the fear of being recogniszed, if the user wishes to remain unknown. +# features + +>Both logged-in and anonymous posting +>Admin dashboard for moderation +>No Javascript whatsoever +>Minimal dependencies +>Minimalistic design + # installation 1. Clone the repository: @@ -26,5 +34,10 @@ python3 -m pip install -r requirements.txt 4. Initialize the MongoDB server: ``` -mongo +mongo #or systemctl enable mongod && systemctl start mongod +``` + +5. Start the server +``` +python3 app.py ``` \ No newline at end of file diff --git a/app.py b/app.py index e258563..abc7b71 100644 --- a/app.py +++ b/app.py @@ -12,6 +12,7 @@ client = MongoClient('localhost', 27017) db = client.flask_db posts_collection = db.posts_collection users_collection = db.users_collection +nuke_counter = db.nuke_counter #app routes @app.route('/', methods=['GET']) @@ -34,13 +35,21 @@ def board(board_name): posts = posts_collection.find({'board_name': board_name}).sort('timestamp', -1) display_name = next((link['display_name'] for link in links if link['name'] == board_name), board_name) - return render_template('board.html', title=board_name, header=display_name, links=links, posts=posts) + admin_user = users_collection.find_one({'username': 'admin'}) + if admin_user and session.get('user_id') == str(admin_user['_id']): + return render_template('board.html', title=board_name, header=display_name, links=links, posts=posts, admin=True) + else: + admin=False + return render_template('board.html', title=board_name, header=display_name, links=links, posts=posts, admin=admin) @app.route('/post', methods=['POST']) def post(): board_name = request.form['board_name'] content = request.form['content'] - image = request.files['image'] + if 'image' in request.files: + image = request.files['image'] + else: + image = None timestamp = datetime.now() if 'user_id' not in session: username = 'Anonymous' @@ -55,7 +64,7 @@ def post(): 'username' : username } - if image: + if image != None: post_data['image'] = image.read() posts_collection.insert_one(post_data) @@ -118,9 +127,15 @@ def register_post(): @app.route('/admin', methods=['GET']) def admin(): admin_user = users_collection.find_one({'username': 'admin'}) - success = request.args.get('success', '') + users = users_collection.find({}) + success1 = request.args.get('success1', '') + success2 = request.args.get('success2', '') + success3 = request.args.get('success3', '') + total_users = users_collection.count_documents({}) + total_posts = posts_collection.count_documents({}) + nuke_count = nuke_counter.count_documents({}) if admin_user or session['user_id'] != str(admin_user['_id']): - return render_template('admin.html', success=success) + return render_template('admin.html', success1=success1, success2=success2, success3=success3, total_users=total_users, total_posts=total_posts, nuke_count=nuke_count, users=users) else: return url_for('index') @@ -133,13 +148,38 @@ def deletepost(): post_id = request.form['post_id'] posts_collection.delete_one({'_id': ObjectId(post_id)}) success = 'post deleted!' - return redirect(url_for('admin', success=success)) + return redirect(url_for('admin', success1=success)) + +@app.route('/deleteuser', methods=['POST']) +def deleteuser(): + admin_user = users_collection.find_one({'username': 'admin'}) + if not admin_user or session['user_id'] != str(admin_user['_id']): + return redirect(url_for('index')) + else: + user_id = request.form['user_id'] + users_collection.delete_one({'_id': ObjectId(user_id)}) + success = 'user deleted!' + return redirect(url_for('admin', success2=success)) + +@app.route('/nukeboard', methods=['POST']) +def nukeboard(): + admin_user = users_collection.find_one({'username': 'admin'}) + if not admin_user or session['user_id'] != str(admin_user['_id']): + return redirect(url_for('index')) + else: + board_name = request.form['board_name'] + posts_collection.delete_many({'board_name': board_name}) + success = 'board nuked!' + nuke_counter.insert_one({'board_name': board_name}, {'date': datetime.now()}) + return redirect(url_for('admin', success3=success)) + @app.route('/logout') def logout(): session.pop('user_id', None) + session.pop('username', None) return redirect(url_for('index')) if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file + app.run(debug=True, host='100.64.0.18', port=5000) \ No newline at end of file diff --git a/static/css/styles.css b/static/css/styles.css old mode 100644 new mode 100755 index ac91a87..47d412e --- a/static/css/styles.css +++ b/static/css/styles.css @@ -7,11 +7,13 @@ body { .link-container { display: flex; flex-direction: column; + justify-content: center; } .bottom { position: fixed; bottom: 2vw; + margin: 2vw; width: 100%; } @@ -30,6 +32,12 @@ a:hover { text-decoration: underline; } +p { + font-size: 1vw; + color: #00FF00; + text-decoration: none; +} + button { background-color: black; color: #00FF00; @@ -75,26 +83,166 @@ h4 { color: #00FF00; } -img { - width: 20vw; +.post_form { + display: grid; + grid-template-columns: min-content; + grid-template-rows: auto auto; + text-align: center; + align-self: center; height: auto; - margin: 2vw; + width: 20vw auto; + max-width: fit-content; + overflow: auto; + background-color: rgb(42, 42, 42); + border: 1px solid #00FF00; + padding: 0.5vw; + margin: 0.5vw; +} + +.post_input { + grid-column: 1 / span 1; + grid-row: 1 / span 1; + text-align: center; + height: 10vw auto; + width: 20vw; + max-width: fit-content; + overflow: auto; + padding: 0.5vw; + margin: 0.5vw; +} + +.post_file_input { + grid-column: 1 / span 1; + grid-row: 2 / span 1; + text-align: center; + font-size: 0.5vw; + height: auto; + width: 20vw auto; + max-width: fit-content; + border: none; + overflow: auto; + padding: 0.5vw; + margin: 0.5vw; +} + +.post_submit { + grid-column: 1 / span 1; + grid-row: 2 / span 1; + text-align: center; + height: auto; + width: 20vw; + max-width: fit-content; + overflow: auto; + padding: 0.5vw; + margin: 0.5vw; } .post { - display: flex; - flex-direction: row; - justify-content: flex-start; + display: grid; + grid-template-columns: auto auto; + grid-template-rows: auto auto; text-align: left; - height: 50% auto; - width: 50% auto; + height: auto; + width: auto; + max-width: fit-content; + overflow: auto; background-color: rgb(42, 42, 42); border: 1px solid #00FF00; - padding: 10px; - margin: 2vw; + padding: 0.5vw; + margin: 0.5vw; +} + +.post_header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + text-align: left; + height: auto; + width: auto; + max-width: fit-content; + overflow: auto; + padding: 0.5vw; + margin: 0.5vw; +} + +.post_content { + grid-row: 2 / span 1; + text-align: left; + height: auto; + width: auto; + max-width: fit-content; + overflow: auto; + padding: 0.5vw; + margin: 0.5vw; +} + +img { + grid-row: 2 / span 1; + grid-column: 1 / span 1; + width: 10vw; + height: auto; + margin: 1vw; +} + +img:hover { + width: 20vw; } .post:hover { - background-color: #00FF00; - color: black; + background-color: rgb(42, 42, 42); +} + +.navbar { + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: center; + background-color: rgb(42, 42, 42); + color: #00FF00; + padding: 0.5vw; + margin: 0; +} + +.admin_actions { + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: center; + background-color: black; + color: #00FF00; + padding: 0.5vw; + margin: 0; +} + +.admin_action { + background-color: black; + color: #00FF00; + border: 1px solid #00FF00; + max-width: fit-content; + height: 20vw; + padding: 1vw; + cursor: pointer; +} + +.user_list { + display: grid; + grid-template-columns: auto auto; + grid-template-rows: auto auto; + grid-gap: 1vw; + text-align: left; + height: auto; + width: auto; + max-width: fit-content; + overflow: auto; + padding: 0.5vw; + margin: 0.5vw; +} + +.user_list a { + font-size: 2vw; + text-decoration: none; +} + +.stats { + text-align: center; + justify-content: center; } \ No newline at end of file diff --git a/templates/admin.html b/templates/admin.html index 8f214b2..d5cee5d 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -7,14 +7,60 @@
-

admin panel

-
-

delete post

-

{{ success }}

-
- - -
+ +

admin panel

+
+

stats

+

total posts: {{ total_posts }}

+

total users: {{ total_users }}

+

nuke count: {{ nuke_count }}

+
+
+
+

delete post

+

{{ success1 }}

+
+ + +
+
+
+

delete user

+

{{ success2 }}

+ {% for user in users %} + {% if user.username != 'admin' %} +
+ {{ user.username }} + + + {% endif %} + {% endfor %} +
+
+
+

nuke board

+

{{ success3 }}

+
+ + +
+
diff --git a/templates/board.html b/templates/board.html index 6a9aee1..bc98569 100644 --- a/templates/board.html +++ b/templates/board.html @@ -8,28 +8,44 @@ {% block content %}
lainlounge.xyz - copyleft all wrongs released diff --git a/templates/login.html b/templates/login.html index 49e5dec..6e4286e 100644 --- a/templates/login.html +++ b/templates/login.html @@ -5,15 +5,17 @@ -

login

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