Skip to content

Commit b3d6937

Browse files
authored
Feat: better landing - styling and animations (#268)
* feat: in landing, background gradient is spread across whole available space, no paddings * feat: fonts optimizations * feat: correct gradient display + update cards in home page * feat: privacy page style update + added description to meta.ts * feat: settings page update * feat: pre-commit not dangerous reformatting for frontend (prettier) and backend (ruff) added * feat: formatting and fixed issues in frontend like load times
1 parent 4912a21 commit b3d6937

78 files changed

Lines changed: 1015 additions & 849 deletions

File tree

Some content is hidden

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

.pre-commit-config.yaml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,18 @@ repos:
66
# Local hooks using uv run to match CI exactly
77
- repo: local
88
hooks:
9-
# Ruff - matches CI: cd backend && uv run ruff check . --config pyproject.toml
9+
# Ruff format - auto-fix formatting
10+
- id: ruff-format-backend
11+
name: ruff format (backend)
12+
entry: bash -c 'cd backend && uv run ruff format --config pyproject.toml .'
13+
language: system
14+
files: ^backend/.*\.py$
15+
pass_filenames: false
16+
17+
# Ruff check - auto-fix safe lint issues (import sorting, etc.)
1018
- id: ruff-backend
1119
name: ruff check (backend)
12-
entry: bash -c 'cd backend && uv run ruff check . --config pyproject.toml'
20+
entry: bash -c 'cd backend && uv run ruff check --fix --config pyproject.toml .'
1321
language: system
1422
files: ^backend/.*\.py$
1523
pass_filenames: false
@@ -46,10 +54,10 @@ repos:
4654
files: ^frontend/src/.*\.css$
4755
pass_filenames: false
4856

49-
# Prettier - matches CI: cd frontend && npx prettier --check
57+
# Prettier - auto-fix formatting
5058
- id: prettier-frontend
5159
name: prettier (frontend)
52-
entry: bash -c 'cd frontend && npx prettier --check "src/**/*.{ts,svelte,json}"'
60+
entry: bash -c 'cd frontend && npx prettier --write "src/**/*.{ts,svelte,json}"'
5361
language: system
5462
files: ^frontend/src/.*\.(ts|svelte|json)$
5563
pass_filenames: false

backend/app/api/routes/admin/executions.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ async def list_executions(
3939
skip: int = Query(0, ge=0),
4040
) -> AdminExecutionListResponse:
4141
executions, total = await service.list_executions(
42-
status=status, priority=priority, user_id=user_id, limit=limit, skip=skip,
42+
status=status,
43+
priority=priority,
44+
user_id=user_id,
45+
limit=limit,
46+
skip=skip,
4347
)
4448
return AdminExecutionListResponse(
4549
executions=[AdminExecutionResponse.model_validate(e) for e in executions],

backend/app/api/routes/admin/users.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,7 @@ async def delete_user(
144144
if admin.user_id == user_id:
145145
raise HTTPException(status_code=400, detail="Cannot delete your own account")
146146

147-
result = await admin_user_service.delete_user(
148-
admin_user_id=admin.user_id, user_id=user_id, cascade=cascade
149-
)
147+
result = await admin_user_service.delete_user(admin_user_id=admin.user_id, user_id=user_id, cascade=cascade)
150148
return DeleteUserResponse.model_validate(result)
151149

152150

backend/app/api/routes/execution.py

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232

3333
@inject
3434
async def get_execution_with_access(
35-
execution_id: Annotated[str, Path()],
36-
current_user: Annotated[User, Depends(current_user)],
37-
execution_service: FromDishka[ExecutionService],
35+
execution_id: Annotated[str, Path()],
36+
current_user: Annotated[User, Depends(current_user)],
37+
execution_service: FromDishka[ExecutionService],
3838
) -> ExecutionInDB:
3939
domain_exec = await execution_service.get_execution_result(execution_id)
4040

@@ -50,22 +50,24 @@ async def get_execution_with_access(
5050
responses={500: {"model": ErrorResponse, "description": "Script execution failed"}},
5151
)
5252
async def create_execution(
53-
request: Request,
54-
current_user: Annotated[User, Depends(current_user)],
55-
execution: ExecutionRequest,
56-
execution_service: FromDishka[ExecutionService],
57-
idempotency_key: Annotated[str | None, Header(alias="Idempotency-Key")] = None,
53+
request: Request,
54+
current_user: Annotated[User, Depends(current_user)],
55+
execution: ExecutionRequest,
56+
execution_service: FromDishka[ExecutionService],
57+
idempotency_key: Annotated[str | None, Header(alias="Idempotency-Key")] = None,
5858
) -> ExecutionResponse:
5959
"""Submit a script for execution in an isolated Kubernetes pod."""
60-
trace.get_current_span().set_attributes({
61-
"http.method": "POST",
62-
"http.route": "/api/v1/execute",
63-
"execution.language": execution.lang,
64-
"execution.language_version": execution.lang_version,
65-
"execution.script_length": len(execution.script),
66-
"user.id": current_user.user_id,
67-
"client.address": get_client_ip(request),
68-
})
60+
trace.get_current_span().set_attributes(
61+
{
62+
"http.method": "POST",
63+
"http.route": "/api/v1/execute",
64+
"execution.language": execution.lang,
65+
"execution.language_version": execution.lang_version,
66+
"execution.script_length": len(execution.script),
67+
"user.id": current_user.user_id,
68+
"client.address": get_client_ip(request),
69+
}
70+
)
6971

7072
exec_result = await execution_service.execute_script_idempotent(
7173
script=execution.script,
@@ -83,7 +85,7 @@ async def create_execution(
8385
responses={403: {"model": ErrorResponse, "description": "Not the owner of this execution"}},
8486
)
8587
async def get_result(
86-
execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
88+
execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
8789
) -> ExecutionResult:
8890
"""Retrieve the result of a specific execution."""
8991
return ExecutionResult.model_validate(execution)
@@ -98,10 +100,10 @@ async def get_result(
98100
},
99101
)
100102
async def cancel_execution(
101-
execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
102-
current_user: Annotated[User, Depends(current_user)],
103-
cancel_request: CancelExecutionRequest,
104-
execution_service: FromDishka[ExecutionService],
103+
execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
104+
current_user: Annotated[User, Depends(current_user)],
105+
cancel_request: CancelExecutionRequest,
106+
execution_service: FromDishka[ExecutionService],
105107
) -> CancelResponse:
106108
"""Cancel a running or queued execution."""
107109
result = await execution_service.cancel_execution(
@@ -122,9 +124,9 @@ async def cancel_execution(
122124
},
123125
)
124126
async def retry_execution(
125-
original_execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
126-
current_user: Annotated[User, Depends(current_user)],
127-
execution_service: FromDishka[ExecutionService],
127+
original_execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
128+
current_user: Annotated[User, Depends(current_user)],
129+
execution_service: FromDishka[ExecutionService],
128130
) -> ExecutionResponse:
129131
"""Retry a failed or completed execution."""
130132

@@ -146,10 +148,10 @@ async def retry_execution(
146148
responses={403: {"model": ErrorResponse, "description": "Not the owner of this execution"}},
147149
)
148150
async def get_execution_events(
149-
execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
150-
event_service: FromDishka[EventService],
151-
event_types: Annotated[list[EventType] | None, Query(description="Event types to filter")] = None,
152-
limit: Annotated[int, Query(ge=1, le=1000)] = 100,
151+
execution: Annotated[ExecutionInDB, Depends(get_execution_with_access)],
152+
event_service: FromDishka[EventService],
153+
event_types: Annotated[list[EventType] | None, Query(description="Event types to filter")] = None,
154+
limit: Annotated[int, Query(ge=1, le=1000)] = 100,
153155
) -> list[DomainEvent]:
154156
"""Get all events for an execution."""
155157
events = await event_service.get_events_by_execution_id(
@@ -160,14 +162,14 @@ async def get_execution_events(
160162

161163
@router.get("/user/executions", response_model=ExecutionListResponse)
162164
async def get_user_executions(
163-
current_user: Annotated[User, Depends(current_user)],
164-
execution_service: FromDishka[ExecutionService],
165-
status: Annotated[ExecutionStatus | None, Query(description="Filter by execution status")] = None,
166-
lang: Annotated[str | None, Query(description="Filter by programming language")] = None,
167-
start_time: Annotated[datetime | None, Query(description="Filter executions created after this time")] = None,
168-
end_time: Annotated[datetime | None, Query(description="Filter executions created before this time")] = None,
169-
limit: Annotated[int, Query(ge=1, le=200)] = 50,
170-
skip: Annotated[int, Query(ge=0)] = 0,
165+
current_user: Annotated[User, Depends(current_user)],
166+
execution_service: FromDishka[ExecutionService],
167+
status: Annotated[ExecutionStatus | None, Query(description="Filter by execution status")] = None,
168+
lang: Annotated[str | None, Query(description="Filter by programming language")] = None,
169+
start_time: Annotated[datetime | None, Query(description="Filter executions created after this time")] = None,
170+
end_time: Annotated[datetime | None, Query(description="Filter executions created before this time")] = None,
171+
limit: Annotated[int, Query(ge=1, le=200)] = 50,
172+
skip: Annotated[int, Query(ge=0)] = 0,
171173
) -> ExecutionListResponse:
172174
"""Get executions for the current user."""
173175

@@ -194,7 +196,7 @@ async def get_user_executions(
194196

195197
@router.get("/example-scripts", response_model=ExampleScripts)
196198
async def get_example_scripts(
197-
execution_service: FromDishka[ExecutionService],
199+
execution_service: FromDishka[ExecutionService],
198200
) -> ExampleScripts:
199201
"""Get example scripts for the code editor."""
200202
scripts = await execution_service.get_example_scripts()
@@ -203,7 +205,7 @@ async def get_example_scripts(
203205

204206
@router.get("/k8s-limits", response_model=ResourceLimits)
205207
async def get_k8s_resource_limits(
206-
execution_service: FromDishka[ExecutionService],
208+
execution_service: FromDishka[ExecutionService],
207209
) -> ResourceLimits:
208210
"""Get Kubernetes resource limits for script execution."""
209211
limits = await execution_service.get_k8s_resource_limits()
@@ -212,9 +214,9 @@ async def get_k8s_resource_limits(
212214

213215
@router.delete("/executions/{execution_id}", response_model=DeleteResponse)
214216
async def delete_execution(
215-
execution_id: str,
216-
admin: Annotated[User, Depends(admin_user)],
217-
execution_service: FromDishka[ExecutionService],
217+
execution_id: str,
218+
admin: Annotated[User, Depends(admin_user)],
219+
execution_service: FromDishka[ExecutionService],
218220
) -> DeleteResponse:
219221
"""Delete an execution and its associated data (admin only)."""
220222
await execution_service.delete_execution(execution_id, admin.user_id)

backend/app/core/dishka_lifespan.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
5050

5151
await container.get(Tracer)
5252
FastAPIInstrumentor().instrument_app(
53-
app, tracer_provider=trace.get_tracer_provider(), excluded_urls="health,metrics,docs,openapi.json",
53+
app,
54+
tracer_provider=trace.get_tracer_provider(),
55+
excluded_urls="health,metrics,docs,openapi.json",
5456
)
5557
logger.info("FastAPI OpenTelemetry instrumentation applied")
5658

backend/app/core/exceptions/handlers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,6 @@ def _map_to_status_code(exc: DomainError) -> int:
4646
if isinstance(exc, InfrastructureError):
4747
return 500
4848
return 500
49+
50+
4951
# --8<-- [end:configure_exception_handlers]

backend/app/core/logging.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ def setup_logger(log_level: str) -> structlog.stdlib.BoundLogger:
102102

103103
logger: structlog.stdlib.BoundLogger = structlog.get_logger("integr8scode")
104104
return logger
105+
106+
105107
# --8<-- [end:setup_logger]
106108

107109

@@ -122,13 +124,15 @@ def setup_log_exporter(settings: Settings, logger: structlog.stdlib.BoundLogger)
122124
if not settings.OTEL_EXPORTER_OTLP_ENDPOINT:
123125
return
124126

125-
resource = Resource.create({
126-
SERVICE_NAME: settings.SERVICE_NAME,
127-
SERVICE_VERSION: settings.SERVICE_VERSION,
128-
"service.namespace": "integr8scode",
129-
"deployment.environment": settings.ENVIRONMENT,
130-
"service.instance.id": settings.HOSTNAME,
131-
})
127+
resource = Resource.create(
128+
{
129+
SERVICE_NAME: settings.SERVICE_NAME,
130+
SERVICE_VERSION: settings.SERVICE_VERSION,
131+
"service.namespace": "integr8scode",
132+
"deployment.environment": settings.ENVIRONMENT,
133+
"service.instance.id": settings.HOSTNAME,
134+
}
135+
)
132136

133137
endpoint = settings.OTEL_EXPORTER_OTLP_ENDPOINT
134138
log_exporter = OTLPLogExporter(

backend/app/core/metrics/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def __init__(self, settings: Settings, meter_name: str | None = None):
1818
meter_name = meter_name or self.__class__.__name__
1919
self._meter = metrics.get_meter(meter_name)
2020
self._create_instruments()
21+
2122
# --8<-- [end:init]
2223

2324
def _create_instruments(self) -> None:

backend/app/core/metrics/dlq.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,3 @@ def record_dlq_processing_error(self, original_topic: str, event_type: str, erro
6565
self.dlq_processing_errors.add(
6666
1, attributes={"original_topic": original_topic, "event_type": event_type, "error_type": error_type}
6767
)
68-

backend/app/core/middlewares/metrics.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ def _get_path_template(path: str) -> str:
118118
path = re.sub(r"/[0-9a-f]{24}", "/{id}", path)
119119

120120
return path
121+
121122
# --8<-- [end:path_template]
122123

123124

@@ -208,4 +209,6 @@ def get_process_metrics(_: CallbackOptions) -> list[Observation]:
208209
meter.create_observable_gauge(
209210
name="process_metrics", callbacks=[get_process_metrics], description="Process-level metrics", unit="mixed"
210211
)
212+
213+
211214
# --8<-- [end:system_metrics]

0 commit comments

Comments
 (0)