GitHub Actions commited on
Commit
0bdcf2c
·
1 Parent(s): 8ff94d4

Sync from GitHub Actions

Browse files
Dockerfile CHANGED
@@ -19,4 +19,4 @@ COPY . .
19
 
20
  EXPOSE 10000
21
 
22
- CMD ["python", "app.py"]
 
19
 
20
  EXPOSE 10000
21
 
22
+ CMD ["python", "-m", "api.app"]
api/admin.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask_admin import Admin
2
+ from flask_admin.contrib.sqla import ModelView
3
+ from flask import redirect, url_for, session
4
+ from api.models import db, User
5
+
6
+ class MyModelView(ModelView):
7
+ def is_accessible(self):
8
+ return session.get('user_id') == 1
9
+
10
+ def inaccessible_callback(self, name, **kwargs):
11
+ return redirect(url_for('auth.login'))
12
+
13
+ admin = Admin(name="Admin", template_mode="bootstrap4", url="/admin")
14
+ admin.add_view(MyModelView(User, db.session))
app.py → api/app.py RENAMED
@@ -2,23 +2,39 @@ from Lawverse.pipeline.rag_pipeline import rag_components, create_chat_chian
2
  from flask import Flask, render_template, request, jsonify, session
3
  from Lawverse.utils.config import MEMORY_DIR
4
  from Lawverse.logger import logging
 
 
5
  import json
6
  import glob
7
  import os
 
8
 
9
- app = Flask(__name__)
10
- app.secret_key = os.getenv("SECRET")
 
 
 
 
 
 
 
11
 
12
  BASE_COMPONENTS = rag_components()
13
  logging.info("Lawverse RAG components ready.")
14
 
15
  active_chains = {}
16
 
 
 
 
 
 
17
  @app.route("/", methods=["GET"])
18
  def home():
19
  return render_template("index.html")
20
 
21
  @app.route("/chat", methods=["GET"])
 
22
  def chat():
23
  chat_id = session.get("chat_id")
24
  if not chat_id or chat_id not in active_chains:
 
2
  from flask import Flask, render_template, request, jsonify, session
3
  from Lawverse.utils.config import MEMORY_DIR
4
  from Lawverse.logger import logging
5
+ from api.auth import auth_bp, login_required
6
+ from api.models import db
7
  import json
8
  import glob
9
  import os
10
+ from api.admin import admin
11
 
12
+ app = Flask(__name__, template_folder="../templates")
13
+ # app.secret_key = os.getenv("SECRET")
14
+ app.secret_key = "lawverse-secret"
15
+ app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
16
+ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
17
+
18
+ db.init_app(app)
19
+ admin.init_app(app)
20
+ app.register_blueprint(auth_bp)
21
 
22
  BASE_COMPONENTS = rag_components()
23
  logging.info("Lawverse RAG components ready.")
24
 
25
  active_chains = {}
26
 
27
+ @app.route("/debug_session")
28
+ def debug_session():
29
+ print("Session Data:", dict(session))
30
+ return "Check your console!"
31
+
32
  @app.route("/", methods=["GET"])
33
  def home():
34
  return render_template("index.html")
35
 
36
  @app.route("/chat", methods=["GET"])
37
+ @login_required
38
  def chat():
39
  chat_id = session.get("chat_id")
40
  if not chat_id or chat_id not in active_chains:
api/auth.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Blueprint, render_template, request, redirect, url_for, flash, session
2
+ from api.models import db, User
3
+ from functools import wraps
4
+
5
+ auth_bp = Blueprint("auth", __name__)
6
+
7
+ @auth_bp.route("/signup", methods=["GET", "POST"])
8
+ def signup():
9
+ if request.method == "POST":
10
+ first_name = request.form.get("first_name")
11
+ last_name = request.form.get("last_name")
12
+ email = request.form.get("email")
13
+ password = request.form.get("password")
14
+ confirm_password = request.form.get("confirm_password")
15
+
16
+ if password != confirm_password:
17
+ flash("Passwords do not match!")
18
+ return redirect(url_for("auth.signup"))
19
+
20
+ if User.query.filter_by(email=email).first():
21
+ flash("Email already registered!")
22
+ return redirect(url_for("auth.signup"))
23
+
24
+ user = User(first_name=first_name, last_name=last_name, email=email)
25
+ user.set_password(password=password)
26
+ db.session.add(user)
27
+ db.session.commit()
28
+
29
+ session['user_id'] = user.id
30
+ session['user_name'] = user.first_name
31
+
32
+ flash("Login Successful!")
33
+ return render_template("signup.html")
34
+
35
+ return render_template("signup.html")
36
+
37
+ @auth_bp.route("/login", methods=["GET", "POST"])
38
+ def login():
39
+ if request.method == "POST":
40
+ email = request.form.get("email")
41
+ password = request.form.get("password")
42
+
43
+ user = User.query.filter_by(email=email).first()
44
+ if user and user.check_password(password):
45
+ session['user_id'] = user.id
46
+ session['user_name'] = user.first_name
47
+ flash("Login Successful!")
48
+ return render_template("login.html")
49
+ else:
50
+ flash("Invalid email or password.")
51
+ return redirect(url_for("auth.login"))
52
+
53
+ return render_template("login.html")
54
+
55
+ @auth_bp.route("/logout")
56
+ def logout():
57
+ session.pop('user_id', None)
58
+ session.pop('user_name', None)
59
+ return redirect(url_for("home"))
60
+
61
+
62
+ def login_required(f):
63
+ @wraps(f)
64
+ def decorated_function(*args, **kwargs):
65
+ if "user_id" not in session:
66
+ return redirect(url_for("auth.login"))
67
+ return f(*args, **kwargs)
68
+ return decorated_function
api/models.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask_sqlalchemy import SQLAlchemy
2
+ from werkzeug.security import generate_password_hash, check_password_hash
3
+
4
+ db = SQLAlchemy()
5
+
6
+ class User(db.Model):
7
+ id = db.Column(db.Integer, primary_key=True)
8
+ first_name = db.Column(db.String(150))
9
+ last_name = db.Column(db.String(150))
10
+ email = db.Column(db.String(150), unique=True, nullable=False)
11
+ password_hash = db.Column(db.String(256), nullable=False)
12
+
13
+ def set_password(self, password):
14
+ self.password_hash = generate_password_hash(password)
15
+
16
+ def check_password(self, password):
17
+ return check_password_hash(self.password_hash, password)
templates/index.html CHANGED
@@ -120,46 +120,40 @@
120
  <body>
121
  <div id="vanta-background" class="fixed inset-0 -z-10"></div>
122
 
123
- <nav class="glass-panel fixed top-0 left-0 right-0 z-50 py-4 px-6">
124
- <div class="container mx-auto flex justify-between items-center">
125
- <div class="flex items-center space-x-2">
126
- <div
127
- class="w-8 h-8 rounded-full bg-gradient-to-r from-blue-500 to-purple-500"
128
- ></div>
129
- <h1
130
- class="text-xl font-bold glow-text cursor-pointer"
131
- onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
132
- >
133
- LAWVERSE
134
- </h1>
135
- </div>
136
- <div class="hidden md:flex space-x-8">
137
- <a
138
- href="#"
139
- class="text-gray-300 hover:text-cyan-300 transition-colors duration-300"
140
- >Home</a
141
- >
142
- <a
143
- onclick="window.location.href='/chat'"
144
- class="text-gray-300 hover:text-cyan-300 transition-colors duration-300"
145
- >Chat</a
146
- >
147
- <a
148
- href="#about"
149
- class="text-gray-300 hover:text-cyan-300 transition-colors duration-300"
150
- >About</a
151
- >
152
- <a
153
- href="#contact"
154
- class="text-gray-300 hover:text-cyan-300 transition-colors duration-300"
155
- >Contact</a
156
- >
157
- </div>
158
- <button class="md:hidden text-gray-300">
159
- <i data-feather="menu"></i>
160
  </button>
161
- </div>
162
- </nav>
 
 
 
 
 
 
 
163
 
164
  <section class="min-h-screen flex items-center justify-center px-6 pt-20">
165
  <div class="container mx-auto text-center">
 
120
  <body>
121
  <div id="vanta-background" class="fixed inset-0 -z-10"></div>
122
 
123
+ <nav class="glass-panel fixed top-0 left-0 right-0 z-50 py-4 px-6">
124
+ <div class="container mx-auto flex justify-between items-center">
125
+ <div class="flex items-center space-x-2">
126
+ <div class="w-8 h-8 rounded-full bg-gradient-to-r from-blue-500 to-purple-500"></div>
127
+ <h1
128
+ class="text-xl font-bold glow-text cursor-pointer"
129
+ onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"
130
+ >
131
+ LAWVERSE
132
+ </h1>
133
+ </div>
134
+
135
+ <div class="hidden md:flex space-x-8 items-center">
136
+ <a href="#" class="text-gray-300 hover:text-cyan-300 transition-colors duration-300">Home</a>
137
+ <a onclick="window.location.href='/chat'" class="text-gray-300 hover:text-cyan-300 transition-colors duration-300">Chat</a>
138
+ <a href="#about" class="text-gray-300 hover:text-cyan-300 transition-colors duration-300">About</a>
139
+ <a href="#contact" class="text-gray-300 hover:text-cyan-300 transition-colors duration-300">Contact</a>
140
+
141
+ {% if session.get('user_id') %}
142
+ <button
143
+ onclick="window.location.href='{{ url_for('auth.logout') }}'"
144
+ class="neon-button text-sm py-2 px-4 ml-4"
145
+ >
146
+ Logout ({{ session.get('user_name') }})
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  </button>
148
+ {% endif %}
149
+ </div>
150
+
151
+ <button class="md:hidden text-gray-300">
152
+ <i data-feather="menu"></i>
153
+ </button>
154
+ </div>
155
+ </nav>
156
+
157
 
158
  <section class="min-h-screen flex items-center justify-center px-6 pt-20">
159
  <div class="container mx-auto text-center">
templates/login.html ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Lawverse Nexus Portal</title>
7
+ <link rel="icon" type="image/x-icon" href="/static/favicon.ico" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://unpkg.com/feather-icons"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script>
11
+ <link
12
+ href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap"
13
+ rel="stylesheet"
14
+ />
15
+ <script>
16
+ tailwind.config = {
17
+ theme: {
18
+ extend: {
19
+ colors: {
20
+ primary: "#6366f1",
21
+ secondary: "#8b5cf6",
22
+ accent: "#cbd5e1",
23
+ },
24
+ fontFamily: {
25
+ orbitron: ["Orbitron", "sans-serif"],
26
+ space: ["Space Grotesk", "sans-serif"],
27
+ },
28
+ animation: {
29
+ "pulse-glow":
30
+ "pulse-glow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
31
+ float: "float 6s ease-in-out infinite",
32
+ "neon-flicker": "neon-flicker 1.5s infinite alternate",
33
+ },
34
+ keyframes: {
35
+ "pulse-glow": {
36
+ "0%, 100%": {
37
+ boxShadow:
38
+ "0 0 5px #6366f1, 0 0 10px #6366f1, 0 0 15px #6366f1",
39
+ },
40
+ "50%": {
41
+ boxShadow:
42
+ "0 0 10px #6366f1, 0 0 20px #6366f1, 0 0 30px #6366f1",
43
+ },
44
+ },
45
+ float: {
46
+ "0%, 100%": { transform: "translateY(0px)" },
47
+ "50%": { transform: "translateY(-10px)" },
48
+ },
49
+ "neon-flicker": {
50
+ "0%, 18%, 22%, 25%, 53%, 57%, 100%": {
51
+ textShadow:
52
+ "0 0 4px #fff, 0 0 11px #fff, 0 0 19px #fff, 0 0 40px #6366f1, 0 0 80px #6366f1, 0 0 90px #6366f1, 0 0 100px #6366f1, 0 0 150px #6366f1",
53
+ opacity: "1",
54
+ },
55
+ "20%, 24%, 55%": {
56
+ textShadow: "none",
57
+ opacity: "0.8",
58
+ },
59
+ },
60
+ },
61
+ },
62
+ },
63
+ };
64
+ </script>
65
+ <style>
66
+ body {
67
+ font-family: "Space Grotesk", sans-serif;
68
+ background: linear-gradient(
69
+ 135deg,
70
+ #000000 0%,
71
+ #1e1b4b 50%,
72
+ #000000 100%
73
+ );
74
+ overflow-x: hidden;
75
+ }
76
+ .glass-panel {
77
+ background: rgba(255, 255, 255, 0.05);
78
+ backdrop-filter: blur(10px);
79
+ border: 1px solid rgba(99, 102, 241, 0.3);
80
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
81
+ }
82
+ .neon-input {
83
+ background: rgba(0, 0, 0, 0.3);
84
+ border: 1px solid rgba(99, 102, 241, 0.5);
85
+ transition: all 0.3s ease;
86
+ }
87
+ .neon-input:focus {
88
+ border-color: #6366f1;
89
+ box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
90
+ }
91
+ .floating-label {
92
+ transition: all 0.3s ease;
93
+ pointer-events: none;
94
+ }
95
+ .neon-input:focus + .floating-label,
96
+ .neon-input:not(:placeholder-shown) + .floating-label {
97
+ top: 0.6rem;
98
+ transform: translateY(-50%) scale(0.9);
99
+ color: #6366f1;
100
+ text-shadow: 0 0 5px currentColor;
101
+ }
102
+ .neon-button {
103
+ background: linear-gradient(45deg, #6366f1, #8b5cf6);
104
+ transition: all 0.3s ease;
105
+ }
106
+ .neon-button:hover {
107
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.7);
108
+ transform: translateY(-2px);
109
+ }
110
+ .portal-transition {
111
+ transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
112
+ }
113
+ .legal-icon {
114
+ filter: drop-shadow(0 0 8px rgba(99, 102, 241, 0.6));
115
+ }
116
+ </style>
117
+ </head>
118
+ <body class="min-h-screen flex items-center justify-center">
119
+ <div
120
+ id="portal"
121
+ class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 opacity-0 pointer-events-none transition-opacity duration-700"
122
+ style="display: none;"
123
+ >
124
+ <div class="text-center">
125
+ <div
126
+ class="w-32 h-32 mx-auto mb-8 rounded-full bg-gradient-to-r from-primary to-secondary flex items-center justify-center animate-pulse"
127
+ >
128
+ <i data-feather="check" class="w-16 h-16 text-white"></i>
129
+ </div>
130
+ <h2 class="text-3xl font-orbitron font-bold text-white mb-4">
131
+ Access Granted
132
+ </h2>
133
+ <p class="text-accent font-space text-lg">
134
+ Entering Lawverse AI Portal...
135
+ </p>
136
+ </div>
137
+ </div>
138
+ <div id="vanta-bg" class="fixed inset-0 z-0"></div>
139
+ <a href="{{ url_for('home') }}" class="fixed top-6 left-6 z-50 group">
140
+ <div
141
+ class="flex items-center space-x-2 text-accent hover:text-primary transition-all duration-300"
142
+ >
143
+ <i
144
+ data-feather="arrow-left"
145
+ class="w-5 h-5 group-hover:scale-110 transition-transform"
146
+ ></i>
147
+ <span class="font-space font-medium">Return to Home</span>
148
+ </div>
149
+ </a>
150
+ {% with messages = get_flashed_messages() %}
151
+ {% if messages %}
152
+ <div id="flash-message" class="fixed top-6 right-6 z-50 p-3 rounded-lg text-sm font-space transition-opacity duration-700
153
+ {% if 'successful' in messages[0]|lower %}bg-green-500/20 text-green-300 border border-green-500
154
+ {% else %}bg-red-500/20 text-red-300 border border-red-500{% endif %}">
155
+ {% for message in messages %}
156
+ {{ message }}
157
+ {% endfor %}
158
+ </div>
159
+
160
+ <script>
161
+
162
+ document.addEventListener("DOMContentLoaded", () => {
163
+ const flashMsg = document.getElementById("flash-message");
164
+
165
+ {% if 'successful' in messages[0]|lower %}
166
+ if (flashMsg) flashMsg.style.display = 'none';
167
+ const portal = document.getElementById("portal");
168
+ if (portal) {
169
+ portal.style.display = "flex";
170
+ portal.classList.remove("opacity-0", "pointer-events-none");
171
+ portal.classList.add("opacity-100");
172
+ }
173
+
174
+ setTimeout(() => {
175
+ window.location.href = "{{ url_for('chat') }}";
176
+ }, 2000);
177
+
178
+ {% else %}
179
+ setTimeout(() => {
180
+ if (flashMsg) flashMsg.style.opacity = "0";
181
+ }, 1500);
182
+ {% endif %}
183
+ });
184
+ </script>
185
+ {% endif %}
186
+ {% endwith %}
187
+
188
+ <div
189
+ class="relative z-10 w-full max-w-md mx-auto px-4 flex flex-col justify-center items-center min-h-screen"
190
+ >
191
+ <div class="text-center mb-12 animate-float">
192
+ <div class="inline-flex items-center space-x-3">
193
+ <div
194
+ class="w-12 h-12 rounded-full bg-gradient-to-r from-primary to-secondary flex items-center justify-center"
195
+ >
196
+ <i data-feather="scale" class="w-6 h-6 text-white"></i>
197
+ </div>
198
+ <h1
199
+ class="text-4xl md:text-5xl font-orbitron font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary via-accent to-secondary"
200
+ >
201
+ LAWVERSE
202
+ </h1>
203
+ </div>
204
+ <p class="mt-4 text-accent font-space text-lg">
205
+ AI-Powered Legal Knowledge Assistant
206
+ </p>
207
+ </div>
208
+
209
+ <div
210
+ class="glass-panel rounded-2xl p-8 transform transition-all duration-500 hover:scale-102 w-full"
211
+ >
212
+ <div class="text-center mb-8">
213
+ <div
214
+ class="w-16 h-16 mx-auto mb-4 rounded-full bg-gradient-to-r from-primary to-secondary flex items-center justify-center"
215
+ >
216
+ <i data-feather="user-check" class="w-8 h-8 text-white"></i>
217
+ </div>
218
+ <h2 class="text-2xl font-orbitron font-semibold text-white mb-2">
219
+ Welcome back to Lawverse
220
+ </h2>
221
+ <p class="text-accent font-space">Continue your legal journey</p>
222
+ </div>
223
+
224
+ <form
225
+ class="space-y-6"
226
+ method="POST"
227
+ action="{{ url_for('auth.login') }}"
228
+ >
229
+ <div class="relative">
230
+ <input
231
+ name="email"
232
+ type="email"
233
+ placeholder=" "
234
+ class="neon-input w-full px-4 py-3 rounded-lg text-white font-space placeholder-transparent"
235
+ required
236
+ />
237
+ <label
238
+ class="floating-label absolute left-4 top-3 text-accent font-space"
239
+ >Email</label
240
+ >
241
+ <i
242
+ data-feather="mail"
243
+ class="absolute right-4 top-3 text-accent w-5 h-5"
244
+ ></i>
245
+ </div>
246
+ <div class="relative">
247
+ <input
248
+ name="password"
249
+ type="password"
250
+ placeholder=" "
251
+ class="neon-input w-full px-4 py-3 rounded-lg text-white font-space placeholder-transparent"
252
+ required
253
+ />
254
+ <label
255
+ class="floating-label absolute left-4 top-3 text-accent font-space"
256
+ >Password</label
257
+ >
258
+ <i
259
+ data-feather="lock"
260
+ class="absolute right-4 top-3 text-accent w-5 h-5"
261
+ ></i>
262
+ </div>
263
+ <div class="flex items-center justify-between">
264
+ <label
265
+ class="flex items-center space-x-2 text-accent font-space text-sm"
266
+ >
267
+ <input
268
+ type="checkbox"
269
+ class="rounded border-accent bg-transparent"
270
+ />
271
+ <span>Remember me</span>
272
+ </label>
273
+ <a
274
+ href="#"
275
+ class="text-primary hover:text-secondary font-space text-sm transition-colors"
276
+ >Forgot password?</a
277
+ >
278
+ </div>
279
+ <button
280
+ type="submit"
281
+ class="neon-button w-full py-3 rounded-lg text-white font-orbitron font-semibold animate-pulse-glow"
282
+ >
283
+ Login to Lawverse
284
+ </button>
285
+ </form>
286
+
287
+ <p class="text-accent font-space text-sm text-center mt-4">
288
+ Don't have an account?
289
+ <a
290
+ href="/signup"
291
+ class="text-primary hover:text-secondary font-medium transition-colors"
292
+ >Create Account</a
293
+ >
294
+ </p>
295
+ </div>
296
+ </div>
297
+ <script>
298
+ VANTA.NET({
299
+ el: "#vanta-bg",
300
+ mouseControls: true,
301
+ touchControls: true,
302
+ gyroControls: false,
303
+ minHeight: 200.0,
304
+ minWidth: 200.0,
305
+ scale: 1.0,
306
+ scaleMobile: 1.0,
307
+ color: 0x6366f1,
308
+ backgroundColor: 0x000000,
309
+ points: 12.0,
310
+ maxDistance: 20.0,
311
+ spacing: 15.0,
312
+ });
313
+ document.addEventListener("DOMContentLoaded", () => {
314
+ feather.replace();
315
+ const inputs = document.querySelectorAll(".neon-input");
316
+ inputs.forEach((input) => {
317
+ input.addEventListener("focus", () => {
318
+ input.parentElement.classList.add("scale-105");
319
+ });
320
+ input.addEventListener("blur", () => {
321
+ input.parentElement.classList.remove("scale-105");
322
+ });
323
+ });
324
+ });
325
+ </script>
326
+ </body>
327
+ </html>
templates/signup.html ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Lawverse Nexus Portal</title>
7
+ <link rel="icon" type="image/x-icon" href="/static/favicon.ico" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://unpkg.com/feather-icons"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script>
11
+ <link
12
+ href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700&family=Space+Grotesk:wght@300;400;500;600&display=swap"
13
+ rel="stylesheet"
14
+ />
15
+
16
+ <script>
17
+ tailwind.config = {
18
+ theme: {
19
+ extend: {
20
+ colors: {
21
+ primary: "#6366f1",
22
+ secondary: "#8b5cf6",
23
+ accent: "#cbd5e1",
24
+ },
25
+ fontFamily: {
26
+ orbitron: ["Orbitron", "sans-serif"],
27
+ space: ["Space Grotesk", "sans-serif"],
28
+ },
29
+ animation: {
30
+ "pulse-glow":
31
+ "pulse-glow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
32
+ float: "float 6s ease-in-out infinite",
33
+ },
34
+ keyframes: {
35
+ "pulse-glow": {
36
+ "0%, 100%": {
37
+ boxShadow:
38
+ "0 0 5px #6366f1, 0 0 10px #6366f1, 0 0 15px #6366f1",
39
+ },
40
+ "50%": {
41
+ boxShadow:
42
+ "0 0 10px #6366f1, 0 0 20px #6366f1, 0 0 30px #6366f1",
43
+ },
44
+ },
45
+ float: {
46
+ "0%, 100%": { transform: "translateY(0px)" },
47
+ "50%": { transform: "translateY(-10px)" },
48
+ },
49
+ },
50
+ },
51
+ },
52
+ };
53
+ </script>
54
+
55
+ <style>
56
+ body {
57
+ font-family: "Space Grotesk", sans-serif;
58
+ background: linear-gradient(
59
+ 135deg,
60
+ #000000 0%,
61
+ #1e1b4b 50%,
62
+ #000000 100%
63
+ );
64
+ overflow-x: hidden;
65
+ }
66
+
67
+ .glass-panel {
68
+ background: rgba(255, 255, 255, 0.05);
69
+ backdrop-filter: blur(10px);
70
+ border: 1px solid rgba(99, 102, 241, 0.3);
71
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
72
+ }
73
+
74
+ .neon-input {
75
+ background: rgba(0, 0, 0, 0.3);
76
+ border: 1px solid rgba(99, 102, 241, 0.5);
77
+ transition: all 0.3s ease;
78
+ padding: 1rem 1rem 0.5rem 1rem;
79
+ font-size: 1.05rem;
80
+ }
81
+
82
+ .neon-input:focus {
83
+ border-color: #6366f1;
84
+ box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
85
+ }
86
+
87
+ .floating-label {
88
+ transition: all 0.3s ease;
89
+ pointer-events: none;
90
+ top: 70%;
91
+ transform: translateY(-50%);
92
+ }
93
+
94
+ .neon-input:focus + .floating-label,
95
+ .neon-input:not(:placeholder-shown) + .floating-label {
96
+ top: 0.6rem;
97
+ transform: translateY(-50%) scale(0.9);
98
+ color: #6366f1;
99
+ text-shadow: 0 0 5px currentColor;
100
+ }
101
+
102
+ .neon-button {
103
+ background: linear-gradient(45deg, #6366f1, #8b5cf6);
104
+ transition: all 0.3s ease;
105
+ }
106
+
107
+ .neon-button:hover {
108
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.7);
109
+ transform: translateY(-2px);
110
+ }
111
+ </style>
112
+ </head>
113
+
114
+ <body class="min-h-screen flex items-center justify-center">
115
+ <div
116
+ id="portal"
117
+ class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 opacity-0 pointer-events-none transition-opacity duration-700"
118
+ style="display: none;"
119
+ >
120
+ <div class="text-center">
121
+ <div
122
+ class="w-32 h-32 mx-auto mb-8 rounded-full bg-gradient-to-r from-primary to-secondary flex items-center justify-center animate-pulse"
123
+ >
124
+ <i data-feather="check" class="w-16 h-16 text-white"></i>
125
+ </div>
126
+ <h2 class="text-3xl font-orbitron font-bold text-white mb-4">
127
+ Access Granted
128
+ </h2>
129
+ <p class="text-accent font-space text-lg">
130
+ Entering Lawverse AI Portal...
131
+ </p>
132
+ </div>
133
+ </div>
134
+ <div id="vanta-bg" class="fixed inset-0 z-0"></div>
135
+ <a href="{{ url_for('home') }}" class="fixed top-6 left-6 z-50 group">
136
+ <div
137
+ class="flex items-center space-x-2 text-accent hover:text-primary transition-all duration-300"
138
+ >
139
+ <i
140
+ data-feather="arrow-left"
141
+ class="w-5 h-5 group-hover:scale-110 transition-transform"
142
+ ></i>
143
+ <span class="font-space font-medium">Return to Home</span>
144
+ </div>
145
+ </a>
146
+ {% with messages = get_flashed_messages() %}
147
+ {% if messages %}
148
+ <div id="flash-message" class="fixed top-6 right-6 z-50 p-3 rounded-lg text-sm font-space transition-opacity duration-700
149
+ {% if 'successful' in messages[0]|lower %}bg-green-500/20 text-green-300 border border-green-500
150
+ {% else %}bg-red-500/20 text-red-300 border border-red-500{% endif %}">
151
+ {% for message in messages %}
152
+ {{ message }}
153
+ {% endfor %}
154
+ </div>
155
+
156
+ <script>
157
+
158
+ document.addEventListener("DOMContentLoaded", () => {
159
+ const flashMsg = document.getElementById("flash-message");
160
+
161
+ {% if 'successful' in messages[0]|lower %}
162
+ if (flashMsg) flashMsg.style.display = 'none';
163
+ const portal = document.getElementById("portal");
164
+ if (portal) {
165
+ portal.style.display = "flex";
166
+ portal.classList.remove("opacity-0", "pointer-events-none");
167
+ portal.classList.add("opacity-100");
168
+ }
169
+
170
+ setTimeout(() => {
171
+ window.location.href = "{{ url_for('chat') }}";
172
+ }, 2000);
173
+
174
+ {% else %}
175
+ setTimeout(() => {
176
+ if (flashMsg) flashMsg.style.opacity = "0";
177
+ }, 1500);
178
+ {% endif %}
179
+ });
180
+ </script>
181
+ {% endif %}
182
+ {% endwith %}
183
+
184
+
185
+
186
+
187
+ <div class="relative z-10 w-full max-w-6xl mx-auto px-4">
188
+ <div class="text-center mb-12 animate-float">
189
+ <div class="inline-flex items-center space-x-3">
190
+ <div
191
+ class="w-12 h-12 rounded-full bg-gradient-to-r from-primary to-secondary flex items-center justify-center"
192
+ >
193
+ <i data-feather="scale" class="w-6 h-6 text-white"></i>
194
+ </div>
195
+ <h1
196
+ class="text-4xl md:text-5xl font-orbitron font-bold text-transparent bg-clip-text bg-gradient-to-r from-primary via-accent to-secondary"
197
+ >
198
+ LAWVERSE
199
+ </h1>
200
+ </div>
201
+ <p class="mt-4 text-accent font-space text-lg">
202
+ AI-Powered Legal Knowledge Assistant
203
+ </p>
204
+ </div>
205
+
206
+ <div
207
+ class="glass-panel rounded-2xl p-8 transform transition-all duration-500 hover:scale-105"
208
+ >
209
+ <div class="text-center mb-8">
210
+ <div
211
+ class="w-16 h-16 mx-auto mb-4 rounded-full bg-gradient-to-r from-secondary to-primary flex items-center justify-center"
212
+ >
213
+ <i data-feather="user-plus" class="w-8 h-8 text-white"></i>
214
+ </div>
215
+ <h2 class="text-2xl font-orbitron font-semibold text-white mb-2">
216
+ Create your Lawverse Account
217
+ </h2>
218
+ <p class="text-accent font-space">
219
+ Join the future of legal assistance
220
+ </p>
221
+ </div>
222
+
223
+ <form
224
+ class="space-y-6"
225
+ method="POST"
226
+ action="{{ url_for('auth.signup') }}"
227
+ >
228
+ <div class="grid grid-cols-2 gap-4">
229
+ <div class="relative">
230
+ <input
231
+ name="first_name"
232
+ type="text"
233
+ placeholder=" "
234
+ class="neon-input w-full px-4 py-3 rounded-lg text-white font-space placeholder-transparent"
235
+ required
236
+ />
237
+ <label
238
+ class="floating-label absolute left-4 top-5 text-accent font-space"
239
+ >First Name</label
240
+ >
241
+ </div>
242
+ <div class="relative">
243
+ <input
244
+ name="last_name"
245
+ type="text"
246
+ placeholder=" "
247
+ class="neon-input w-full px-4 py-3 rounded-lg text-white font-space placeholder-transparent"
248
+ required
249
+ />
250
+ <label
251
+ class="floating-label absolute left-4 top-5 text-accent font-space"
252
+ >Last Name</label
253
+ >
254
+ </div>
255
+ </div>
256
+
257
+ <div class="relative">
258
+ <input
259
+ name="email"
260
+ type="email"
261
+ placeholder=" "
262
+ class="neon-input w-full px-4 py-3 rounded-lg text-white font-space placeholder-transparent"
263
+ required
264
+ />
265
+ <label
266
+ class="floating-label absolute left-4 top-5 text-accent font-space"
267
+ >Email</label
268
+ >
269
+ <i
270
+ data-feather="mail"
271
+ class="absolute right-4 top-3 text-accent w-5 h-5"
272
+ ></i>
273
+ </div>
274
+
275
+ <div class="relative">
276
+ <input
277
+ name="password"
278
+ type="password"
279
+ placeholder=" "
280
+ class="neon-input w-full px-4 py-3 rounded-lg text-white font-space placeholder-transparent"
281
+ required
282
+ />
283
+ <label
284
+ class="floating-label absolute left-4 top-5 text-accent font-space"
285
+ >Password</label
286
+ >
287
+ <i
288
+ data-feather="lock"
289
+ class="absolute right-4 top-3 text-accent w-5 h-5"
290
+ ></i>
291
+ </div>
292
+
293
+ <div class="relative">
294
+ <input
295
+ name="confirm_password"
296
+ type="password"
297
+ placeholder=" "
298
+ class="neon-input w-full px-4 py-3 rounded-lg text-white font-space placeholder-transparent"
299
+ required
300
+ />
301
+ <label
302
+ class="floating-label absolute left-4 top-5 text-accent font-space"
303
+ >Confirm Password</label
304
+ >
305
+ <i
306
+ data-feather="shield"
307
+ class="absolute right-4 top-3 text-accent w-5 h-5"
308
+ ></i>
309
+ </div>
310
+
311
+ <button
312
+ type="submit"
313
+ class="neon-button w-full py-3 rounded-lg text-white font-orbitron font-semibold animate-pulse-glow"
314
+ >
315
+ Create Account
316
+ </button>
317
+ </form>
318
+ </div>
319
+ </div>
320
+
321
+ <script>
322
+ VANTA.NET({
323
+ el: "#vanta-bg",
324
+ mouseControls: true,
325
+ touchControls: true,
326
+ gyroControls: false,
327
+ color: 0x6366f1,
328
+ backgroundColor: 0x000000,
329
+ points: 12.0,
330
+ maxDistance: 20.0,
331
+ spacing: 15.0,
332
+ });
333
+
334
+ document.addEventListener("DOMContentLoaded", () => {
335
+ feather.replace();
336
+ document.querySelector(".neon-button").addEventListener("click", () => {
337
+ const portal = document.getElementById("portal");
338
+ portal.classList.remove("opacity-0", "pointer-events-none");
339
+ portal.classList.add("opacity-100");
340
+ setTimeout(() => (window.location.href = "/chat"), 2000);
341
+ });
342
+ });
343
+ </script>
344
+ </body>
345
+ </html>