Skip to content

Commit 83bdb43

Browse files
author
mohamedachrefhacheni
committed
adding user_role_system
1 parent ba1706b commit 83bdb43

File tree

5,286 files changed

+1377748
-6
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

5,286 files changed

+1377748
-6
lines changed

.env

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ STACK_NAME=full-stack-fastapi-project
1818

1919
# Backend
2020
BACKEND_CORS_ORIGINS="http://localhost,http://localhost:5173,https://localhost,https://localhost:5173,http://localhost.tiangolo.com"
21-
SECRET_KEY=changethis
21+
SECRET_KEY=thissrckeyforyou
2222
FIRST_SUPERUSER=[email protected]
23-
FIRST_SUPERUSER_PASSWORD=changethis
23+
FIRST_SUPERUSER_PASSWORD=admin123
2424

2525
# Emails
2626
SMTP_HOST=
@@ -36,7 +36,8 @@ POSTGRES_SERVER=localhost
3636
POSTGRES_PORT=5432
3737
POSTGRES_DB=app
3838
POSTGRES_USER=postgres
39-
POSTGRES_PASSWORD=changethis
39+
POSTGRES_PASSWORD=12345
40+
4041

4142
SENTRY_DSN=
4243

backend/app/api/routes/users.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import uuid
22
from typing import Any
33

4+
5+
from fastapi import APIRouter, HTTPException, Depends
6+
from app.models import User, UserRole
7+
from app.core.security import hash_password
8+
from app.core.db import SessionDep
9+
10+
router = APIRouter()
11+
412
from fastapi import APIRouter, Depends, HTTPException
513
from sqlmodel import col, delete, func, select
614

@@ -226,3 +234,17 @@ def delete_user(
226234
session.delete(user)
227235
session.commit()
228236
return Message(message="User deleted successfully")
237+
def register_user(
238+
email: str,
239+
password: str,
240+
role: UserRole = UserRole.USER, # Allow specifying role (default: user)
241+
session: SessionDep = Depends(),
242+
):
243+
existing_user = session.query(User).filter(User.email == email).first()
244+
if existing_user:
245+
raise HTTPException(status_code=400, detail="Email already registered")
246+
247+
user = User(email=email, hashed_password=hash_password(password), role=role)
248+
session.add(user)
249+
session.commit()
250+
return {"message": "User created successfully", "role": user.role}

backend/app/core/config.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ def all_cors_origins(self) -> list[str]:
5353
POSTGRES_SERVER: str
5454
POSTGRES_PORT: int = 5432
5555
POSTGRES_USER: str
56-
POSTGRES_PASSWORD: str = ""
57-
POSTGRES_DB: str = ""
56+
POSTGRES_PASSWORD: str = "12345"
57+
POSTGRES_DB: str = "app"
5858

5959
@computed_field # type: ignore[prop-decorator]
6060
@property
@@ -120,3 +120,5 @@ def _enforce_non_default_secrets(self) -> Self:
120120

121121

122122
settings = Settings() # type: ignore
123+
SQLALCHEMY_DATABASE_URL = "postgresql://postgres:12345@localhost:5432/app"
124+

backend/app/core/db.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
def init_db(session: Session) -> None:
1616
# Tables should be created with Alembic migrations
17-
# But if you don't want to use migrations, create
17+
# But if you don't want to use migrations, create
1818
# the tables un-commenting the next lines
1919
# from sqlmodel import SQLModel
2020

backend/app/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class User(UserBase, table=True):
4545
hashed_password: str
4646
items: list["Item"] = Relationship(back_populates="owner", cascade_delete=True)
4747

48+
class UserRole(str, Enum):
49+
USER = "user"
50+
ADMIN = "admin"
51+
MODERATOR = "moderator"
52+
4853

4954
# Properties to return via API, id is always required
5055
class UserPublic(UserBase):
@@ -112,3 +117,5 @@ class TokenPayload(SQLModel):
112117
class NewPassword(SQLModel):
113118
token: str
114119
new_password: str = Field(min_length=8, max_length=40)
120+
121+

backend/poetry.lock

Lines changed: 1976 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies = [
2121
"pydantic-settings<3.0.0,>=2.2.1",
2222
"sentry-sdk[fastapi]<2.0.0,>=1.40.6",
2323
"pyjwt<3.0.0,>=2.8.0",
24+
"uvicorn (>=0.34.0,<0.35.0)",
2425
]
2526

2627
[tool.uv]
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
2+
3+
/* Greenlet object interface */
4+
5+
#ifndef Py_GREENLETOBJECT_H
6+
#define Py_GREENLETOBJECT_H
7+
8+
9+
#include <Python.h>
10+
11+
#ifdef __cplusplus
12+
extern "C" {
13+
#endif
14+
15+
/* This is deprecated and undocumented. It does not change. */
16+
#define GREENLET_VERSION "1.0.0"
17+
18+
#ifndef GREENLET_MODULE
19+
#define implementation_ptr_t void*
20+
#endif
21+
22+
typedef struct _greenlet {
23+
PyObject_HEAD
24+
PyObject* weakreflist;
25+
PyObject* dict;
26+
implementation_ptr_t pimpl;
27+
} PyGreenlet;
28+
29+
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
30+
31+
32+
/* C API functions */
33+
34+
/* Total number of symbols that are exported */
35+
#define PyGreenlet_API_pointers 12
36+
37+
#define PyGreenlet_Type_NUM 0
38+
#define PyExc_GreenletError_NUM 1
39+
#define PyExc_GreenletExit_NUM 2
40+
41+
#define PyGreenlet_New_NUM 3
42+
#define PyGreenlet_GetCurrent_NUM 4
43+
#define PyGreenlet_Throw_NUM 5
44+
#define PyGreenlet_Switch_NUM 6
45+
#define PyGreenlet_SetParent_NUM 7
46+
47+
#define PyGreenlet_MAIN_NUM 8
48+
#define PyGreenlet_STARTED_NUM 9
49+
#define PyGreenlet_ACTIVE_NUM 10
50+
#define PyGreenlet_GET_PARENT_NUM 11
51+
52+
#ifndef GREENLET_MODULE
53+
/* This section is used by modules that uses the greenlet C API */
54+
static void** _PyGreenlet_API = NULL;
55+
56+
# define PyGreenlet_Type \
57+
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
58+
59+
# define PyExc_GreenletError \
60+
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
61+
62+
# define PyExc_GreenletExit \
63+
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
64+
65+
/*
66+
* PyGreenlet_New(PyObject *args)
67+
*
68+
* greenlet.greenlet(run, parent=None)
69+
*/
70+
# define PyGreenlet_New \
71+
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
72+
_PyGreenlet_API[PyGreenlet_New_NUM])
73+
74+
/*
75+
* PyGreenlet_GetCurrent(void)
76+
*
77+
* greenlet.getcurrent()
78+
*/
79+
# define PyGreenlet_GetCurrent \
80+
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
81+
82+
/*
83+
* PyGreenlet_Throw(
84+
* PyGreenlet *greenlet,
85+
* PyObject *typ,
86+
* PyObject *val,
87+
* PyObject *tb)
88+
*
89+
* g.throw(...)
90+
*/
91+
# define PyGreenlet_Throw \
92+
(*(PyObject * (*)(PyGreenlet * self, \
93+
PyObject * typ, \
94+
PyObject * val, \
95+
PyObject * tb)) \
96+
_PyGreenlet_API[PyGreenlet_Throw_NUM])
97+
98+
/*
99+
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
100+
*
101+
* g.switch(*args, **kwargs)
102+
*/
103+
# define PyGreenlet_Switch \
104+
(*(PyObject * \
105+
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
106+
_PyGreenlet_API[PyGreenlet_Switch_NUM])
107+
108+
/*
109+
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
110+
*
111+
* g.parent = new_parent
112+
*/
113+
# define PyGreenlet_SetParent \
114+
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
115+
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
116+
117+
/*
118+
* PyGreenlet_GetParent(PyObject* greenlet)
119+
*
120+
* return greenlet.parent;
121+
*
122+
* This could return NULL even if there is no exception active.
123+
* If it does not return NULL, you are responsible for decrementing the
124+
* reference count.
125+
*/
126+
# define PyGreenlet_GetParent \
127+
(*(PyGreenlet* (*)(PyGreenlet*)) \
128+
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
129+
130+
/*
131+
* deprecated, undocumented alias.
132+
*/
133+
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
134+
135+
# define PyGreenlet_MAIN \
136+
(*(int (*)(PyGreenlet*)) \
137+
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
138+
139+
# define PyGreenlet_STARTED \
140+
(*(int (*)(PyGreenlet*)) \
141+
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
142+
143+
# define PyGreenlet_ACTIVE \
144+
(*(int (*)(PyGreenlet*)) \
145+
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
146+
147+
148+
149+
150+
/* Macro that imports greenlet and initializes C API */
151+
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
152+
keep the older definition to be sure older code that might have a copy of
153+
the header still works. */
154+
# define PyGreenlet_Import() \
155+
{ \
156+
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
157+
}
158+
159+
#endif /* GREENLET_MODULE */
160+
161+
#ifdef __cplusplus
162+
}
163+
#endif
164+
#endif /* !Py_GREENLETOBJECT_H */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Poetry 2.1.1
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright 2006-2025 the Mako authors and contributors <see AUTHORS file>.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of
4+
this software and associated documentation files (the "Software"), to deal in
5+
the Software without restriction, including without limitation the rights to
6+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7+
of the Software, and to permit persons to whom the Software is furnished to do
8+
so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

0 commit comments

Comments
 (0)