Skip to content

Commit 87c64e3

Browse files
authored
Enhance/production settings (#203)
* Attempting to add mysql + postgres to Dockerfile for production and still work with testing * Attempting to add mysql + postgres to Dockerfile for production and still work with testing * v0.2.1 * Run postgres no matter what unless you specify not to * Run postgres no matter what unless you specify not to * Updated poetry lock * Updated sqlmode for prod too * Ran black formatter * Added support for AWS S3 * Ran black formatter * Added feature flags via AWS AppConfig * Cleaned up feature flags * Ready for 0.2.1-alpha.6 * Revert "Ready for 0.2.1-alpha.6" This reverts commit 4fa6f45. * Ready for 0.2.1-alpha.6 * Ran black formatter * Ready for FeatureFlags + More testing * Ran formatter * Adding debug statements * Adding feature flags (AWS AppConfig / Cache) * Changed the buttons to be "Dashboard" if logged in, rather than "login" and "register" * Added user roles (USER/DEV/TESTER/STAFF) * Ran formatters * v0.2.1 * Minor cleanup
1 parent a183ccf commit 87c64e3

File tree

23 files changed

+30930
-343
lines changed

23 files changed

+30930
-343
lines changed

backend/admin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from django.contrib import admin
12
from django.contrib.auth.admin import UserAdmin
23

34
from backend.models import (
@@ -16,8 +17,8 @@
1617
TeamInvitation,
1718
User,
1819
InvoiceProduct,
20+
FeatureFlags,
1921
)
20-
from django.contrib import admin
2122

2223
# admin.register(Invoice)
2324
admin.site.register(UserSettings)
@@ -34,6 +35,7 @@
3435
admin.site.register(Team)
3536
admin.site.register(TeamInvitation)
3637
admin.site.register(InvoiceProduct)
38+
admin.site.register(FeatureFlags)
3739
admin.site.register(User, UserAdmin)
3840

3941
admin.site.site_header = "MyFinances Admin"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Generated by Django 5.0.2 on 2024-02-14 19:26
2+
3+
from django.db import migrations, models
4+
5+
import settings.settings
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
("backend", "0015_alter_notification_user_alter_team_name"),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name="invoice",
17+
name="logo",
18+
field=models.ImageField(
19+
blank=True,
20+
null=True,
21+
storage=settings.settings.CustomPrivateMediaStorage(),
22+
upload_to="invoice_logos",
23+
),
24+
),
25+
migrations.AlterField(
26+
model_name="receipt",
27+
name="image",
28+
field=models.ImageField(
29+
storage=settings.settings.CustomPrivateMediaStorage(),
30+
upload_to="receipts",
31+
),
32+
),
33+
migrations.AlterField(
34+
model_name="usersettings",
35+
name="profile_picture",
36+
field=models.ImageField(
37+
blank=True,
38+
null=True,
39+
storage=settings.settings.CustomPublicMediaStorage(),
40+
upload_to="profile_pictures/",
41+
),
42+
),
43+
]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Generated by Django 5.0.2 on 2024-02-18 20:17
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("backend", "0016_alter_invoice_logo_alter_receipt_image_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="FeatureFlags",
15+
fields=[
16+
(
17+
"id",
18+
models.BigAutoField(
19+
auto_created=True,
20+
primary_key=True,
21+
serialize=False,
22+
verbose_name="ID",
23+
),
24+
),
25+
("name", models.CharField(max_length=100)),
26+
("value", models.BooleanField(default=False)),
27+
("updated_at", models.DateTimeField(auto_now=True)),
28+
],
29+
),
30+
]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Generated by Django 5.0.1 on 2024-02-20 10:18
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("backend", "0017_featureflags"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="user",
15+
name="role",
16+
field=models.CharField(
17+
choices=[
18+
("DEV", "Developer"),
19+
("STAFF", "Staff"),
20+
("USER", "User"),
21+
("TESTER", "Tester"),
22+
],
23+
default="USER",
24+
max_length=10,
25+
),
26+
),
27+
]

backend/models.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ class User(AbstractUser):
3838

3939
logged_in_as_team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True)
4040

41+
class Role(models.TextChoices):
42+
# NAME DJANGO ADMIN NAME
43+
DEV = "DEV", "Developer"
44+
STAFF = "STAFF", "Staff"
45+
USER = "USER", "User"
46+
TESTER = "TESTER", "Tester"
47+
48+
role = models.CharField(max_length=10, choices=Role.choices, default=Role.USER)
49+
4150

4251
class CustomUserMiddleware:
4352
def __init__(self, get_response):
@@ -79,7 +88,10 @@ class UserSettings(models.Model):
7988
choices=[(code, info["name"]) for code, info in CURRENCIES.items()],
8089
)
8190
profile_picture = models.ImageField(
82-
upload_to="profile_pictures/", blank=True, null=True
91+
upload_to="profile_pictures/",
92+
storage=settings.CustomPublicMediaStorage(),
93+
blank=True,
94+
null=True,
8395
)
8496

8597
@property
@@ -148,7 +160,9 @@ class Receipt(models.Model):
148160
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
149161
organization = models.ForeignKey(Team, on_delete=models.CASCADE, null=True)
150162
name = models.CharField(max_length=100)
151-
image = models.ImageField(upload_to="receipts")
163+
image = models.ImageField(
164+
upload_to="receipts", storage=settings.CustomPrivateMediaStorage()
165+
)
152166
total_price = models.FloatField(null=True, blank=True)
153167
date = models.DateField(null=True, blank=True)
154168
date_uploaded = models.DateTimeField(auto_now_add=True)
@@ -254,7 +268,12 @@ class Invoice(models.Model):
254268
reference = models.CharField(max_length=100, blank=True, null=True)
255269
invoice_number = models.CharField(max_length=100, blank=True, null=True)
256270
vat_number = models.CharField(max_length=100, blank=True, null=True)
257-
logo = models.ImageField(upload_to="invoice_logos", blank=True, null=True)
271+
logo = models.ImageField(
272+
upload_to="invoice_logos",
273+
storage=settings.CustomPrivateMediaStorage(),
274+
blank=True,
275+
null=True,
276+
)
258277
notes = models.TextField(blank=True, null=True)
259278

260279
payment_status = models.CharField(
@@ -414,6 +433,12 @@ def __str__(self):
414433
return str(self.error)
415434

416435

436+
class FeatureFlags(models.Model):
437+
name = models.CharField(max_length=100)
438+
value = models.BooleanField(default=False)
439+
updated_at = models.DateTimeField(auto_now=True)
440+
441+
417442
def SEND_SENDGRID_EMAIL(
418443
to_email,
419444
subject,

backend/signals.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from django.core.files.storage import default_storage
2-
from django.db.models.signals import pre_save, post_delete, post_save
2+
from django.db.models.signals import pre_save, post_delete, post_save, post_migrate
33
from django.dispatch import receiver
44

5-
from backend.models import UserSettings, Receipt, User
5+
from backend.models import UserSettings, Receipt, User, FeatureFlags
66

77

88
@receiver(pre_save, sender=UserSettings)
@@ -51,3 +51,23 @@ def user_account_create_make_usersettings(sender, instance, created, **kwargs):
5151

5252
if not users_settings:
5353
UserSettings.objects.create(user=instance)
54+
55+
56+
@receiver(post_delete, sender=Receipt)
57+
def delete_receipt_image_on_delete(sender, instance: Receipt, **kwargs):
58+
instance.image.delete(False)
59+
60+
61+
feature_flags = [{"name": "areSignupsEnabled", "default": True, "pk": 1}]
62+
63+
64+
def insert_initial_data(**kwargs):
65+
for feature in feature_flags:
66+
FeatureFlags.objects.get_or_create(
67+
id=feature.get("pk"),
68+
name=feature.get("name"),
69+
value=feature.get("default"),
70+
)
71+
72+
73+
post_migrate.connect(insert_initial_data)

backend/utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,20 @@
1+
from django.core.cache import cache
2+
from django.core.cache.backends.redis import RedisCacheClient
13

4+
cache: RedisCacheClient = cache
5+
6+
from backend.models import FeatureFlags
7+
8+
9+
def get_feature_status(feature):
10+
key = f"myfinances:feature_flag:{feature}"
11+
cached_value = cache.get(key)
12+
if cached_value:
13+
return cached_value
14+
15+
value = FeatureFlags.objects.filter(name=feature).first()
16+
if value:
17+
cache.set(key, value.value, timeout=300)
18+
return value.value
19+
else:
20+
return False

backend/views/core/other/index.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from django.core.cache import cache
12
from django.http import HttpRequest
23
from django.shortcuts import render
34

backend/views/core/other/login.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
from backend.decorators import *
1212
from backend.models import LoginLog, AuditLog, User
13+
from backend.utils import get_feature_status
14+
15+
# from backend.utils import appconfig
1316
from settings.settings import (
1417
SOCIAL_AUTH_GITHUB_ENABLED,
1518
SOCIAL_AUTH_GOOGLE_OAUTH2_ENABLED,
@@ -66,6 +69,10 @@ class CreateAccountChooseView(View):
6669
def get(self, request):
6770
if request.user.is_authenticated:
6871
return redirect("dashboard")
72+
SIGNUPS_ENABLED = get_feature_status("areSignupsEnabled")
73+
if not SIGNUPS_ENABLED:
74+
messages.error(request, "New account signups are currently disabled")
75+
return redirect("login")
6976
return render(
7077
request,
7178
"pages/login/create_account_choose.html",
@@ -80,11 +87,19 @@ class CreateAccountManualView(View):
8087
def get(self, request):
8188
if request.user.is_authenticated:
8289
return redirect("dashboard")
90+
SIGNUPS_ENABLED = get_feature_status("areSignupsEnabled")
91+
if not SIGNUPS_ENABLED:
92+
messages.error(request, "New account signups are currently disabled")
93+
return redirect("login")
8394
return render(request, "pages/login/create_account_manual.html")
8495

8596
def post(self, request):
8697
if request.user.is_authenticated:
8798
return redirect("dashboard")
99+
SIGNUPS_ENABLED = get_feature_status("areSignupsEnabled")
100+
if not SIGNUPS_ENABLED:
101+
messages.error(request, "New account signups are currently disabled")
102+
return redirect("login")
88103

89104
email = request.POST.get("email")
90105
password = request.POST.get("password")

docs/changelog.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,42 @@
11
# Changelog
22

3-
## [v0.1.1](https://github.com/TreyWW/MyFinances/releases/tag/v0.1.1) <small>([view diff](https://github.com/TreyWW/MyFinances/compare/v0.1.0...v0.1.1))</small>
3+
## [v0.2.1](https://github.com/TreyWW/MyFinances/releases/tag/v0.2.1) <small>([view diff](https://github.com/TreyWW/MyFinances/compare/v0.2.0...v0.2.1))</small>
4+
### Overview
5+
* 🚩 Added **feature flags**
6+
* 🐘 Added more **postgres** support
7+
* 🚅 Added **cache** settings
8+
* 🎉 **Deployment** has been **successful** and will now undergo more testing!
9+
* 👋 Possible clients already interested and ready for official launches!
10+
11+
12+
## [v0.2.0](https://github.com/TreyWW/MyFinances/releases/tag/v0.2.0) <small>([view diff](https://github.com/TreyWW/MyFinances/compare/v0.1.2...v0.2.0))</small>
13+
### Overview
14+
* 👥 Added **Teams** functionality
15+
* 🐘 Added **PostgreSQL** database support
16+
* 🐧 Added **matrix** support so we can now run tests with multiple django versions and multiple ubuntu versions
17+
* 🧾 Added PDF Upload option for receipts
18+
* 🧹 Added djLint for HTML file linting
19+
* 📦 Ready to start getting the product ready for pre-production and more testing
20+
421

5-
### Features
22+
## [v0.1.2](https://github.com/TreyWW/MyFinances/releases/tag/v0.1.2) <small>([view diff](https://github.com/TreyWW/MyFinances/compare/v0.1.1...v0.1.2))</small>
23+
### Overview
24+
* 📦 Added [python poetry](https://python-poetry.org/)
25+
* ✏️ Added the ability to edit invoices
26+
* 📜 Added [hyperscript](https://hyperscript.org/)
27+
* 🧾 Added more info for receipts & the ability to SAFELY download them
28+
* 🐬 Fixed docker builds, now uses poetry, and runs much faster
29+
30+
31+
## [v0.1.1](https://github.com/TreyWW/MyFinances/releases/tag/v0.1.1) <small>([view diff](https://github.com/TreyWW/MyFinances/compare/v0.1.0...v0.1.1))</small>
32+
### Overview
633
* Added documentation
734
* Added social logins (github + google)
835
* Added custom colours for tailwind theme
936
* Added signal for UserSettings addition on user creation
1037

1138
## [v0.1.0](https://github.com/TreyWW/MyFinances/releases/tag/v0.1.0)
12-
39+
### Overview
1340
First release of the project. Some core features that we made a start to:
1441
- 🧾 Receipts
1542
- 📜 Invoices

0 commit comments

Comments
 (0)