Skip to content

Commit 30f379a

Browse files
committed
Limit user sign up
1 parent 38da14d commit 30f379a

File tree

7 files changed

+279
-243
lines changed

7 files changed

+279
-243
lines changed

beaverhabits/app/auth.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ async def user_create(
8383
)
8484
return user
8585
except UserAlreadyExists:
86-
raise
86+
raise Exception("User already exists!")
8787

8888

8989
def user_logout() -> bool:

beaverhabits/app/crud.py

+9
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,12 @@ async def get_user_habit_list(user: User) -> HabitListModel | None:
3737
result = await session.execute(stmt)
3838
logger.info(f"[CRUD] User {user.id} habit list query")
3939
return result.scalar()
40+
41+
42+
async def get_user_count() -> int:
43+
async with get_async_session_context() as session:
44+
stmt = select(User)
45+
result = await session.execute(stmt)
46+
user_count = len(result.all())
47+
logger.info(f"[CRUD] User count query: {user_count}")
48+
return user_count

beaverhabits/configs.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
logging.getLogger("niceGUI").setLevel(logging.INFO)
1010
dotenv.load_dotenv()
1111

12+
USER_DATA_FOLDER = ".user"
13+
1214

1315
class StorageType(Enum):
1416
SESSION = "SESSION"
@@ -29,7 +31,8 @@ class Settings(BaseSettings):
2931

3032
# Storage
3133
HABITS_STORAGE: StorageType = StorageType.USER_DATABASE
32-
DATABASE_URL: str = "sqlite+aiosqlite:///./habits.db"
34+
DATABASE_URL: str = f"sqlite+aiosqlite:///./{USER_DATA_FOLDER}/habits.db"
35+
MAX_USER_COUNT: int = -1
3336

3437
# Customization
3538
FIRST_DAY_OF_WEEK: int = calendar.MONDAY

beaverhabits/routes.py

+28-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Optional
22

3-
from fastapi import Depends, FastAPI, Request
3+
from fastapi import Depends, FastAPI, HTTPException, Request
44
from fastapi.responses import RedirectResponse
55
from fastapi.routing import APIRoute
66
from nicegui import app, ui
@@ -13,6 +13,7 @@
1313
user_create_token,
1414
)
1515
from .app.db import User
16+
from .app.crud import get_user_count
1617
from .app.users import current_active_user
1718
from .configs import settings
1819
from .frontend.add_page import add_page_ui
@@ -113,37 +114,51 @@ async def try_login():
113114

114115
with ui.card().classes("absolute-center shadow-none w-96"):
115116
email = ui.input("email").on("keydown.enter", try_login)
116-
password = ui.input("password", password=True, password_toggle_button=True).on(
117-
"keydown.enter", try_login
118-
)
119-
ui.button("Continue", on_click=try_login).props('padding="xs lg"')
120-
ui.separator()
121-
with ui.row():
122-
ui.label("New around here?").classes("text-sm")
123-
ui.link("Create account", target="/register").classes("text-sm")
117+
email.classes("w-56")
118+
119+
password = ui.input("password", password=True, password_toggle_button=True)
120+
password.on("keydown.enter", try_login)
121+
password.classes("w-56")
122+
123+
with ui.element("div").classes("flex mt-4 justify-between items-center"):
124+
ui.button("Continue", on_click=try_login).props('padding="xs lg"')
125+
126+
if not await get_user_count() >= settings.MAX_USER_COUNT > 0:
127+
ui.separator()
128+
with ui.row():
129+
ui.label("New around here?").classes("text-sm")
130+
ui.link("Create account", target="/register").classes("text-sm")
124131

125132

126133
@ui.page("/register")
127134
async def register():
135+
async def validate_max_user_count():
136+
if await get_user_count() >= settings.MAX_USER_COUNT > 0:
137+
raise HTTPException(status_code=404, detail="User limit reached")
138+
128139
async def try_register():
129140
try:
141+
await validate_max_user_count()
130142
user = await user_create(email=email.value, password=password.value)
131143
except Exception as e:
132-
ui.notify(str(e))
144+
ui.notify(str(e), color="negative")
133145
else:
134146
token = user and await user_create_token(user)
135147
if token is not None:
136148
app.storage.user.update({"auth_token": token})
137149
ui.navigate.to(app.storage.user.get("referrer_path", "/"))
138150

151+
await validate_max_user_count()
152+
139153
with ui.card().classes("absolute-center shadow-none w-96"):
140-
email = ui.input("email").on("keydown.enter", try_register)
154+
email = ui.input("email").on("keydown.enter", try_register).classes("w-56")
141155
password = ui.input("password", password=True, password_toggle_button=True).on(
142156
"keydown.enter", try_register
143-
)
157+
).classes("w-56")
144158

145159
with ui.element("div").classes("flex mt-4 justify-between items-center"):
146-
ui.button("Register", on_click=try_register)
160+
ui.button("Register", on_click=try_register).props('padding="xs lg"')
161+
147162
ui.separator()
148163
with ui.row():
149164
ui.label("Already have an account?")

beaverhabits/storage/user_file.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
from typing import Optional
33

44
from nicegui.storage import PersistentDict
5+
56
from beaverhabits.app.db import User
7+
from beaverhabits.configs import USER_DATA_FOLDER
68
from beaverhabits.storage.dict import DictHabitList
79
from beaverhabits.storage.storage import UserStorage
810

@@ -18,8 +20,8 @@ async def get_user_habit_list(self, user: User) -> Optional[DictHabitList]:
1820

1921
async def save_user_habit_list(self, user: User, habit_list: DictHabitList) -> None:
2022
d = self._get_persistent_dict(user)
21-
d["data"] = habit_list.data
23+
d[KEY_NAME] = habit_list.data
2224

2325
def _get_persistent_dict(self, user: User) -> PersistentDict:
24-
path = Path(f".user/{str(user.email)}.json")
26+
path = Path(f"{USER_DATA_FOLDER}/{str(user.email)}.json")
2527
return PersistentDict(path, encoding="utf-8")

0 commit comments

Comments
 (0)