Skip to content

Commit e222e35

Browse files
committed
deployed app to google cloud run
1 parent 253ef83 commit e222e35

3 files changed

Lines changed: 299 additions & 0 deletions

File tree

.dockerignore

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
2+
.git/
3+
4+
tests/
5+
6+
.python-version
7+
8+
# Created by https://www.toptal.com/developers/gitignore/api/python,macos,visualstudiocode,dotenv
9+
# Edit at https://www.toptal.com/developers/gitignore?templates=python,macos,visualstudiocode,dotenv
10+
11+
### dotenv ###
12+
.env
13+
14+
### Database Files ###
15+
*.sqlite3
16+
*.db
17+
18+
### macOS ###
19+
# General
20+
.DS_Store
21+
.AppleDouble
22+
.LSOverride
23+
24+
# Icon must end with two \r
25+
Icon
26+
27+
28+
# Thumbnails
29+
._*
30+
31+
# Files that might appear in the root of a volume
32+
.DocumentRevisions-V100
33+
.fseventsd
34+
.Spotlight-V100
35+
.TemporaryItems
36+
.Trashes
37+
.VolumeIcon.icns
38+
.com.apple.timemachine.donotpresent
39+
40+
# Directories potentially created on remote AFP share
41+
.AppleDB
42+
.AppleDesktop
43+
Network Trash Folder
44+
Temporary Items
45+
.apdisk
46+
47+
### macOS Patch ###
48+
# iCloud generated files
49+
*.icloud
50+
51+
### Python ###
52+
# Byte-compiled / optimized / DLL files
53+
__pycache__/
54+
*.py[cod]
55+
*$py.class
56+
57+
# C extensions
58+
*.so
59+
60+
# Distribution / packaging
61+
.Python
62+
build/
63+
develop-eggs/
64+
dist/
65+
downloads/
66+
eggs/
67+
.eggs/
68+
lib/
69+
lib64/
70+
parts/
71+
sdist/
72+
var/
73+
wheels/
74+
share/python-wheels/
75+
*.egg-info/
76+
.installed.cfg
77+
*.egg
78+
MANIFEST
79+
80+
# PyInstaller
81+
# Usually these files are written by a python script from a template
82+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
83+
*.manifest
84+
*.spec
85+
86+
# Installer logs
87+
pip-log.txt
88+
pip-delete-this-directory.txt
89+
90+
# Unit test / coverage reports
91+
htmlcov/
92+
.tox/
93+
.nox/
94+
.coverage
95+
.coverage.*
96+
.cache
97+
nosetests.xml
98+
coverage.xml
99+
*.cover
100+
*.py,cover
101+
.hypothesis/
102+
.pytest_cache/
103+
cover/
104+
105+
# Translations
106+
*.mo
107+
*.pot
108+
109+
# Django stuff:
110+
*.log
111+
local_settings.py
112+
db.sqlite3
113+
db.sqlite3-journal
114+
115+
# Flask stuff:
116+
instance/
117+
.webassets-cache
118+
119+
# Scrapy stuff:
120+
.scrapy
121+
122+
# Sphinx documentation
123+
docs/_build/
124+
125+
# PyBuilder
126+
.pybuilder/
127+
target/
128+
129+
# Jupyter Notebook
130+
.ipynb_checkpoints
131+
132+
# IPython
133+
profile_default/
134+
ipython_config.py
135+
136+
# pyenv
137+
# For a library or package, you might want to ignore these files since the code is
138+
# intended to run in multiple environments; otherwise, check them in:
139+
# .python-version
140+
141+
# pipenv
142+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
143+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
144+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
145+
# install all needed dependencies.
146+
#Pipfile.lock
147+
148+
# poetry
149+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
150+
# This is especially recommended for binary packages to ensure reproducibility, and is more
151+
# commonly ignored for libraries.
152+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
153+
#poetry.lock
154+
155+
# pdm
156+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
157+
#pdm.lock
158+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
159+
# in version control.
160+
# https://pdm.fming.dev/#use-with-ide
161+
.pdm.toml
162+
163+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
164+
__pypackages__/
165+
166+
# Celery stuff
167+
celerybeat-schedule
168+
celerybeat.pid
169+
170+
# SageMath parsed files
171+
*.sage.py
172+
173+
# Environments
174+
.venv
175+
env/
176+
venv/
177+
ENV/
178+
env.bak/
179+
venv.bak/
180+
181+
# Spyder project settings
182+
.spyderproject
183+
.spyproject
184+
185+
# Rope project settings
186+
.ropeproject
187+
188+
# mkdocs documentation
189+
/site
190+
191+
# mypy
192+
.mypy_cache/
193+
.dmypy.json
194+
dmypy.json
195+
196+
# Pyre type checker
197+
.pyre/
198+
199+
# pytype static type analyzer
200+
.pytype/
201+
202+
# Cython debug symbols
203+
cython_debug/
204+
205+
# PyCharm
206+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
207+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
208+
# and can be added to the global gitignore or merged into this file. For a more nuclear
209+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
210+
#.idea/
211+
212+
### Python Patch ###
213+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
214+
poetry.toml
215+
216+
# ruff
217+
.ruff_cache/
218+
219+
# LSP config files
220+
pyrightconfig.json
221+
222+
### VisualStudioCode ###
223+
.vscode/*
224+
!.vscode/settings.json
225+
!.vscode/tasks.json
226+
!.vscode/launch.json
227+
!.vscode/extensions.json
228+
!.vscode/*.code-snippets
229+
230+
# Local History for Visual Studio Code
231+
.history/
232+
233+
# Built Visual Studio Code Extensions
234+
*.vsix
235+
236+
### VisualStudioCode Patch ###
237+
# Ignore all local history of files
238+
.history
239+
.ionide
240+
241+
# End of https://www.toptal.com/developers/gitignore/api/python,macos,visualstudiocode,dotenv

Dockerfile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# BUILD STAGE
2+
FROM python:3.14.4-slim-bookworm AS builder
3+
4+
# Copy UV binary from official image
5+
COPY --from=ghcr.io/astral-sh/uv:0.11.6 /uv /uvx /bin/
6+
7+
WORKDIR /app
8+
9+
# UV Docker optimizations
10+
ENV UV_COMPILE_BYTECODE=1
11+
ENV UV_LINK_MODE=copy
12+
ENV UV_PYTHON_DOWNLOADS=0
13+
14+
# Install dependencies first (cached if unchanged)
15+
COPY pyproject.toml uv.lock ./
16+
RUN uv sync --locked --no-install-project --no-dev
17+
18+
# Copy app code and install project
19+
COPY . ./
20+
RUN uv sync --locked --no-dev
21+
22+
# PRODUCTION STAGE
23+
FROM python:3.14.4-slim-bookworm
24+
25+
WORKDIR /app
26+
27+
# Run as non-root user for security
28+
RUN useradd -m appuser && chown -R appuser:appuser /app
29+
USER appuser
30+
31+
# Copy app and dependencies from builder stage
32+
COPY --from=builder --chown=appuser:appuser /app /app
33+
34+
ENV PATH="/app/.venv/bin:$PATH"
35+
ENV PYTHONUNBUFFERED=1
36+
ENV PORT=8080
37+
38+
# exec replaces shell so fastapi receives SIGTERM for clean shutdown
39+
CMD ["/bin/sh", "-c", "exec fastapi run --host 0.0.0.0 --port \"$PORT\" --proxy-headers --forwarded-allow-ips '*'"]

main.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from routers import users, posts
2020

2121

22+
2223
@asynccontextmanager
2324
async def lifespan(_app: FastAPI):
2425
yield
@@ -35,6 +36,24 @@ async def lifespan(_app: FastAPI):
3536
app.include_router(users.router, prefix="/api/users", tags=["users"])
3637
app.include_router(posts.router, prefix="/api/posts", tags=["posts"])
3738

39+
@app.middleware("http")
40+
async def add_security_headers(request: Request, call_next):
41+
response = await call_next(request)
42+
43+
response.headers["X-Frame-Options"] = "SAMEORIGIN"
44+
45+
response.headers["X-Content-Type-Options"] = "nosniff"
46+
47+
if "Referrer-Policy" not in response.headers:
48+
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
49+
50+
if request.url.hostname not in ("localhost", "127.0.0.1"):
51+
response.headers["Strict-Transport-Security"] = (
52+
"max-age=63072000; includeSubDomains"
53+
)
54+
55+
return response
56+
3857
@app.get("/health")
3958
async def health_check(db: Annotated[AsyncSession, Depends(get_db)]):
4059
try:

0 commit comments

Comments
 (0)