Initial commit - EcoCharge backend API + admin
This commit is contained in:
106
app.py
Normal file
106
app.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
from flask import Flask, jsonify, render_template, request, redirect, url_for, session
|
||||||
|
from flask_cors import CORS
|
||||||
|
from config import Config
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object(Config)
|
||||||
|
app.secret_key = app.config["SECRET_KEY"]
|
||||||
|
|
||||||
|
CORS(app)
|
||||||
|
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
host="localhost",
|
||||||
|
port=5432,
|
||||||
|
database="ecocharge",
|
||||||
|
user="eco",
|
||||||
|
password="eco_pass"
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def home():
|
||||||
|
return jsonify({
|
||||||
|
"message": "API EcoCharge en ligne"
|
||||||
|
})
|
||||||
|
|
||||||
|
@app.route("/api/status")
|
||||||
|
def status():
|
||||||
|
return jsonify({
|
||||||
|
"project": "EcoCharge",
|
||||||
|
"api": "ok",
|
||||||
|
"database": "postgresql_connected"
|
||||||
|
})
|
||||||
|
|
||||||
|
@app.route("/admin/login", methods=["GET", "POST"])
|
||||||
|
def admin_login():
|
||||||
|
if request.method == "POST":
|
||||||
|
username = request.form.get("username")
|
||||||
|
password = request.form.get("password")
|
||||||
|
|
||||||
|
if username == "admin" and password == "admin123":
|
||||||
|
session["admin_logged_in"] = True
|
||||||
|
return redirect(url_for("admin_dashboard"))
|
||||||
|
|
||||||
|
return render_template("login.html", error="Identifiants incorrects")
|
||||||
|
|
||||||
|
return render_template("login.html")
|
||||||
|
|
||||||
|
@app.route("/admin/dashboard", methods=["GET", "POST"])
|
||||||
|
def admin_dashboard():
|
||||||
|
if not session.get("admin_logged_in"):
|
||||||
|
return redirect(url_for("admin_login"))
|
||||||
|
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
min_battery = request.form.get("min_battery")
|
||||||
|
max_temp = request.form.get("max_temp")
|
||||||
|
min_power = request.form.get("min_power")
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
INSERT INTO settings (key, value)
|
||||||
|
VALUES ('min_battery_voltage', %s)
|
||||||
|
ON CONFLICT (key)
|
||||||
|
DO UPDATE SET value = EXCLUDED.value
|
||||||
|
""", (min_battery,))
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
INSERT INTO settings (key, value)
|
||||||
|
VALUES ('max_battery_temperature', %s)
|
||||||
|
ON CONFLICT (key)
|
||||||
|
DO UPDATE SET value = EXCLUDED.value
|
||||||
|
""", (max_temp,))
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
INSERT INTO settings (key, value)
|
||||||
|
VALUES ('min_solar_power', %s)
|
||||||
|
ON CONFLICT (key)
|
||||||
|
DO UPDATE SET value = EXCLUDED.value
|
||||||
|
""", (min_power,))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
cur.execute("SELECT value FROM settings WHERE key = 'min_battery_voltage'")
|
||||||
|
row1 = cur.fetchone()
|
||||||
|
|
||||||
|
cur.execute("SELECT value FROM settings WHERE key = 'max_battery_temperature'")
|
||||||
|
row2 = cur.fetchone()
|
||||||
|
|
||||||
|
cur.execute("SELECT value FROM settings WHERE key = 'min_solar_power'")
|
||||||
|
row3 = cur.fetchone()
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
"min_battery": row1[0] if row1 else "10.5",
|
||||||
|
"max_temp": row2[0] if row2 else "45",
|
||||||
|
"min_power": row3[0] if row3 else "5"
|
||||||
|
}
|
||||||
|
|
||||||
|
return render_template("admin_dashboard.html", settings=settings)
|
||||||
|
|
||||||
|
@app.route("/admin/logout")
|
||||||
|
def admin_logout():
|
||||||
|
session.pop("admin_logged_in", None)
|
||||||
|
return redirect(url_for("admin_login"))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||||
13
config.py
Normal file
13
config.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
SECRET_KEY = os.getenv("SECRET_KEY", "ecocharge_secret_2026")
|
||||||
|
|
||||||
|
DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||||
|
DB_PORT = int(os.getenv("DB_PORT", 3306))
|
||||||
|
DB_NAME = os.getenv("DB_NAME", "ecocharge")
|
||||||
|
DB_USER = os.getenv("DB_USER", "root")
|
||||||
|
DB_PASSWORD = os.getenv("DB_PASSWORD", "root")
|
||||||
323
database.sql
Normal file
323
database.sql
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
--
|
||||||
|
-- PostgreSQL database dump
|
||||||
|
--
|
||||||
|
|
||||||
|
\restrict ijnV8LNUvXkT7zfNUtK8rYjTPMZ7E4a8Dp7BES0I4ggucAotiMgPl90yvhq0XQq
|
||||||
|
|
||||||
|
-- Dumped from database version 18.3 (Debian 18.3-1.pgdg13+1)
|
||||||
|
-- Dumped by pg_dump version 18.3 (Debian 18.3-1.pgdg13+1)
|
||||||
|
|
||||||
|
SET statement_timeout = 0;
|
||||||
|
SET lock_timeout = 0;
|
||||||
|
SET idle_in_transaction_session_timeout = 0;
|
||||||
|
SET transaction_timeout = 0;
|
||||||
|
SET client_encoding = 'UTF8';
|
||||||
|
SET standard_conforming_strings = on;
|
||||||
|
SELECT pg_catalog.set_config('search_path', '', false);
|
||||||
|
SET check_function_bodies = false;
|
||||||
|
SET xmloption = content;
|
||||||
|
SET client_min_messages = warning;
|
||||||
|
SET row_security = off;
|
||||||
|
|
||||||
|
SET default_tablespace = '';
|
||||||
|
|
||||||
|
SET default_table_access_method = heap;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: settings; Type: TABLE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.settings (
|
||||||
|
id integer NOT NULL,
|
||||||
|
key character varying(50),
|
||||||
|
value character varying(100)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.settings OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: settings_id_seq; Type: SEQUENCE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.settings_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.settings_id_seq OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: settings_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.settings_id_seq OWNED BY public.settings.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: system_status; Type: TABLE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.system_status (
|
||||||
|
id integer NOT NULL,
|
||||||
|
device_name character varying(50),
|
||||||
|
online boolean,
|
||||||
|
last_seen timestamp without time zone
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.system_status OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: system_status_id_seq; Type: SEQUENCE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.system_status_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.system_status_id_seq OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: system_status_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.system_status_id_seq OWNED BY public.system_status.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: telemetry; Type: TABLE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.telemetry (
|
||||||
|
id integer NOT NULL,
|
||||||
|
"timestamp" timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
upv double precision,
|
||||||
|
ipv double precision,
|
||||||
|
ubat double precision,
|
||||||
|
ibat double precision,
|
||||||
|
power double precision,
|
||||||
|
temperature double precision,
|
||||||
|
luminosity double precision,
|
||||||
|
humidity double precision
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.telemetry OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: telemetry_id_seq; Type: SEQUENCE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.telemetry_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.telemetry_id_seq OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: telemetry_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.telemetry_id_seq OWNED BY public.telemetry.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: users; Type: TABLE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE public.users (
|
||||||
|
id integer NOT NULL,
|
||||||
|
username character varying(50),
|
||||||
|
password character varying(255),
|
||||||
|
is_admin boolean DEFAULT false
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE public.users OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE public.users_id_seq
|
||||||
|
AS integer
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.users_id_seq OWNER TO eco;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: settings id; Type: DEFAULT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.settings ALTER COLUMN id SET DEFAULT nextval('public.settings_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: system_status id; Type: DEFAULT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.system_status ALTER COLUMN id SET DEFAULT nextval('public.system_status_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: telemetry id; Type: DEFAULT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.telemetry ALTER COLUMN id SET DEFAULT nextval('public.telemetry_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: users id; Type: DEFAULT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Data for Name: settings; Type: TABLE DATA; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
COPY public.settings (id, key, value) FROM stdin;
|
||||||
|
1 temp_max 60
|
||||||
|
2 battery_min 11
|
||||||
|
3 solar_max 22
|
||||||
|
\.
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Data for Name: system_status; Type: TABLE DATA; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
COPY public.system_status (id, device_name, online, last_seen) FROM stdin;
|
||||||
|
1 esp32 f 2026-03-05 15:07:28.827398
|
||||||
|
\.
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Data for Name: telemetry; Type: TABLE DATA; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
COPY public.telemetry (id, "timestamp", upv, ipv, ubat, ibat, power, temperature, luminosity, humidity) FROM stdin;
|
||||||
|
1 2026-03-05 14:51:17.119119 18.5 0.7 12.4 0.5 13 32 500 45
|
||||||
|
2 2026-03-05 15:07:19.878569 19 0.8 12.5 0.5 14 31 600 40
|
||||||
|
\.
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
COPY public.users (id, username, password, is_admin) FROM stdin;
|
||||||
|
1 admin admin123 t
|
||||||
|
\.
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: settings_id_seq; Type: SEQUENCE SET; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT pg_catalog.setval('public.settings_id_seq', 3, true);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: system_status_id_seq; Type: SEQUENCE SET; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT pg_catalog.setval('public.system_status_id_seq', 1, true);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: telemetry_id_seq; Type: SEQUENCE SET; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT pg_catalog.setval('public.telemetry_id_seq', 2, true);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: users_id_seq; Type: SEQUENCE SET; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT pg_catalog.setval('public.users_id_seq', 1, true);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: settings settings_key_key; Type: CONSTRAINT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.settings
|
||||||
|
ADD CONSTRAINT settings_key_key UNIQUE (key);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.settings
|
||||||
|
ADD CONSTRAINT settings_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: system_status system_status_pkey; Type: CONSTRAINT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.system_status
|
||||||
|
ADD CONSTRAINT system_status_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: telemetry telemetry_pkey; Type: CONSTRAINT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.telemetry
|
||||||
|
ADD CONSTRAINT telemetry_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.users
|
||||||
|
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: users users_username_key; Type: CONSTRAINT; Schema: public; Owner: eco
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.users
|
||||||
|
ADD CONSTRAINT users_username_key UNIQUE (username);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- PostgreSQL database dump complete
|
||||||
|
--
|
||||||
|
|
||||||
|
\unrestrict ijnV8LNUvXkT7zfNUtK8rYjTPMZ7E4a8Dp7BES0I4ggucAotiMgPl90yvhq0XQq
|
||||||
|
|
||||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Flask==3.0.3
|
||||||
|
flask-cors==4.0.1
|
||||||
|
python-dotenv==1.0.1
|
||||||
|
mysql-connector-python==9.0.0
|
||||||
|
Werkzeug==3.0.3
|
||||||
78
templates/admin_dashboard.html
Normal file
78
templates/admin_dashboard.html
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Admin EcoCharge</title>
|
||||||
|
<style>
|
||||||
|
body{
|
||||||
|
font-family: Arial;
|
||||||
|
background:#f5f7fb;
|
||||||
|
padding:40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card{
|
||||||
|
background:white;
|
||||||
|
padding:30px;
|
||||||
|
border-radius:10px;
|
||||||
|
box-shadow:0 4px 12px rgba(0,0,0,0.1);
|
||||||
|
max-width:500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2{
|
||||||
|
margin-top:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label{
|
||||||
|
display:block;
|
||||||
|
margin-top:15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input{
|
||||||
|
width:100%;
|
||||||
|
padding:8px;
|
||||||
|
margin-top:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button{
|
||||||
|
margin-top:20px;
|
||||||
|
padding:10px;
|
||||||
|
background:#2563eb;
|
||||||
|
color:white;
|
||||||
|
border:none;
|
||||||
|
border-radius:6px;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
display:block;
|
||||||
|
margin-top:20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
|
||||||
|
<h2>Administration EcoCharge</h2>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
|
||||||
|
<label>Seuil batterie minimum</label>
|
||||||
|
<input type="number" step="0.1" name="min_battery" value="{{ settings.min_battery }}">
|
||||||
|
|
||||||
|
<label>Température max batterie</label>
|
||||||
|
<input type="number" step="0.1" name="max_temp" value="{{ settings.max_temp }}">
|
||||||
|
|
||||||
|
<label>Puissance solaire minimum</label>
|
||||||
|
<input type="number" step="0.1" name="min_power" value="{{ settings.min_power }}">
|
||||||
|
|
||||||
|
<button type="submit">Sauvegarder</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<a href="/admin/logout">Se déconnecter</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
87
templates/login.html
Normal file
87
templates/login.html
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Connexion Admin - EcoCharge</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background: #f4f7fb;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
width: 360px;
|
||||||
|
margin: 100px auto;
|
||||||
|
background: white;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 18px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
margin-top: 15px;
|
||||||
|
color: #374151;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #2563eb;
|
||||||
|
color: white;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #1d4ed8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="login-container">
|
||||||
|
<h2>Connexion Admin</h2>
|
||||||
|
<form method="POST" action="/admin/login">
|
||||||
|
<label for="username">Nom d'utilisateur</label>
|
||||||
|
<input type="text" id="username" name="username" required>
|
||||||
|
|
||||||
|
<label for="password">Mot de passe</label>
|
||||||
|
<input type="password" id="password" name="password" required>
|
||||||
|
|
||||||
|
<button type="submit">Se connecter</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if error %}
|
||||||
|
<p class="error">{{ error }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user