Compare commits

...

91 Commits

Author SHA1 Message Date
504d237405 swag 2026-02-25 15:53:41 +00:00
6696bcc4e7 a 2026-02-25 15:36:18 +00:00
32a1458218 a 2026-02-25 15:33:42 +00:00
4697086f86 a 2026-02-25 15:32:36 +00:00
c4b9e5f360 w 2026-02-25 15:14:30 +00:00
0a9934b889 b 2026-02-25 15:14:07 +00:00
8ad1650079 a 2026-02-25 15:13:51 +00:00
6e50013a84 swag 2026-02-25 15:03:45 +00:00
dba9a7690d a 2026-02-25 14:59:35 +00:00
319d237e5d omg 2026-02-25 14:58:11 +00:00
af6515e55f a 2026-02-25 14:44:54 +00:00
1b83098fd0 swag 2026-02-25 14:39:11 +00:00
ec11a3d4c4 a 2026-02-25 14:28:49 +00:00
024b829562 a 2026-02-25 14:26:55 +00:00
d918540252 a 2026-02-25 14:26:20 +00:00
9022704e5c a 2026-02-25 14:25:46 +00:00
4046d25aa7 a 2026-02-25 14:07:04 +00:00
aac2ec7ce2 a 2026-02-25 14:06:32 +00:00
a714814ad0 a 2026-02-25 14:05:45 +00:00
47555303e2 a 2026-02-25 14:04:18 +00:00
9c83537d65 a 2026-02-25 14:03:39 +00:00
b7d47b130d a 2026-02-25 13:59:30 +00:00
baa8c1392d a 2026-02-25 13:58:29 +00:00
b18012713e a 2026-02-25 13:58:11 +00:00
b714a6c4e6 a 2026-02-25 13:57:08 +00:00
7188ad7c75 a 2026-02-25 13:55:06 +00:00
68314ba7af a 2026-02-25 13:54:30 +00:00
f1e591ab5b a 2026-02-25 13:50:50 +00:00
ee5d1b2758 a 2026-02-25 13:48:10 +00:00
7f2d0ec2f6 a 2026-02-25 13:47:10 +00:00
2b22eaf0e9 slop 2026-02-25 13:46:14 +00:00
19d646aa40 a 2026-02-25 13:43:50 +00:00
acd57b90a8 update 2026-02-25 13:42:03 +00:00
d63d9cdb78 swag 2026-02-25 13:32:06 +00:00
2ca97cba47 a 2026-02-25 12:19:29 +00:00
5ee126d0ec a 2026-02-25 11:51:28 +00:00
0d69fcc296 a 2026-02-25 11:20:20 +00:00
46c99f1ae0 a 2026-02-25 11:18:10 +00:00
1801e6d054 a 2026-02-25 11:10:52 +00:00
ae17970d8c a 2026-02-25 11:05:31 +00:00
30fef37d45 a 2026-02-25 11:03:59 +00:00
76e90450f8 aa 2026-02-25 11:03:40 +00:00
845a1c8494 a 2026-02-25 11:03:16 +00:00
4104ee47c4 a 2026-02-25 10:40:55 +00:00
597900d7b6 a 2026-02-25 09:02:32 +00:00
942a3adaf6 a 2026-02-25 09:00:23 +00:00
96960c17a1 a 2026-02-24 14:57:24 +00:00
6f7fc56e32 a 2026-02-24 14:55:11 +00:00
1cfa2668b2 a 2026-02-24 14:53:20 +00:00
f524ed4e6a aaa 2026-02-24 14:27:28 +00:00
245f524c38 typo 2026-02-24 14:24:37 +00:00
76f857ca0a add: time 2026-02-24 14:23:56 +00:00
6aeea9dedb add: contenttype 2026-02-24 13:06:48 +00:00
dd6ca2d75b add: json 2026-02-24 13:04:50 +00:00
d9796b0b87 typo 2026-02-24 12:50:25 +00:00
9958763f6f add: client 2026-02-24 12:47:07 +00:00
a71f3f3681 add: edit 2026-02-20 15:07:45 +00:00
9f8a9eebfe a 2026-02-20 15:03:22 +00:00
a1fc7a4b27 a 2026-02-20 14:55:12 +00:00
0e8b558747 a 2026-02-20 14:54:16 +00:00
b6d60e7210 a 2026-02-20 14:52:55 +00:00
bf31f647e7 a 2026-02-20 14:50:50 +00:00
121cc91ad1 a 2026-02-20 14:36:54 +00:00
ac2b23c459 a 2026-02-20 14:35:03 +00:00
10675cffbc boolean bruh 2026-02-20 14:33:18 +00:00
32dee47628 a 2026-02-20 14:19:27 +00:00
c08f81bd03 a 2026-02-20 13:40:04 +00:00
5063d78198 a 2026-02-20 13:37:30 +00:00
a12366867d add: whitelist 2026-02-20 13:36:02 +00:00
614dea44b4 add a 2026-02-20 10:58:52 +00:00
305b269093 add: db 2026-02-20 10:55:55 +00:00
612ee5c740 a 2026-02-20 09:48:06 +00:00
64c2269c0a add: lookup 2026-02-20 09:44:08 +00:00
f3ac9a8c1b a 2026-02-20 09:00:55 +00:00
db414a0b03 dicts 2026-02-20 09:00:47 +00:00
51e9b87a3f az 2026-02-19 15:35:16 +00:00
4444d976e6 a 2026-02-19 15:34:06 +00:00
4036a15999 a 2026-02-19 15:33:06 +00:00
3755a1fef5 a 2026-02-19 15:31:40 +00:00
323a62c29a z 2026-02-19 15:14:23 +00:00
faac097a98 a 2026-02-19 15:13:42 +00:00
882dc94190 a 2026-02-19 15:10:38 +00:00
53d767a773 c 2026-02-19 15:09:05 +00:00
e2e0fe6175 b 2026-02-19 15:08:36 +00:00
7a74ce6227 a 2026-02-19 15:08:30 +00:00
d94086a3ef better css 2026-02-19 15:07:51 +00:00
6504cdc0fd fix 2026-02-19 15:03:51 +00:00
e8ea43124a add: endpoint 2026-02-19 15:03:08 +00:00
bfc2ac286d fix 2026-02-19 14:57:02 +00:00
035dd4a9c7 Merge branch 'main' of https://git.vavaas.dev/Namonay/bg_shutdown 2026-02-19 14:52:38 +00:00
a244a27cff add: bootstrat ig 2026-02-19 14:52:03 +00:00
4 changed files with 335 additions and 15 deletions

86
client/bs_shutdown.py Normal file
View File

@@ -0,0 +1,86 @@
import urllib.request
import json
import socket
from datetime import datetime
import subprocess
import time
DEFAULT_SHUTDOWN = "21:00"
LAST_VALUE = 0
machine_name = socket.gethostname()
url = "http://192.168.50.27:5000/lookup"
payload = {
"name": machine_name,
}
def get_data():
data = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(
url,
headers={"Content-Type": "application/json"},
data=data,
method="POST"
)
try:
with urllib.request.urlopen(req, timeout=10) as response:
data = response.read()
json_data = json.loads(data)
print(json_data)
return json_data
except:
return None
def to_seconds(t):
return t.hour * 3600 + t.minute * 60 + t.second
def set_shutdown(time, now):
global LAST_VALUE
if (time - now) < 0 or time == LAST_VALUE:
return
LAST_VALUE = time
print("[BS Shutdown] shutting down in " + str(time - now) + " seconds")
# subprocess.run(["shutdown", "/s", "/t", (time - now), " "])
def remove_shutdown():
print("[BS Shutdown] Removed shutdown")
# subprocess.run(["shutdown", "/a"])
def update():
global LAST_VALUE
data = get_data()
if data is not None and data["noshutdown"] == 1:
if (LAST_VALUE != 0):
remove_shutdown() # In case
LAST_VALUE = 0
return
if data is None:
time = to_seconds(datetime.strptime(DEFAULT_SHUTDOWN, "%H:%M").time())
else:
time = to_seconds(datetime.strptime(data["time"], "%H:%M").time())
now = to_seconds(datetime.now().time())
if (time - now) < 0:
return
if (time == LAST_VALUE):
return
if (LAST_VALUE != 0):
remove_shutdown()
print("[BS Shutdown] New value")
LAST_VALUE = time
set_shutdown(time, now)
if __name__ == '__main__':
while True:
update()
time.sleep(30)

View File

@@ -1,7 +1,125 @@
from flask import Flask, render_template
from flask import Flask, render_template, request, redirect, url_for, jsonify
import sqlite3
import re
app = Flask(__name__)
pattern = re.compile(r"^(?:[01]\d|2[0-3]):[0-5]\d$") # Regex "XX:XX"
DB_PATH = "data.db"
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
time TEXT,
noshutdown INTEGER DEFAULT 0
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS val (
default_time TEXT NOT NULL DEFAULT '21:00'
)
""")
cursor.execute("SELECT COUNT(*) FROM val")
count = cursor.fetchone()[0]
if count == 0:
cursor.execute("INSERT INTO val (default_time) VALUES (?)", ('21:00',))
conn.commit()
conn.close()
def get_db_connection():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
@app.route("/")
def mainpage():
return render_template('main.html', title='BG Shutdown')
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT name, time, noshutdown FROM items ORDER BY name")
items = cursor.fetchall()
cursor.execute("SELECT default_time FROM val")
default_time_row = cursor.fetchone()
default_time = default_time_row[0] if default_time_row else '21:00'
conn.close()
return render_template('index.html', title='BS Shutdown', items=items, default_time=default_time)
@app.route('/submit', methods =['POST'])
def submit():
value = request.form.get("query")
time_value = request.form.get("time")
noshutdown = 1 if request.form.get("noshutdown") == "on" else 0
if not value:
return redirect(url_for("mainpage"))
if noshutdown == 0 and not time_value:
return redirect(url_for("mainpage"))
if noshutdown == 1:
time_value = 'None'
if time_value != 'None':
if bool(pattern.match(time_value)) is not True:
return redirect(url_for("mainpage"))
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT 1 FROM items WHERE name = ?', (value,))
item = cursor.fetchone()
if item:
cursor.execute("UPDATE items SET time = ?, noshutdown = ? WHERE name = ?", (time_value, noshutdown, value))
else:
cursor.execute("INSERT INTO items (name, time, noshutdown) VALUES (?, ?, ?)", (value, time_value, noshutdown))
conn.commit()
conn.close()
return redirect(url_for("mainpage"))
@app.route("/submit-default", methods=["POST"])
def submit_default():
time_value = request.form.get("default-time")
conn = get_db_connection()
cursor = conn.cursor()
if bool(pattern.match(time_value)) is not True:
return redirect(url_for("mainpage"))
cursor.execute("UPDATE val SET default_time = ?", (time_value,))
conn.commit()
conn.close()
return redirect(url_for("mainpage"))
@app.route("/delete", methods=["POST"])
def delete():
value = request.form.get("item_name")
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT 1 FROM items WHERE name = ?', (value,))
item = cursor.fetchone()
if not item:
conn.close()
return redirect(url_for("mainpage"))
cursor.execute(
"DELETE FROM items WHERE name = ?",
(value,)
)
conn.commit()
conn.close()
return redirect(url_for("mainpage"))
@app.route("/lookup", methods=["POST"])
def lookup():
data = request.get_json()
if not data or "name" not in data:
return jsonify({"error": "Not found"}), 404
name = data["name"]
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"SELECT name, time, noshutdown FROM items WHERE name = ?",
(name,)
)
item = cursor.fetchone()
conn.close()
if item:
return jsonify({"name": item["name"], "noshutdown": item["noshutdown"], "time": item["time"]}), 200
else:
return jsonify({"error": "Not found"}), 404

View File

@@ -35,23 +35,17 @@
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{{ url_for('home') }}">Mon Portfolio</a>
<a class="navbar-brand" href="{{ url_for('mainpage') }}">BS Shutdown</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link {% if title=='Accueil' %}active{% endif %}" href="{{ url_for('home') }}">Accueil</a>
<a class="nav-link {% if title=='BS Shutdown' %}active{% endif %}" href="{{ url_for('mainpage') }}">Accueil</a>
</li>
<li class="nav-item">
<a class="nav-link {% if title=='Projets' %}active{% endif %}" href="{{ url_for('projects') }}">Projets</a>
</li>
<li class="nav-item">
<a class="nav-link {% if title=='Horse' %}active{% endif %}" href="{{ url_for('horse') }}">Horse</a>
</li>
<li class="nav-item">
<a class="nav-link {% if title=='Contact' %}active{% endif %}" href="{{ url_for('contact') }}">Contact</a>
<a class="nav-link {% if title=='BG Shutdown' %}active{% endif %}" href="{{ url_for('mainpage') }}">Groupes</a>
</li>
</ul>
</div>
@@ -59,15 +53,14 @@
</nav>
</header>
<main class="container my-5">
<main class="container my-5 flex-fill">
{% block content %}{% endblock %}
</main>
<footer class="bg-dark text-center text-lg-start py-3 border-top">
<div class="container d-flex justify-content-center align-items-center">
<p class="mb-0 text-light">
&copy; 2025 Avec amour et
<img src="{{ url_for('static', filename='img/monster.png') }}" alt="Battery" width="16" height="16" class="ms-1">
Spirit
</p>
</div>
</footer>

View File

@@ -1,4 +1,127 @@
{% extends "base.html" %}
{% block content %}
{ % endblock % }
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<div class="container py-4">
<div class="card bg-dark bg-opacity-75 p-3 mb-4">
<div class="row g-2 align-items-center">
<div class="col-10">
<form method="post" action="/submit" class="row g-2 align-items-center">
<div class="col-md-2">
<input
type="text"
name="query"
class="form-control bg-dark text-white"
placeholder="Nom de la machine">
</div>
<div class="col-md-2">
<select name="time" class="form-select bg-dark text-white" id="time-select">
{% for hour in range(0, 24) %}
<option value="{{ '%02d:00'|format(hour) }}">{{ '%02d:00'|format(hour) }}</option>
<option value="{{ '%02d:30'|format(hour) }}">{{ '%02d:30'|format(hour) }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-2">
<div class="form-check text-white">
<input class="form-check-input" type="checkbox" name="noshutdown" id="noshutdown">
<label class="form-check-label" for="noshutdown">Whitelist shutdown</label>
</div>
</div>
<div class="col-md-1">
<button type="submit" class="btn btn-sm btn-primary w-100">
<i class="bi bi-plus-lg"> Save</i>
</button>
</div>
</form>
</div>
<div class="col-auto gap-1 ms-auto">
<form method="post" action="/submit-default" class="d-flex align-items-center gap-1">
<select name="default-time" class="form-select bg-dark text-white flex-grow-1" id="default-time-select">
{% for hour in range(0, 24) %}
{% set h1 = '%02d:00'|format(hour) %}
{% set h2 = '%02d:30'|format(hour) %}
<option value="{{ h1 }}"
{% if h1 == default_time %}selected{% endif %}>
{{ h1 }}
</option>
<option value="{{ h2 }}"
{% if h2 == default_time %}selected{% endif %}>
{{ h2 }}
</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-sm btn-primary w-100">
<i class="bi bi-save"> Save</i>
</button>
</form>
</div>
</div>
</div>
<div class="card bg-dark bg-opacity-75 p-3">
<h5 class="text-white mb-3">Liste des éléments</h5>
<div class="row fw-bold text-white mb-2">
<div class="col-4">Nom</div>
<div class="col-3">Whitelist</div>
<div class="col-3">Time</div>
<div class="col-2 text-end">Actions</div>
</div>
<div class="table-items">
{% for item in items %}
<div class="row align-items-center py-2">
<div class="col-4 text-white">
{{ item.name }}
</div>
<div class="col-3">
{% if item.noshutdown == 1 %}
<span class="text-success">OUI</span>
{% else %}
<span class="text-danger">NON</span>
{% endif %}
</div>
<div class="col-3 text-white">
{% if item.noshutdown == 0 %}
{{ item.time }}
{% endif %}
</div>
<div class="col-2 text-end">
<form method="post" action="/delete" class="m-0">
<input type="hidden" name="item_name" value="{{ item.name }}">
<button type="submit" class="btn btn-sm btn-danger">
<i class="bi bi-x-lg"></i>Supprimer
</button>
</form>
</div>
</div>
{% if not loop.last %}
<hr class="border-secondary my-1">
{% endif %}
{% endfor %}
</div>
</div>
</div>
<script>
const tickCheckbox = document.getElementById('noshutdown'); // script pr desactiver la selection de l'heure si whitelist
const timeSelect = document.getElementById('time-select');
timeSelect.disabled = tickCheckbox.checked;
tickCheckbox.addEventListener('change', function() {
timeSelect.disabled = this.checked;
});
</script>
{% endblock %}