Ajit Panday commited on
Commit
a46907b
·
1 Parent(s): 0e6aefc

Remove database credentials from customer model and interface

Browse files
Files changed (3) hide show
  1. app/auth.py +2 -22
  2. app/models.py +4 -6
  3. app/static/admin.html +260 -55
app/auth.py CHANGED
@@ -100,7 +100,7 @@ async def create_customer(
100
  """Create a new customer"""
101
  try:
102
  # Validate required fields
103
- required_fields = ["name", "company_name", "email", "db_host", "db_port", "db_user", "db_password", "db_name"]
104
  missing_fields = [field for field in required_fields if field not in customer_data]
105
  if missing_fields:
106
  raise HTTPException(
@@ -121,29 +121,9 @@ async def create_customer(
121
  name=customer_data["name"],
122
  company_name=customer_data["company_name"],
123
  email=customer_data["email"],
124
- api_key=str(secrets.token_urlsafe(32)),
125
- db_host=customer_data["db_host"],
126
- db_port=customer_data["db_port"],
127
- db_user=customer_data["db_user"],
128
- db_password=customer_data["db_password"],
129
- db_name=customer_data["db_name"]
130
  )
131
 
132
- # Test database connection
133
- engine = customer.get_db_engine()
134
- if not engine:
135
- raise HTTPException(
136
- status_code=status.HTTP_400_BAD_REQUEST,
137
- detail="Invalid database credentials or connection failed"
138
- )
139
-
140
- # Create tables in customer's database
141
- if not customer.create_tables():
142
- raise HTTPException(
143
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
144
- detail="Failed to create database tables"
145
- )
146
-
147
  db.add(customer)
148
  db.commit()
149
  db.refresh(customer)
 
100
  """Create a new customer"""
101
  try:
102
  # Validate required fields
103
+ required_fields = ["name", "company_name", "email"]
104
  missing_fields = [field for field in required_fields if field not in customer_data]
105
  if missing_fields:
106
  raise HTTPException(
 
121
  name=customer_data["name"],
122
  company_name=customer_data["company_name"],
123
  email=customer_data["email"],
124
+ api_key=str(secrets.token_urlsafe(32))
 
 
 
 
 
125
  )
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  db.add(customer)
128
  db.commit()
129
  db.refresh(customer)
app/models.py CHANGED
@@ -33,6 +33,10 @@ class Customer(Base):
33
  # Relationship with call records
34
  call_records = relationship("CallRecord", back_populates="customer", cascade="all, delete-orphan")
35
 
 
 
 
 
36
  def get_db_engine(self):
37
  """Create SQLAlchemy engine for customer's database"""
38
  try:
@@ -104,12 +108,6 @@ class Customer(Base):
104
  finally:
105
  session.close()
106
 
107
- def get_db_url(self) -> Optional[str]:
108
- """Get database URL if credentials are configured"""
109
- if all([self.db_host, self.db_port, self.db_name, self.db_user, self.db_password]):
110
- return f"mysql+pymysql://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_name}"
111
- return None
112
-
113
  def get_db_engine(self):
114
  """Get SQLAlchemy engine for customer's database"""
115
  db_url = self.get_db_url()
 
33
  # Relationship with call records
34
  call_records = relationship("CallRecord", back_populates="customer", cascade="all, delete-orphan")
35
 
36
+ def get_db_url(self) -> str:
37
+ """Get the database URL for this customer"""
38
+ return f"mysql+pymysql://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_name}"
39
+
40
  def get_db_engine(self):
41
  """Create SQLAlchemy engine for customer's database"""
42
  try:
 
108
  finally:
109
  session.close()
110
 
 
 
 
 
 
 
111
  def get_db_engine(self):
112
  """Get SQLAlchemy engine for customer's database"""
113
  db_url = self.get_db_url()
app/static/admin.html CHANGED
@@ -5,31 +5,230 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>vBot Admin Interface</title>
7
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
 
8
  <style>
9
- .container { max-width: 800px; margin-top: 2rem; }
10
- .customer-list { margin-top: 2rem; }
11
- .token-section { margin-bottom: 2rem; }
12
- .hidden { display: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  </style>
14
  </head>
15
  <body>
16
  <div class="container">
17
- <h1 class="mb-4">vBot Admin Interface</h1>
 
 
18
 
19
  <!-- Login Section -->
20
  <div class="card token-section" id="loginSection">
 
 
 
21
  <div class="card-body">
22
- <h5 class="card-title">Admin Login</h5>
23
  <form id="loginForm">
24
  <div class="mb-3">
25
  <label for="username" class="form-label">Username</label>
26
- <input type="text" class="form-control" id="username" required>
 
 
 
27
  </div>
28
  <div class="mb-3">
29
  <label for="password" class="form-label">Password</label>
30
- <input type="password" class="form-control" id="password" required>
 
 
 
31
  </div>
32
- <button type="submit" class="btn btn-primary">Login</button>
 
 
33
  </form>
34
  <div id="tokenStatus" class="mt-3"></div>
35
  </div>
@@ -39,42 +238,37 @@
39
  <div id="mainContent" class="hidden">
40
  <!-- Add Customer Section -->
41
  <div class="card">
 
 
 
42
  <div class="card-body">
43
- <h5 class="card-title">Add New Customer</h5>
44
  <form id="customerForm">
45
- <div class="mb-3">
46
- <label for="name" class="form-label">Name</label>
47
- <input type="text" class="form-control" id="name" required>
48
- </div>
49
- <div class="mb-3">
50
- <label for="company_name" class="form-label">Company Name</label>
51
- <input type="text" class="form-control" id="company_name" required>
 
 
 
 
 
 
 
 
52
  </div>
53
  <div class="mb-3">
54
  <label for="email" class="form-label">Email</label>
55
- <input type="email" class="form-control" id="email" required>
 
 
 
56
  </div>
57
- <div class="mb-3">
58
- <label for="db_host" class="form-label">Database Host</label>
59
- <input type="text" class="form-control" id="db_host" required>
60
- </div>
61
- <div class="mb-3">
62
- <label for="db_port" class="form-label">Database Port</label>
63
- <input type="number" class="form-control" id="db_port" value="3306" required>
64
- </div>
65
- <div class="mb-3">
66
- <label for="db_user" class="form-label">Database Username</label>
67
- <input type="text" class="form-control" id="db_user" required>
68
- </div>
69
- <div class="mb-3">
70
- <label for="db_password" class="form-label">Database Password</label>
71
- <input type="password" class="form-control" id="db_password" required>
72
- </div>
73
- <div class="mb-3">
74
- <label for="db_name" class="form-label">Database Name</label>
75
- <input type="text" class="form-control" id="db_name" required>
76
- </div>
77
- <button type="submit" class="btn btn-success">Add Customer</button>
78
  </form>
79
  <div id="customerStatus" class="mt-3"></div>
80
  </div>
@@ -82,8 +276,13 @@
82
 
83
  <!-- Customer List Section -->
84
  <div class="card customer-list">
 
 
 
85
  <div class="card-body">
86
- <h5 class="card-title">Customer List</h5>
 
 
87
  <div id="customerList" class="table-responsive">
88
  <table class="table">
89
  <thead>
@@ -125,18 +324,18 @@
125
  const data = await response.json();
126
  accessToken = data.access_token;
127
  document.getElementById('tokenStatus').innerHTML =
128
- '<div class="alert alert-success">Login successful!</div>';
129
  // Show main content and hide login section
130
  document.getElementById('loginSection').classList.add('hidden');
131
  document.getElementById('mainContent').classList.remove('hidden');
132
  loadCustomers();
133
  } else {
134
  document.getElementById('tokenStatus').innerHTML =
135
- '<div class="alert alert-danger">Login failed. Please check your credentials.</div>';
136
  }
137
  } catch (error) {
138
  document.getElementById('tokenStatus').innerHTML =
139
- '<div class="alert alert-danger">Error connecting to server.</div>';
140
  }
141
  });
142
 
@@ -145,19 +344,14 @@
145
  e.preventDefault();
146
  if (!accessToken) {
147
  document.getElementById('customerStatus').innerHTML =
148
- '<div class="alert alert-danger">Please login first.</div>';
149
  return;
150
  }
151
 
152
  const customerData = {
153
  name: document.getElementById('name').value,
154
  company_name: document.getElementById('company_name').value,
155
- email: document.getElementById('email').value,
156
- db_host: document.getElementById('db_host').value,
157
- db_port: parseInt(document.getElementById('db_port').value),
158
- db_user: document.getElementById('db_user').value,
159
- db_password: document.getElementById('db_password').value,
160
- db_name: document.getElementById('db_name').value
161
  };
162
 
163
  try {
@@ -176,17 +370,17 @@
176
 
177
  if (response.ok) {
178
  document.getElementById('customerStatus').innerHTML =
179
- '<div class="alert alert-success">Customer added successfully!</div>';
180
  document.getElementById('customerForm').reset();
181
  loadCustomers();
182
  } else {
183
  document.getElementById('customerStatus').innerHTML =
184
- `<div class="alert alert-danger">Error: ${data.detail || 'Unknown error occurred'}</div>`;
185
  }
186
  } catch (error) {
187
  console.error('Error creating customer:', error);
188
  document.getElementById('customerStatus').innerHTML =
189
- `<div class="alert alert-danger">Error connecting to server: ${error.message}</div>`;
190
  }
191
  });
192
 
@@ -194,6 +388,9 @@
194
  async function loadCustomers() {
195
  if (!accessToken) return;
196
 
 
 
 
197
  try {
198
  const response = await fetch('/api/v1/customers', {
199
  headers: {
@@ -221,8 +418,12 @@
221
  <td>${customer.name}</td>
222
  <td>${customer.company_name}</td>
223
  <td>${customer.email}</td>
224
- <td><code>${customer.api_key}</code></td>
225
- <td>${customer.is_active ? '<span class="badge bg-success">Active</span>' : '<span class="badge bg-danger">Inactive</span>'}</td>
 
 
 
 
226
  `;
227
  tbody.appendChild(tr);
228
  });
@@ -231,6 +432,7 @@
231
  console.error('Error loading customers:', error);
232
  document.getElementById('customerList').innerHTML = `
233
  <div class="alert alert-danger">
 
234
  Error loading customers: ${error.detail || 'Unknown error'}
235
  </div>
236
  `;
@@ -239,9 +441,12 @@
239
  console.error('Error loading customers:', error);
240
  document.getElementById('customerList').innerHTML = `
241
  <div class="alert alert-danger">
 
242
  Error connecting to server
243
  </div>
244
  `;
 
 
245
  }
246
  }
247
  </script>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>vBot Admin Interface</title>
7
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
9
  <style>
10
+ :root {
11
+ --primary-color: #2c3e50;
12
+ --secondary-color: #3498db;
13
+ --accent-color: #e74c3c;
14
+ --background-color: #f8f9fa;
15
+ --card-background: #ffffff;
16
+ }
17
+
18
+ body {
19
+ background-color: var(--background-color);
20
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1200px;
25
+ margin-top: 2rem;
26
+ padding: 0 1rem;
27
+ }
28
+
29
+ .page-header {
30
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
31
+ color: white;
32
+ padding: 2rem 0;
33
+ margin-bottom: 2rem;
34
+ border-radius: 10px;
35
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
36
+ }
37
+
38
+ .page-header h1 {
39
+ margin: 0;
40
+ font-size: 2.5rem;
41
+ font-weight: 600;
42
+ }
43
+
44
+ .card {
45
+ border: none;
46
+ border-radius: 15px;
47
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
48
+ margin-bottom: 2rem;
49
+ background: var(--card-background);
50
+ transition: transform 0.3s ease;
51
+ }
52
+
53
+ .card:hover {
54
+ transform: translateY(-5px);
55
+ }
56
+
57
+ .card-header {
58
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
59
+ color: white;
60
+ border-radius: 15px 15px 0 0 !important;
61
+ padding: 1.5rem;
62
+ }
63
+
64
+ .card-header h5 {
65
+ margin: 0;
66
+ font-size: 1.5rem;
67
+ font-weight: 600;
68
+ }
69
+
70
+ .card-body {
71
+ padding: 2rem;
72
+ }
73
+
74
+ .form-control {
75
+ border-radius: 8px;
76
+ border: 1px solid #dee2e6;
77
+ padding: 0.75rem;
78
+ transition: all 0.3s ease;
79
+ }
80
+
81
+ .form-control:focus {
82
+ border-color: var(--secondary-color);
83
+ box-shadow: 0 0 0 0.2rem rgba(52, 152, 219, 0.25);
84
+ }
85
+
86
+ .btn {
87
+ border-radius: 8px;
88
+ padding: 0.75rem 1.5rem;
89
+ font-weight: 600;
90
+ transition: all 0.3s ease;
91
+ }
92
+
93
+ .btn-primary {
94
+ background: var(--secondary-color);
95
+ border: none;
96
+ }
97
+
98
+ .btn-primary:hover {
99
+ background: #2980b9;
100
+ transform: translateY(-2px);
101
+ }
102
+
103
+ .btn-success {
104
+ background: #2ecc71;
105
+ border: none;
106
+ }
107
+
108
+ .btn-success:hover {
109
+ background: #27ae60;
110
+ transform: translateY(-2px);
111
+ }
112
+
113
+ .table {
114
+ border-radius: 10px;
115
+ overflow: hidden;
116
+ }
117
+
118
+ .table thead th {
119
+ background: var(--primary-color);
120
+ color: white;
121
+ border: none;
122
+ padding: 1rem;
123
+ }
124
+
125
+ .table tbody td {
126
+ padding: 1rem;
127
+ vertical-align: middle;
128
+ }
129
+
130
+ .badge {
131
+ padding: 0.5rem 1rem;
132
+ border-radius: 20px;
133
+ font-weight: 500;
134
+ }
135
+
136
+ .alert {
137
+ border-radius: 10px;
138
+ border: none;
139
+ padding: 1rem;
140
+ }
141
+
142
+ .token-section {
143
+ max-width: 500px;
144
+ margin: 0 auto;
145
+ }
146
+
147
+ .hidden {
148
+ display: none;
149
+ }
150
+
151
+ .form-label {
152
+ font-weight: 500;
153
+ color: var(--primary-color);
154
+ }
155
+
156
+ .customer-list {
157
+ margin-top: 2rem;
158
+ }
159
+
160
+ .api-key {
161
+ background: #f8f9fa;
162
+ padding: 0.5rem;
163
+ border-radius: 5px;
164
+ font-family: monospace;
165
+ font-size: 0.9rem;
166
+ }
167
+
168
+ .status-badge {
169
+ padding: 0.5rem 1rem;
170
+ border-radius: 20px;
171
+ font-weight: 500;
172
+ }
173
+
174
+ .status-active {
175
+ background: #2ecc71;
176
+ color: white;
177
+ }
178
+
179
+ .status-inactive {
180
+ background: #e74c3c;
181
+ color: white;
182
+ }
183
+
184
+ .loading {
185
+ display: none;
186
+ text-align: center;
187
+ padding: 2rem;
188
+ }
189
+
190
+ .loading i {
191
+ font-size: 2rem;
192
+ color: var(--secondary-color);
193
+ animation: spin 1s linear infinite;
194
+ }
195
+
196
+ @keyframes spin {
197
+ 0% { transform: rotate(0deg); }
198
+ 100% { transform: rotate(360deg); }
199
+ }
200
  </style>
201
  </head>
202
  <body>
203
  <div class="container">
204
+ <div class="page-header text-center">
205
+ <h1><i class="fas fa-robot me-2"></i>vBot Admin Interface</h1>
206
+ </div>
207
 
208
  <!-- Login Section -->
209
  <div class="card token-section" id="loginSection">
210
+ <div class="card-header">
211
+ <h5><i class="fas fa-lock me-2"></i>Admin Login</h5>
212
+ </div>
213
  <div class="card-body">
 
214
  <form id="loginForm">
215
  <div class="mb-3">
216
  <label for="username" class="form-label">Username</label>
217
+ <div class="input-group">
218
+ <span class="input-group-text"><i class="fas fa-user"></i></span>
219
+ <input type="text" class="form-control" id="username" required>
220
+ </div>
221
  </div>
222
  <div class="mb-3">
223
  <label for="password" class="form-label">Password</label>
224
+ <div class="input-group">
225
+ <span class="input-group-text"><i class="fas fa-key"></i></span>
226
+ <input type="password" class="form-control" id="password" required>
227
+ </div>
228
  </div>
229
+ <button type="submit" class="btn btn-primary w-100">
230
+ <i class="fas fa-sign-in-alt me-2"></i>Login
231
+ </button>
232
  </form>
233
  <div id="tokenStatus" class="mt-3"></div>
234
  </div>
 
238
  <div id="mainContent" class="hidden">
239
  <!-- Add Customer Section -->
240
  <div class="card">
241
+ <div class="card-header">
242
+ <h5><i class="fas fa-user-plus me-2"></i>Add New Customer</h5>
243
+ </div>
244
  <div class="card-body">
 
245
  <form id="customerForm">
246
+ <div class="row">
247
+ <div class="col-md-6 mb-3">
248
+ <label for="name" class="form-label">Name</label>
249
+ <div class="input-group">
250
+ <span class="input-group-text"><i class="fas fa-user"></i></span>
251
+ <input type="text" class="form-control" id="name" required>
252
+ </div>
253
+ </div>
254
+ <div class="col-md-6 mb-3">
255
+ <label for="company_name" class="form-label">Company Name</label>
256
+ <div class="input-group">
257
+ <span class="input-group-text"><i class="fas fa-building"></i></span>
258
+ <input type="text" class="form-control" id="company_name" required>
259
+ </div>
260
+ </div>
261
  </div>
262
  <div class="mb-3">
263
  <label for="email" class="form-label">Email</label>
264
+ <div class="input-group">
265
+ <span class="input-group-text"><i class="fas fa-envelope"></i></span>
266
+ <input type="email" class="form-control" id="email" required>
267
+ </div>
268
  </div>
269
+ <button type="submit" class="btn btn-success w-100">
270
+ <i class="fas fa-plus-circle me-2"></i>Add Customer
271
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  </form>
273
  <div id="customerStatus" class="mt-3"></div>
274
  </div>
 
276
 
277
  <!-- Customer List Section -->
278
  <div class="card customer-list">
279
+ <div class="card-header">
280
+ <h5><i class="fas fa-users me-2"></i>Customer List</h5>
281
+ </div>
282
  <div class="card-body">
283
+ <div class="loading">
284
+ <i class="fas fa-spinner"></i>
285
+ </div>
286
  <div id="customerList" class="table-responsive">
287
  <table class="table">
288
  <thead>
 
324
  const data = await response.json();
325
  accessToken = data.access_token;
326
  document.getElementById('tokenStatus').innerHTML =
327
+ '<div class="alert alert-success"><i class="fas fa-check-circle me-2"></i>Login successful!</div>';
328
  // Show main content and hide login section
329
  document.getElementById('loginSection').classList.add('hidden');
330
  document.getElementById('mainContent').classList.remove('hidden');
331
  loadCustomers();
332
  } else {
333
  document.getElementById('tokenStatus').innerHTML =
334
+ '<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Login failed. Please check your credentials.</div>';
335
  }
336
  } catch (error) {
337
  document.getElementById('tokenStatus').innerHTML =
338
+ '<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Error connecting to server.</div>';
339
  }
340
  });
341
 
 
344
  e.preventDefault();
345
  if (!accessToken) {
346
  document.getElementById('customerStatus').innerHTML =
347
+ '<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Please login first.</div>';
348
  return;
349
  }
350
 
351
  const customerData = {
352
  name: document.getElementById('name').value,
353
  company_name: document.getElementById('company_name').value,
354
+ email: document.getElementById('email').value
 
 
 
 
 
355
  };
356
 
357
  try {
 
370
 
371
  if (response.ok) {
372
  document.getElementById('customerStatus').innerHTML =
373
+ '<div class="alert alert-success"><i class="fas fa-check-circle me-2"></i>Customer added successfully!</div>';
374
  document.getElementById('customerForm').reset();
375
  loadCustomers();
376
  } else {
377
  document.getElementById('customerStatus').innerHTML =
378
+ `<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Error: ${data.detail || 'Unknown error occurred'}</div>`;
379
  }
380
  } catch (error) {
381
  console.error('Error creating customer:', error);
382
  document.getElementById('customerStatus').innerHTML =
383
+ `<div class="alert alert-danger"><i class="fas fa-exclamation-circle me-2"></i>Error connecting to server: ${error.message}</div>`;
384
  }
385
  });
386
 
 
388
  async function loadCustomers() {
389
  if (!accessToken) return;
390
 
391
+ const loading = document.querySelector('.loading');
392
+ loading.style.display = 'block';
393
+
394
  try {
395
  const response = await fetch('/api/v1/customers', {
396
  headers: {
 
418
  <td>${customer.name}</td>
419
  <td>${customer.company_name}</td>
420
  <td>${customer.email}</td>
421
+ <td><code class="api-key">${customer.api_key}</code></td>
422
+ <td>
423
+ <span class="status-badge ${customer.is_active ? 'status-active' : 'status-inactive'}">
424
+ ${customer.is_active ? '<i class="fas fa-check-circle me-1"></i>Active' : '<i class="fas fa-times-circle me-1"></i>Inactive'}
425
+ </span>
426
+ </td>
427
  `;
428
  tbody.appendChild(tr);
429
  });
 
432
  console.error('Error loading customers:', error);
433
  document.getElementById('customerList').innerHTML = `
434
  <div class="alert alert-danger">
435
+ <i class="fas fa-exclamation-circle me-2"></i>
436
  Error loading customers: ${error.detail || 'Unknown error'}
437
  </div>
438
  `;
 
441
  console.error('Error loading customers:', error);
442
  document.getElementById('customerList').innerHTML = `
443
  <div class="alert alert-danger">
444
+ <i class="fas fa-exclamation-circle me-2"></i>
445
  Error connecting to server
446
  </div>
447
  `;
448
+ } finally {
449
+ loading.style.display = 'none';
450
  }
451
  }
452
  </script>