devnamdev2003 commited on
Commit
1d8dc03
·
1 Parent(s): 9d354e3
Files changed (3) hide show
  1. Dockerfile +1 -1
  2. api/views.py +11 -355
  3. run_migrations.bat +3 -0
Dockerfile CHANGED
@@ -22,4 +22,4 @@ USER user
22
  EXPOSE 7860
23
 
24
  # Run Django with Gunicorn (PRODUCTION SAFE)
25
- CMD ["gunicorn", "expensewise_api.wsgi:application", "--bind", "0.0.0.0:7860"]
 
22
  EXPOSE 7860
23
 
24
  # Run Django with Gunicorn (PRODUCTION SAFE)
25
+ CMD sh -c "python manage.py migrate && gunicorn expensewise_api.wsgi:application --bind 0.0.0.0:7860"
api/views.py CHANGED
@@ -5,10 +5,6 @@ from .models import UserData, AIKey, AppVersion, Contact
5
  from django.shortcuts import get_object_or_404
6
  from .serializers import ContactSerializer
7
  from rest_framework.generics import CreateAPIView
8
- import os
9
- import smtplib
10
- from email.mime.text import MIMEText
11
- from email.mime.multipart import MIMEMultipart
12
 
13
 
14
  class UserDataPostView(APIView):
@@ -98,361 +94,21 @@ class GetFieldView(APIView):
98
  return Response({"error": "Invalid field"}, status=status.HTTP_400_BAD_REQUEST)
99
 
100
 
101
- upper_body = """
102
- <!DOCTYPE html>
103
- <html lang="en">
104
-
105
- <head>
106
- <meta charset="UTF-8">
107
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
108
- <title>Contact Form Confirmation Template</title>
109
-
110
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
111
-
112
- <style>
113
- :root {
114
- --brand-100: #dbeafe;
115
- --brand-500: #3b82f6;
116
- --brand-600: #2563eb;
117
- --brand-900: #1e3a8a;
118
- --gray-50: #f9fafb;
119
- --gray-100: #f3f4f6;
120
- --gray-200: #e5e7eb;
121
- --gray-400: #9ca3af;
122
- --gray-500: #6b7280;
123
- --gray-600: #4b5563;
124
- --gray-700: #374151;
125
- --gray-900: #111827;
126
- --white: #ffffff;
127
- }
128
-
129
- body {
130
- margin: 0;
131
- padding: 20px;
132
- background-color: var(--gray-100);
133
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
134
- color: var(--gray-700);
135
- display: flex;
136
- flex-direction: column;
137
- align-items: center;
138
- min-height: 100vh;
139
- }
140
-
141
- a {
142
- text-decoration: none;
143
- }
144
-
145
- strong {
146
- font-weight: 700;
147
- }
148
-
149
- .btn-copy {
150
- background-color: var(--gray-900);
151
- color: var(--white);
152
- border: none;
153
- padding: 8px 16px;
154
- border-radius: 6px;
155
- font-size: 14px;
156
- font-weight: 500;
157
- cursor: pointer;
158
- display: flex;
159
- align-items: center;
160
- gap: 8px;
161
- transition: background-color 0.2s;
162
- }
163
-
164
- .btn-copy:hover {
165
- background-color: #000000;
166
- }
167
-
168
- #email-template {
169
- width: 100%;
170
- max-width: 600px;
171
- background-color: var(--white);
172
- border-radius: 12px;
173
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
174
- overflow: hidden;
175
- border: 1px solid var(--gray-100);
176
- margin: 0 auto;
177
- }
178
-
179
- .email-header {
180
- background-color: var(--brand-600);
181
- padding: 32px;
182
- text-align: left;
183
- }
184
-
185
- .email-header h1 {
186
- color: var(--white);
187
- font-size: 24px;
188
- font-weight: 700;
189
- letter-spacing: 0.025em;
190
- margin: 0;
191
- text-transform: uppercase;
192
- }
193
-
194
- .email-header p {
195
- color: var(--brand-100);
196
- margin-top: 4px;
197
- margin-bottom: 0;
198
- font-size: 14px;
199
- }
200
-
201
- .email-body {
202
- padding: 32px;
203
- }
204
-
205
- .greeting {
206
- font-size: 18px;
207
- color: var(--gray-700);
208
- margin-bottom: 24px;
209
- }
210
-
211
- .highlight {
212
- color: var(--brand-600);
213
- font-weight: 600;
214
- }
215
-
216
- .text-paragraph {
217
- color: var(--gray-600);
218
- line-height: 1.6;
219
- margin-bottom: 24px;
220
- margin-top: 0;
221
- }
222
-
223
- .recap-box {
224
- background-color: var(--gray-50);
225
- border-radius: 8px;
226
- padding: 24px;
227
- border-left: 4px solid var(--brand-500);
228
- margin-bottom: 32px;
229
- }
230
-
231
- .recap-label {
232
- font-size: 12px;
233
- font-weight: 700;
234
- color: var(--gray-400);
235
- text-transform: uppercase;
236
- letter-spacing: 0.05em;
237
- margin: 0 0 8px 0;
238
- }
239
-
240
- .recap-content {
241
- font-style: italic;
242
- color: var(--gray-600);
243
- font-size: 14px;
244
- margin: 0;
245
- }
246
-
247
- .btn-container {
248
- margin-top: 32px;
249
- text-align: left;
250
- }
251
-
252
- .btn-primary {
253
- display: inline-block;
254
- background-color: var(--brand-600);
255
- color: var(--white);
256
- font-weight: 500;
257
- padding: 12px 24px;
258
- border-radius: 8px;
259
- font-size: 14px;
260
- transition: background-color 0.2s;
261
- }
262
-
263
- .btn-primary:hover {
264
- background-color: var(--brand-900);
265
- }
266
-
267
- .email-footer {
268
- background-color: var(--gray-50);
269
- padding: 24px 32px;
270
- border-top: 1px solid var(--gray-100);
271
- text-align: center;
272
- }
273
-
274
- .social-icons {
275
- margin-bottom: 16px;
276
- }
277
-
278
- .social-link {
279
- color: var(--gray-400);
280
- margin: 0 8px;
281
- font-size: 20px;
282
- transition: color 0.2s;
283
- }
284
-
285
- .social-link:hover {
286
- color: var(--brand-600);
287
- }
288
-
289
- .footer-text {
290
- font-size: 12px;
291
- color: var(--gray-400);
292
- margin: 0;
293
- line-height: 1.5;
294
- }
295
-
296
- .footer-link {
297
- color: var(--gray-400);
298
- text-decoration: underline;
299
- }
300
-
301
- .footer-link:hover {
302
- color: var(--gray-600);
303
- }
304
-
305
- #toast {
306
- position: fixed;
307
- bottom: 20px;
308
- right: 20px;
309
- background-color: var(--gray-900);
310
- color: var(--white);
311
- padding: 12px 24px;
312
- border-radius: 8px;
313
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
314
- opacity: 0;
315
- transform: translateY(20px);
316
- transition: all 0.3s ease;
317
- }
318
-
319
- #toast.show {
320
- opacity: 1;
321
- transform: translateY(0);
322
- }
323
-
324
- @media (max-width: 600px) {
325
-
326
- .email-header,
327
- .email-body,
328
- .email-footer {
329
- padding: 24px;
330
- }
331
-
332
- .email-header,
333
- .btn-container {
334
- text-align: center;
335
- }
336
- .btn-copy {
337
- width: 100%;
338
- justify-content: center;
339
- }
340
- }
341
- </style>
342
- </head>
343
-
344
- <body>
345
- <div id="email-template">
346
- <div class="email-header">
347
- <h1>THANK YOU!</h1>
348
- <p>I've received your message.</p>
349
- </div>
350
- <div class="email-body">
351
- <p class="greeting">Hi <span class="highlight">"""
352
-
353
- mid_body = """</span>,</p>
354
- <p class="text-paragraph">
355
- Thanks for reaching out via my portfolio! I wanted to let you know that your message has landed safely
356
- in my inbox.
357
- </p>
358
- <p class="text-paragraph">
359
- I appreciate your interest. I typically review inquiries within <strong>24 hours</strong> and will get
360
- back to you as soon as possible.
361
- </p>
362
- <div class="recap-box">
363
- <h3 class="recap-label">You wrote:</h3>
364
- <p class="recap-content">
365
- """
366
-
367
- lower_body = """
368
- </p>
369
- </div>
370
- <p class="text-paragraph" style="margin-bottom: 0;">
371
- Best regards,<br>
372
- <span style="font-weight: 600; color: #111827;">Dev Namdev</span>
373
- </p>
374
- <div class="btn-container">
375
- <a href="https://devnamdev2003.github.io/" class="btn-primary">
376
- Return to Portfolio
377
- </a>
378
- </div>
379
- </div>
380
- <div class="email-footer">
381
- <div class="social-icons">
382
- <a href="https://www.linkedin.com/in/devnamdev/" class="social-link"><i class="fab fa-linkedin"></i></a>
383
- <a href="https://github.com/devnamdev2003" class="social-link"><i class="fab fa-github"></i></a>
384
- </div>
385
- <p class="footer-text">
386
- &copy; 2024 Dev Namdev. All rights reserved.<br>
387
- </p>
388
- </div>
389
- </div>
390
-
391
- </body>
392
-
393
- </html>
394
- """
395
-
396
- EMAIL_HOST = "smtp.gmail.com"
397
- EMAIL_PORT = 587
398
- EMAIL_USER = os.getenv("EMAIL_HOST_USER")
399
- EMAIL_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD")
400
-
401
-
402
  class ContactList1(CreateAPIView):
403
  queryset = Contact.objects.all()
404
  serializer_class = ContactSerializer
405
 
406
- def perform_create(self, serializer):
407
- instance = serializer.save()
408
- user_ip = self.get_client_ip(self.request)
409
-
410
- email = instance.email
411
- name = instance.name
412
- message = instance.message
413
-
414
- try:
415
- print("mail sending..")
416
- msg = MIMEMultipart()
417
- msg["From"] = EMAIL_USER
418
- msg["To"] = email
419
- msg["Subject"] = "Confirmation: We've Received Your Message"
420
-
421
- body = upper_body + name + mid_body + message + lower_body
422
- msg.attach(MIMEText(body, "html"))
423
 
424
- # 🔥 IMPORTANT: timeout added
425
- server = smtplib.SMTP(EMAIL_HOST, EMAIL_PORT, timeout=10)
426
- server.starttls()
427
- server.login(EMAIL_USER, EMAIL_PASSWORD)
428
-
429
- server.sendmail(EMAIL_USER, email, msg.as_string())
430
- print("mail send to user")
431
-
432
- admin_text = (
433
- f"Subject: Form Submission from {name}\n\n"
434
- f"Name: {name}\n"
435
- f"Email: {email}\n"
436
- f"Message: {message}\n"
437
- f"IP Address: {user_ip}"
438
  )
439
 
440
- server.sendmail(EMAIL_USER, "devnamdevcse@gmail.com", admin_text)
441
- print("mail send to devnamdevcse@gmail.com")
442
- except Exception as e:
443
- # ❗ Prevent worker crash
444
- print("Email error:", e)
445
-
446
- finally:
447
- try:
448
- server.quit()
449
- except:
450
- pass
451
-
452
- def get_client_ip(self, request):
453
- x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
454
- if x_forwarded_for:
455
- ip = x_forwarded_for.split(",")[0]
456
- else:
457
- ip = request.META.get("REMOTE_ADDR")
458
- return ip
 
5
  from django.shortcuts import get_object_or_404
6
  from .serializers import ContactSerializer
7
  from rest_framework.generics import CreateAPIView
 
 
 
 
8
 
9
 
10
  class UserDataPostView(APIView):
 
94
  return Response({"error": "Invalid field"}, status=status.HTTP_400_BAD_REQUEST)
95
 
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  class ContactList1(CreateAPIView):
98
  queryset = Contact.objects.all()
99
  serializer_class = ContactSerializer
100
 
101
+ def create(self, request, *args, **kwargs):
102
+ serializer = self.get_serializer(data=request.data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
+ if serializer.is_valid():
105
+ serializer.save()
106
+ return Response(
107
+ {"message": "Contact saved successfully"},
108
+ status=status.HTTP_201_CREATED
 
 
 
 
 
 
 
 
 
109
  )
110
 
111
+ return Response(
112
+ {"message": "Validation failed", "errors": serializer.errors},
113
+ status=status.HTTP_400_BAD_REQUEST
114
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
run_migrations.bat CHANGED
@@ -3,6 +3,9 @@
3
  REM Activate the virtual environment
4
  call env\Scripts\activate.bat
5
 
 
 
 
6
  REM Make and apply migrations
7
  python manage.py makemigrations
8
 
 
3
  REM Activate the virtual environment
4
  call env\Scripts\activate.bat
5
 
6
+ REM Set environment variable to live
7
+ set DEVELOPMENT=local
8
+
9
  REM Make and apply migrations
10
  python manage.py makemigrations
11