feat: Add bulk uploading
All checks were successful
Build Docker / BuildImage (push) Successful in 1m44s

This commit is contained in:
2025-07-29 12:27:32 +10:00
parent 402e321396
commit 55e6306e35
5 changed files with 116 additions and 1 deletions

View File

@@ -262,6 +262,12 @@ def startTGBot(mainThread: bool = False):
print( print(
"Telegram bot token or name not set. Notifications via Telegram will not work.") "Telegram bot token or name not set. Notifications via Telegram will not work.")
return return
# Check if DEV=true
if os.getenv('DEV', 'false').lower() == 'true':
print("Development mode is enabled. Skipping Telegram bot start.")
return
if TG_bot_running: if TG_bot_running:
print("Telegram bot is already running.") print("Telegram bot is already running.")

View File

@@ -135,6 +135,89 @@ def logout():
return response return response
@app.route("/bulk_upload", methods=["POST"])
def bulk_upload_notifications():
"""
Bulk upload notifications from a CSV file.
"""
token = request.cookies.get("token")
if not token:
return redirect(f"https://login.hns.au/auth?return={request.host_url}login")
user_data = requests.get(f"https://login.hns.au/auth/user?token={token}")
if user_data.status_code != 200:
return redirect(f"https://login.hns.au/auth?return={request.host_url}login")
user_data = user_data.json()
username = user_data.get("username", None)
if not username:
return jsonify({"error": "Invalid user data"}), 400
# Check if the request contains a file
if 'file' not in request.files:
return jsonify({"error": "No file part in the request"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
# Read the CSV file
try:
content = file.read().decode('utf-8')
lines = content.splitlines()
for line in lines:
parts = line.split(',')
if len(parts) < 3:
continue # Skip invalid lines
domain = parts[0].strip()
blocks = parts[1].strip()
notification_type = parts[2].strip().lower() # Normalize to lowercase
# Find the notification type
notificationType = None
for notification in NOTIFICATION_TYPES:
if notification['type'] == notification_type:
notificationType = notification
break
else:
return jsonify({"error": f"Invalid notification type: {notification_type}"}), 400
continue # Skip invalid notification types
# Create the notification data
notification_data = {
'domain': domain,
'blocks': blocks,
'type': notification_type,
'user_name': username,
'id': os.urandom(16).hex() # Generate a random ID for the notification
}
arg = 3
# Add additional fields based on the notification type
for field in notificationType['fields']:
if field['name'] not in notification_data and field.get('required', False):
# Try to read the field from the line
if len(parts) > arg:
field_value = parts[arg].strip()
if field_value:
notification_data[field['name']] = field_value
else:
return jsonify({"error": f"Missing required field: {field['name']}"}), 400
else:
# Auto fill default values for username
if field['type'] == 'username':
notification_data[field['name']] = username
else:
return jsonify({"error": f"Missing required field: {field['name']}"}), 400
print(notification_data)
domains.add_notification(domain, notification_data)
return redirect(f"{request.host_url}account")
except Exception as e:
return jsonify({"error": f"Failed to process file: {str(e)}"}), 500
@app.route("/notification/<notificationtype>", methods=["POST"]) @app.route("/notification/<notificationtype>", methods=["POST"])
def addNotification(notificationtype: str): def addNotification(notificationtype: str):
""" """
@@ -252,7 +335,6 @@ def catch_all(path: str):
return render_template("404.html"), 404 return render_template("404.html"), 404
# endregion # endregion
# region API routes # region API routes

View File

@@ -126,6 +126,28 @@
{% endfor %} {% endfor %}
</div> </div>
</section> </section>
<section class="add-alerts-section">
<h2>Bulk Upload Domains</h2>
<p class="section-description">Upload a CSV file with your Handshake domains to set up multiple alerts at once.</p>
<div class="bulk-upload-card">
<form method="POST" action="/bulk_upload" enctype="multipart/form-data" class="bulk-upload-form">
<div class="form-group">
<label for="bulk-file">CSV File:</label>
<input type="file" id="bulk-file" name="file" accept=".csv" required>
<small class="form-note">Upload a CSV file with one domain per line.</small>
</div>
<div class="form-actions">
<button type="submit" class="button primary">Upload CSV</button>
</div>
</form>
<div class="bulk-upload-info">
<p>Format: Each line should contain a domain name, followed by the type of notification (e.g., "exampledomain, email") & notification parameters</p>
<p>Example: <code>exampledomain, email</code></p>
<p>Supported types: email, discord, slack, webhook</p>
<p>Download example CSV: <a href="/assets/csv/example.csv" class="button secondary" download>Download Example CSV</a></p>
</div>
</section>
</main> </main>
</div> </div>
</body> </body>

View File

@@ -29,6 +29,8 @@
.alerts-section, .add-alerts-section { .alerts-section, .add-alerts-section {
margin-bottom: 60px; margin-bottom: 60px;
max-width: 1000px;
margin: 16px auto;
} }
.alerts-section h2, .add-alerts-section h2 { .alerts-section h2, .add-alerts-section h2 {

View File

@@ -0,0 +1,3 @@
woodburn,1008,email,example@woodburn.au
woodburn1,1008,discord_webhook,https://discord.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyz
woodburn2,1008,telegram
1 woodburn,1008,email,example@woodburn.au
2 woodburn1,1008,discord_webhook,https://discord.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyz
3 woodburn2,1008,telegram