Skip to content

Commit 1119f05

Browse files
authored
Merge pull request #25 from ArielMAJ/release/2.0.0
Release/2.0.0
2 parents 6c5cc13 + 1342b07 commit 1119f05

30 files changed

Lines changed: 1155 additions & 383 deletions

.env.example

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
APPLICATION_HOST=127.0.0.1
22
APPLICATION_PORT=3000
33

4-
APPLICATION_ROOT=
5-
64
POSTGRES_USER=postgres
75
POSTGRES_PASSWORD=postgres
86
POSTGRES_HOST=localhost
97
POSTGRES_DATABASE=postgres
108
POSTGRES_PORT=5432
119

12-
POSTGRES_ECHO=true
13-
DATABASE_ENABLE_CONNECTION_POOLING=true
10+
POSTGRES_ECHO=true # Set to false if you don't want to see SQL queries in the console
11+
DATABASE_ENABLE_CONNECTION_POOLING=true # Set to false to correctly work on Vercel
12+
13+
LOGGER_IGNORE_PATHS=GET:/docs,GET:/openapi.json
14+
LOGGER_IGNORE_INPUT_BODY_PATHS=POST:/user/register,PUT:/user/,POST:/auth/token
15+
LOGGER_IGNORE_OUTPUT_BODY_PATHS=POST:/auth/token
1416

17+
# Needs to be set to correctly work. Run `make generate-secret-key` to generate a random key.
1518
SECRET_KEY=
1619
ALGORITHM=
1720
ACCESS_TOKEN_EXPIRE_MINUTES=

.flake8

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[flake8]
2+
extend-ignore = W503

Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,38 @@ minor: ## Bump project version to next minor (feature release).
6969
major: ## Bump project version to next major (breaking change).
7070
poetry version major
7171

72+
.PHONY: generate-secret-key
73+
generate-secret-key: ## Generate a new secret key.
74+
poetry run python -c 'import secrets; print(secrets.token_hex(32))'
75+
76+
.PHONY: init-env
77+
init-env: ## Copy .env.example to .env and populate SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES.
78+
@if [ -f .env ]; then \
79+
echo "Error: .env already exists. Aborting."; \
80+
exit 1; \
81+
fi;
82+
83+
@cp .env.example .env
84+
@sed -i '' -e '/^SECRET_KEY=/d' -e '/^ALGORITHM=/d' -e '/^ACCESS_TOKEN_EXPIRE_MINUTES=/d' .env
85+
86+
@SECRET_KEY=$$( \
87+
if command -v python >/dev/null 2>&1; then \
88+
python -c 'import secrets; print(secrets.token_hex(32))'; \
89+
elif command -v node >/dev/null 2>&1; then \
90+
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"; \
91+
elif command -v openssl >/dev/null 2>&1; then \
92+
openssl rand -hex 32; \
93+
fi \
94+
); \
95+
if [ -z "$$SECRET_KEY" ]; then \
96+
echo "Error: Failed to generate SECRET_KEY. Aborting."; \
97+
exit 1; \
98+
fi; \
99+
echo "SECRET_KEY=$$SECRET_KEY" >> .env
100+
101+
@echo "ALGORITHM=HS256" >> .env
102+
@echo "ACCESS_TOKEN_EXPIRE_MINUTES=30" >> .env
103+
72104
.PHONY: clean
73105
clean: ## Clean project's temporary files.
74106
find . -name '__pycache__' -exec rm -rf {} +

poetry.lock

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

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "fastapi-backend-template"
3-
version = "1.0.0"
3+
version = "2.0.0"
44
description = "A FastAPI backend template."
55
authors = ["ArielMAJ <ariel.maj@hotmail.com>"]
66
readme = "README.md"

requirements.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
alembic==1.15.2 ; python_version >= "3.12" and python_version < "4.0"
1+
alembic==1.16.1 ; python_version >= "3.12" and python_version < "4.0"
22
annotated-types==0.7.0 ; python_version >= "3.12" and python_version < "4.0"
33
anyio==4.9.0 ; python_version >= "3.12" and python_version < "4.0"
44
asyncpg==0.30.0 ; python_version >= "3.12" and python_version < "4.0"
55
bcrypt==4.3.0 ; python_version >= "3.12" and python_version < "4.0"
66
certifi==2025.4.26 ; python_version >= "3.12" and python_version < "4.0"
7-
click==8.2.0 ; python_version >= "3.12" and python_version < "4.0"
7+
click==8.2.1 ; python_version >= "3.12" and python_version < "4.0"
88
colorama==0.4.6 ; python_version >= "3.12" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
99
dnspython==2.7.0 ; python_version >= "3.12" and python_version < "4.0"
1010
email-validator==2.2.0 ; python_version >= "3.12" and python_version < "4.0"
@@ -24,20 +24,20 @@ passlib[bcrypt]==1.7.4 ; python_version >= "3.12" and python_version < "4.0"
2424
pendulum==3.1.0 ; python_version >= "3.12" and python_version < "4.0"
2525
psycopg==3.2.9 ; python_version >= "3.12" and python_version < "4.0"
2626
pydantic-core==2.33.2 ; python_version >= "3.12" and python_version < "4.0"
27-
pydantic==2.11.4 ; python_version >= "3.12" and python_version < "4.0"
28-
pydantic[email]==2.11.4 ; python_version >= "3.12" and python_version < "4.0"
27+
pydantic==2.11.5 ; python_version >= "3.12" and python_version < "4.0"
28+
pydantic[email]==2.11.5 ; python_version >= "3.12" and python_version < "4.0"
2929
pyjwt==2.10.1 ; python_version >= "3.12" and python_version < "4.0"
3030
python-dateutil==2.9.0.post0 ; python_version >= "3.12" and python_version < "4.0"
3131
python-dotenv==1.1.0 ; python_version >= "3.12" and python_version < "4.0"
3232
python-multipart==0.0.20 ; python_version >= "3.12" and python_version < "4.0"
3333
pyyaml==6.0.2 ; python_version >= "3.12" and python_version < "4.0"
3434
six==1.17.0 ; python_version >= "3.12" and python_version < "4.0"
3535
sniffio==1.3.1 ; python_version >= "3.12" and python_version < "4.0"
36-
sqlalchemy==2.0.40 ; python_version >= "3.12" and python_version < "4.0"
37-
sqlalchemy[asyncio]==2.0.40 ; python_version >= "3.12" and python_version < "4.0"
36+
sqlalchemy==2.0.41 ; python_version >= "3.12" and python_version < "4.0"
37+
sqlalchemy[asyncio]==2.0.41 ; python_version >= "3.12" and python_version < "4.0"
3838
starlette==0.46.2 ; python_version >= "3.12" and python_version < "4.0"
3939
typing-extensions==4.13.2 ; python_version >= "3.12" and python_version < "4.0"
40-
typing-inspection==0.4.0 ; python_version >= "3.12" and python_version < "4.0"
40+
typing-inspection==0.4.1 ; python_version >= "3.12" and python_version < "4.0"
4141
tzdata==2025.2 ; python_version >= "3.12" and python_version < "4.0"
4242
uvicorn==0.34.2 ; python_version >= "3.12" and python_version < "4.0"
4343
uvicorn[standard]==0.34.2 ; python_version >= "3.12" and python_version < "4.0"

src/configs/envs.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,53 @@
77
from sqlalchemy import URL, AsyncAdaptedQueuePool, NullPool
88

99

10+
def _split_method_path(method_path: str) -> tuple[str, str]:
11+
"""Split method and path from a string.
12+
This function takes a string in the format "METHOD:PATH" and splits it into
13+
a tuple containing the method and the path.
14+
For example, "GET:/api/v1/resource" will return ("GET", "/api/v1/resource").
15+
16+
Args:
17+
method_path (str): The string to split.
18+
Returns:
19+
tuple[str, str]: The method and path.
20+
Raises:
21+
ValueError: If the string is not in the correct format.
22+
"""
23+
if not method_path.strip():
24+
raise ValueError("Empty string provided")
25+
parts = method_path.split(":")
26+
if len(parts) != 2:
27+
raise ValueError(
28+
f"Invalid format: {method_path}. Expected format: 'METHOD:PATH'"
29+
f" (e.g. 'GET:/api/v1/resource')"
30+
)
31+
return tuple(parts)
32+
33+
34+
def _split_method_path_list_string(
35+
method_path: str,
36+
) -> tuple[tuple[str, str]]:
37+
"""Split a comma-separated string of method and path pairs.
38+
This function takes a string where each pair is separated by a comma,
39+
and each pair consists of a method and a path separated by a colon.
40+
For example, "GET:/api/v1/resource,POST:/api/v1/resource".
41+
It returns a tuple of tuples, where each inner tuple contains the method and path.
42+
43+
Args:
44+
method_path (str): The string to split.
45+
Returns:
46+
tuple[tuple[str, str]]: The method and path.
47+
Raises:
48+
ValueError: If the string is not in the correct format.
49+
"""
50+
if not method_path.strip():
51+
return []
52+
if not isinstance(method_path, str):
53+
raise ValueError(f"Invalid type: {type(method_path)}. Expected type: str")
54+
return tuple(_split_method_path(url.strip()) for url in method_path.split(","))
55+
56+
1057
class DatabaseConfig:
1158
"""Database configuration."""
1259

@@ -76,8 +123,20 @@ class Config:
76123
WORKERS_COUNT = int(os.getenv("WORKERS_COUNT", "1"))
77124
RELOAD = os.getenv("RELOAD", "true").lower() == "true"
78125
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
126+
LOGGER_IGNORE_PATHS = _split_method_path_list_string(
127+
os.getenv("LOGGER_IGNORE_PATHS", "GET:/docs,GET:/openapi.json")
128+
)
79129

80-
APPLICATION_ROOT = os.getenv("APPLICATION_ROOT", "")
130+
LOGGER_IGNORE_INPUT_BODY_PATHS = _split_method_path_list_string(
131+
os.getenv(
132+
"LOGGER_IGNORE_INPUT_BODY_PATHS",
133+
"POST:/user/register,PUT:/user/,POST:/auth/token",
134+
)
135+
)
136+
137+
LOGGER_IGNORE_OUTPUT_BODY_PATHS = _split_method_path_list_string(
138+
os.getenv("LOGGER_IGNORE_OUTPUT_BODY_PATHS", "POST:/auth/token")
139+
)
81140

82141
DATABASE: DatabaseConfig = DatabaseConfig()
83142
AUTH: AuthConfig = AuthConfig()

src/database/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from src.database.models import users # noqa
1+
from src.database.models import user_types, users # noqa

0 commit comments

Comments
 (0)