issamlaradji commited on
Commit
897006f
·
verified ·
1 Parent(s): 22b65ef

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +154 -0
  2. data/users.json +1 -0
  3. templates/index.html +333 -0
app.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, redirect, url_for, session, flash
2
+ import json
3
+ import os
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+
7
+ app = Flask(__name__)
8
+ app.secret_key = "your-secret-key-change-in-production"
9
+
10
+ DATA_DIR = Path("data")
11
+ USERS_FILE = DATA_DIR / "users.json"
12
+
13
+
14
+ # Initialize data directory and default user
15
+ def init_data():
16
+ DATA_DIR.mkdir(exist_ok=True)
17
+ if not USERS_FILE.exists():
18
+ default_user = {"admin": {"password": "123"}}
19
+ with open(USERS_FILE, "w") as f:
20
+ json.dump(default_user, f)
21
+
22
+
23
+ init_data()
24
+
25
+
26
+ # User management
27
+ def load_users():
28
+ with open(USERS_FILE, "r") as f:
29
+ return json.load(f)
30
+
31
+
32
+ def save_users(users):
33
+ with open(USERS_FILE, "w") as f:
34
+ json.dump(users, f, indent=2)
35
+
36
+
37
+ # Note management
38
+ def get_user_notes(username):
39
+ user_dir = DATA_DIR / username
40
+ if not user_dir.exists():
41
+ return []
42
+
43
+ notes = []
44
+ for note_file in sorted(user_dir.glob("note_*.txt"), reverse=True):
45
+ timestamp = note_file.stem.replace("note_", "")
46
+ with open(note_file, "r") as f:
47
+ content = f.read()
48
+ notes.append(
49
+ {"timestamp": timestamp, "content": content, "filename": note_file.name}
50
+ )
51
+ return notes
52
+
53
+
54
+ def save_note(username, content):
55
+ user_dir = DATA_DIR / username
56
+ user_dir.mkdir(exist_ok=True)
57
+
58
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
59
+ note_file = user_dir / f"note_{timestamp}.txt"
60
+
61
+ with open(note_file, "w") as f:
62
+ f.write(content)
63
+
64
+
65
+ def delete_note(username, filename):
66
+ note_file = DATA_DIR / username / filename
67
+ if note_file.exists():
68
+ note_file.unlink()
69
+
70
+
71
+ # Routes
72
+ @app.route("/")
73
+ def index():
74
+ if "username" in session:
75
+ return redirect(url_for("dashboard"))
76
+ return render_template("index.html", page="login")
77
+
78
+
79
+ @app.route("/login", methods=["POST"])
80
+ def login():
81
+ username = request.form.get("username")
82
+ password = request.form.get("password")
83
+
84
+ users = load_users()
85
+ if username in users and users[username]["password"] == password:
86
+ session["username"] = username
87
+ return redirect(url_for("dashboard"))
88
+
89
+ flash("Invalid credentials", "error")
90
+ return redirect(url_for("index"))
91
+
92
+
93
+ @app.route("/signup", methods=["GET", "POST"])
94
+ def signup():
95
+ if request.method == "GET":
96
+ return render_template("index.html", page="signup")
97
+
98
+ username = request.form.get("username")
99
+ password = request.form.get("password")
100
+
101
+ users = load_users()
102
+ if username in users:
103
+ flash("Username already exists", "error")
104
+ return redirect(url_for("signup"))
105
+
106
+ users[username] = {"password": password}
107
+ save_users(users)
108
+ session["username"] = username
109
+ return redirect(url_for("dashboard"))
110
+
111
+
112
+ @app.route("/dashboard")
113
+ def dashboard():
114
+ if "username" not in session:
115
+ return redirect(url_for("index"))
116
+
117
+ username = session["username"]
118
+ notes = get_user_notes(username)
119
+ return render_template(
120
+ "index.html", page="dashboard", username=username, notes=notes
121
+ )
122
+
123
+
124
+ @app.route("/add_note", methods=["POST"])
125
+ def add_note():
126
+ if "username" not in session:
127
+ return redirect(url_for("index"))
128
+
129
+ content = request.form.get("content", "").strip()
130
+ if content:
131
+ save_note(session["username"], content)
132
+ flash("Note added successfully", "success")
133
+
134
+ return redirect(url_for("dashboard"))
135
+
136
+
137
+ @app.route("/delete_note/<filename>")
138
+ def delete_note_route(filename):
139
+ if "username" not in session:
140
+ return redirect(url_for("index"))
141
+
142
+ delete_note(session["username"], filename)
143
+ flash("Note deleted", "success")
144
+ return redirect(url_for("dashboard"))
145
+
146
+
147
+ @app.route("/logout")
148
+ def logout():
149
+ session.pop("username", None)
150
+ return redirect(url_for("index"))
151
+
152
+
153
+ if __name__ == "__main__":
154
+ app.run(debug=True)
data/users.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"admin": {"password": "123"}}
templates/index.html ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Simple CMS</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ }
20
+
21
+ .container {
22
+ max-width: 1200px;
23
+ margin: 0 auto;
24
+ }
25
+
26
+ .auth-box {
27
+ max-width: 400px;
28
+ margin: 100px auto;
29
+ background: white;
30
+ padding: 40px;
31
+ border-radius: 10px;
32
+ box-shadow: 0 10px 40px rgba(0,0,0,0.2);
33
+ }
34
+
35
+ h1, h2 {
36
+ color: #333;
37
+ margin-bottom: 20px;
38
+ }
39
+
40
+ .auth-box h2 {
41
+ text-align: center;
42
+ color: #667eea;
43
+ }
44
+
45
+ .form-group {
46
+ margin-bottom: 20px;
47
+ }
48
+
49
+ label {
50
+ display: block;
51
+ margin-bottom: 5px;
52
+ color: #555;
53
+ font-weight: 500;
54
+ }
55
+
56
+ input[type="text"],
57
+ input[type="password"],
58
+ textarea {
59
+ width: 100%;
60
+ padding: 12px;
61
+ border: 2px solid #e0e0e0;
62
+ border-radius: 6px;
63
+ font-size: 14px;
64
+ transition: border-color 0.3s;
65
+ }
66
+
67
+ input[type="text"]:focus,
68
+ input[type="password"]:focus,
69
+ textarea:focus {
70
+ outline: none;
71
+ border-color: #667eea;
72
+ }
73
+
74
+ textarea {
75
+ resize: vertical;
76
+ min-height: 100px;
77
+ font-family: inherit;
78
+ }
79
+
80
+ button {
81
+ width: 100%;
82
+ padding: 12px;
83
+ background: #667eea;
84
+ color: white;
85
+ border: none;
86
+ border-radius: 6px;
87
+ font-size: 16px;
88
+ font-weight: 600;
89
+ cursor: pointer;
90
+ transition: background 0.3s;
91
+ }
92
+
93
+ button:hover {
94
+ background: #5568d3;
95
+ }
96
+
97
+ .link {
98
+ text-align: center;
99
+ margin-top: 15px;
100
+ color: #666;
101
+ }
102
+
103
+ .link a {
104
+ color: #667eea;
105
+ text-decoration: none;
106
+ font-weight: 600;
107
+ }
108
+
109
+ .link a:hover {
110
+ text-decoration: underline;
111
+ }
112
+
113
+ .dashboard-header {
114
+ background: white;
115
+ padding: 20px 30px;
116
+ border-radius: 10px;
117
+ margin-bottom: 30px;
118
+ display: flex;
119
+ justify-content: space-between;
120
+ align-items: center;
121
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
122
+ }
123
+
124
+ .dashboard-header h1 {
125
+ margin: 0;
126
+ color: #667eea;
127
+ }
128
+
129
+ .user-info {
130
+ color: #666;
131
+ }
132
+
133
+ .logout-btn {
134
+ width: auto;
135
+ padding: 8px 20px;
136
+ background: #764ba2;
137
+ margin-left: 20px;
138
+ }
139
+
140
+ .logout-btn:hover {
141
+ background: #653a8a;
142
+ }
143
+
144
+ .note-form {
145
+ background: white;
146
+ padding: 30px;
147
+ border-radius: 10px;
148
+ margin-bottom: 30px;
149
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
150
+ }
151
+
152
+ .note-form textarea {
153
+ min-height: 120px;
154
+ }
155
+
156
+ .note-form button {
157
+ margin-top: 10px;
158
+ }
159
+
160
+ .notes-grid {
161
+ display: grid;
162
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
163
+ gap: 20px;
164
+ }
165
+
166
+ .note-card {
167
+ background: white;
168
+ padding: 20px;
169
+ border-radius: 10px;
170
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
171
+ transition: transform 0.2s, box-shadow 0.2s;
172
+ position: relative;
173
+ }
174
+
175
+ .note-card:hover {
176
+ transform: translateY(-5px);
177
+ box-shadow: 0 5px 20px rgba(0,0,0,0.15);
178
+ }
179
+
180
+ .note-timestamp {
181
+ font-size: 12px;
182
+ color: #999;
183
+ margin-bottom: 10px;
184
+ }
185
+
186
+ .note-content {
187
+ color: #333;
188
+ line-height: 1.6;
189
+ word-wrap: break-word;
190
+ white-space: pre-wrap;
191
+ }
192
+
193
+ .delete-btn {
194
+ position: absolute;
195
+ top: 15px;
196
+ right: 15px;
197
+ background: #ff4757;
198
+ color: white;
199
+ border: none;
200
+ border-radius: 50%;
201
+ width: 30px;
202
+ height: 30px;
203
+ cursor: pointer;
204
+ font-size: 18px;
205
+ line-height: 1;
206
+ transition: background 0.3s;
207
+ padding: 0;
208
+ }
209
+
210
+ .delete-btn:hover {
211
+ background: #ee384a;
212
+ }
213
+
214
+ .flash-messages {
215
+ max-width: 1200px;
216
+ margin: 0 auto 20px;
217
+ }
218
+
219
+ .flash {
220
+ padding: 15px 20px;
221
+ border-radius: 6px;
222
+ margin-bottom: 10px;
223
+ }
224
+
225
+ .flash.success {
226
+ background: #d4edda;
227
+ color: #155724;
228
+ border: 1px solid #c3e6cb;
229
+ }
230
+
231
+ .flash.error {
232
+ background: #f8d7da;
233
+ color: #721c24;
234
+ border: 1px solid #f5c6cb;
235
+ }
236
+
237
+ .empty-state {
238
+ text-align: center;
239
+ padding: 60px 20px;
240
+ color: white;
241
+ font-size: 18px;
242
+ }
243
+ </style>
244
+ </head>
245
+ <body>
246
+ {% with messages = get_flashed_messages(with_categories=true) %}
247
+ {% if messages %}
248
+ <div class="flash-messages">
249
+ {% for category, message in messages %}
250
+ <div class="flash {{ category }}">{{ message }}</div>
251
+ {% endfor %}
252
+ </div>
253
+ {% endif %}
254
+ {% endwith %}
255
+
256
+ {% if page == 'login' %}
257
+ <div class="auth-box">
258
+ <h2>Login</h2>
259
+ <form method="POST" action="/login">
260
+ <div class="form-group">
261
+ <label for="username">Username</label>
262
+ <input type="text" id="username" name="username" required>
263
+ </div>
264
+ <div class="form-group">
265
+ <label for="password">Password</label>
266
+ <input type="password" id="password" name="password" required>
267
+ </div>
268
+ <button type="submit">Login</button>
269
+ </form>
270
+ <div class="link">
271
+ Don't have an account? <a href="/signup">Sign up</a>
272
+ </div>
273
+ </div>
274
+
275
+ {% elif page == 'signup' %}
276
+ <div class="auth-box">
277
+ <h2>Sign Up</h2>
278
+ <form method="POST" action="/signup">
279
+ <div class="form-group">
280
+ <label for="username">Username</label>
281
+ <input type="text" id="username" name="username" required>
282
+ </div>
283
+ <div class="form-group">
284
+ <label for="password">Password</label>
285
+ <input type="password" id="password" name="password" required>
286
+ </div>
287
+ <button type="submit">Sign Up</button>
288
+ </form>
289
+ <div class="link">
290
+ Already have an account? <a href="/">Login</a>
291
+ </div>
292
+ </div>
293
+
294
+ {% elif page == 'dashboard' %}
295
+ <div class="container">
296
+ <div class="dashboard-header">
297
+ <h1>My Notes</h1>
298
+ <div style="display: flex; align-items: center;">
299
+ <span class="user-info">Welcome, <strong>{{ username }}</strong></span>
300
+ <a href="/logout"><button class="logout-btn">Logout</button></a>
301
+ </div>
302
+ </div>
303
+
304
+ <div class="note-form">
305
+ <h2>Add New Note</h2>
306
+ <form method="POST" action="/add_note">
307
+ <textarea name="content" placeholder="Write your note here..." required></textarea>
308
+ <button type="submit">Add Note</button>
309
+ </form>
310
+ </div>
311
+
312
+ {% if notes %}
313
+ <div class="notes-grid">
314
+ {% for note in notes %}
315
+ <div class="note-card">
316
+ <a href="/delete_note/{{ note.filename }}" onclick="return confirm('Delete this note?')">
317
+ <button class="delete-btn" title="Delete note">×</button>
318
+ </a>
319
+ <div class="note-timestamp">{{ note.timestamp.replace('_', ' ') }}</div>
320
+ <div class="note-content">{{ note.content }}</div>
321
+ </div>
322
+ {% endfor %}
323
+ </div>
324
+ {% else %}
325
+ <div class="empty-state">
326
+ <p>No notes yet. Add your first note above!</p>
327
+ </div>
328
+ {% endif %}
329
+ </div>
330
+ {% endif %}
331
+ </body>
332
+ </html>
333
+