cleanup precommit hooks

This commit is contained in:
Yogesh Ojha
2026-01-26 15:02:53 +05:30
parent b64fabc988
commit 47f4bf20c7
31 changed files with 162 additions and 133 deletions
+6 -6
View File
@@ -45,12 +45,12 @@ body:
- **Docker compose**: 2.15.1
- **Browser**: Microsoft Edge 112.0.1722.58
value: |
- reNgine:
- OS:
- Python:
- Docker Engine:
- Docker Compose:
- Browser:
- reNgine:
- OS:
- Python:
- Docker Engine:
- Docker Compose:
- Browser:
render: markdown
validations:
required: true
+1 -1
View File
@@ -1,7 +1,7 @@
# Security Policy
> **[IMPORTANT NOTICE - February 9, 2025]**
> **[IMPORTANT NOTICE - February 9, 2025]**
> reNgine is currently undergoing a major refactoring to address all XSS-related vulnerabilities. While we are committed to security, we are temporarily suspending new XSS vulnerability reports until this refactoring is complete. We will continue to accept and investigate all other types of security vulnerabilities. Thank you for your understanding and continued support in making reNgine more secure.
>
> Please note that most reported XSS vulnerabilities in reNgine affect on-premise installations with limited exploitability. Nevertheless, we are committed to fixing these issues systematically through our ongoing refactoring effort.
+13 -13
View File
@@ -23,35 +23,35 @@ jobs:
script: |
const { owner, repo } = context.repo;
const author = context.payload.sender.login;
if (context.eventName === 'issues' && context.payload.action === 'opened') {
const issueTitle = context.payload.issue.title.toLowerCase();
let commentBody;
if (issueTitle.includes('feat')) {
commentBody = `Hey @${author}! 🚀 Thanks for this exciting feature idea!
We love seeing fresh concepts that could take reNgine to the next level. 🌟
To help us understand your vision better, could you:
📝 Provide a detailed description of the feature
🎯 Explain the problem it solves or the value it adds
💡 Share any implementation ideas you might have
Your input is invaluable in shaping the future of reNgine. Let's innovate together! 💪`;
} else {
commentBody = `Hey @${author}! 👋 Thanks for flagging this bug! 🐛🔍
You're our superhero bug hunter! 🦸‍♂️🦸‍♀️ Before we suit up to squash this bug, could you please:
📚 Double-check our documentation: https://rengine.wiki
🕵️ Make sure it's not a known issue
📝 Provide all the juicy details about this sneaky bug
Once again - thanks for your vigilance! 🛠️🚀`;
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner,
@@ -74,8 +74,8 @@ jobs:
if (isPRMerged) {
commentBody = `Holy smokes! 🤯 You've just made reNgine even more awesome!
Your code is now part of the reNgine hall of fame. 🏆
Your code is now part of the reNgine hall of fame. 🏆
Keep the cool ideas coming - maybe next time you'll break the internet! 💻💥
Virtual high fives all around! 🙌`;
@@ -83,9 +83,9 @@ jobs:
commentBody = `Hey, thanks for your contribution! 🙏
We appreciate the time and effort you put into this PR. Sadly this is not the right fit for reNgine at the moment.
While we couldn't merge it this time, we value your interest in improving reNgine.
Feel free to reach out if you have any questions. Thanks again!`;
}
@@ -95,4 +95,4 @@ jobs:
repo,
body: commentBody
});
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ name: 🏗️ Build Docker image for pull request
on:
pull_request:
branches:
branches:
- master
- release/*
+1 -1
View File
@@ -55,4 +55,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-to: type=gha,mode=max
+1 -1
View File
@@ -14,4 +14,4 @@ docker-compose.override.yml
# Temporary files
*.tmp
*.temp
.cache/
.cache/
+4 -4
View File
@@ -3,12 +3,12 @@
"**/__pycache__": true,
"**/*.pyc": true,
"**/*.pyo": true,
".ruff_cache": true,
".ruff_cache": true
},
"search.exclude": {
"**/__pycache__": true,
".venv": true,
"venv": true,
".ruff_cache": true,
},
}
".ruff_cache": true
}
}
+14 -14
View File
@@ -202,7 +202,7 @@
* Fix uninitialised variable cmd in custom_subdomain_tools by @cpandya2909 in https://github.com/yogeshojha/rengine/pull/1207
* [FIX] security: OS Command Injection vulnerability (x2) #1219 by @0xtejas in https://github.com/yogeshojha/rengine/pull/1227
### New Contributors :rocket:
### New Contributors :rocket:
* @yarysp made their first contribution in https://github.com/yogeshojha/rengine/pull/1199
* @jostasik made their first contribution in https://github.com/yogeshojha/rengine/pull/1226
* @cpandya2909 made their first contribution in https://github.com/yogeshojha/rengine/pull/1207
@@ -278,19 +278,19 @@
* fix(tool_arsenal): incorrect regex version numbers by @AnonymousWP in https://github.com/yogeshojha/rengine/pull/1086
### New Contributors
* @luizmlo made their first contribution in https://github.com/yogeshojha/rengine/pull/1029 :partying_face:
* @aqhmal made their first contribution in https://github.com/yogeshojha/rengine/pull/1021 :partying_face:
* @C0wnuts made their first contribution in https://github.com/yogeshojha/rengine/pull/973 :partying_face:
* @ocervell made their first contribution in https://github.com/yogeshojha/rengine/pull/1058 :partying_face:
* @Vijayragha1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1074 :partying_face:
* @jxdv made their first contribution in https://github.com/yogeshojha/rengine/pull/1081 :partying_face:
* @SeanOverton made their first contribution in https://github.com/yogeshojha/rengine/pull/1106 :partying_face:
* @SubGlitch1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1107 :partying_face:
* @Linuxinet made their first contribution in https://github.com/yogeshojha/rengine/pull/1112 :partying_face:
* @luizmlo made their first contribution in https://github.com/yogeshojha/rengine/pull/1029 :partying_face:
* @aqhmal made their first contribution in https://github.com/yogeshojha/rengine/pull/1021 :partying_face:
* @C0wnuts made their first contribution in https://github.com/yogeshojha/rengine/pull/973 :partying_face:
* @ocervell made their first contribution in https://github.com/yogeshojha/rengine/pull/1058 :partying_face:
* @Vijayragha1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1074 :partying_face:
* @jxdv made their first contribution in https://github.com/yogeshojha/rengine/pull/1081 :partying_face:
* @SeanOverton made their first contribution in https://github.com/yogeshojha/rengine/pull/1106 :partying_face:
* @SubGlitch1 made their first contribution in https://github.com/yogeshojha/rengine/pull/1107 :partying_face:
* @Linuxinet made their first contribution in https://github.com/yogeshojha/rengine/pull/1112 :partying_face:
**Full Changelog**: https://github.com/yogeshojha/rengine/compare/v2.0.1...v2.0.2
Once again excellent work on reNgine v2.0.2 by @AnonymousWP, @psyray, @ocervell and everybody else! :rocket:
Once again excellent work on reNgine v2.0.2 by @AnonymousWP, @psyray, @ocervell and everybody else! :rocket:
## 2.0.1
@@ -299,7 +299,7 @@ Once again excellent work on reNgine v2.0.2 by @AnonymousWP, @psyray, @ocervell
2.0.1 fixes a ton of issues in reNgine 2.0.
Fixes:
Fixes:
1. Prevent duplicating Nuclei vulns for subdomain #1012 @psyray
2. Fixes for empty subdomain returned during nuclei scan #1011 @psyray
3. Add all the missing slug in scanEngine view & other places #1005 @psyray
@@ -314,13 +314,13 @@ Fixes:
11. Add stack trace into make logs if DEBUG True #994 @psyray
12. Fix dirfuzz base64 name display #993 #992 @psyray
13. Fix target subdomains list not loading #991 @psyray
14. Change WORDLIST constant value #987, fixes #986@psyray
14. Change WORDLIST constant value #987, fixes #986@psyray
15. fix(notification_settings): submitting results in error 502 #981 fixes #970 @psyray
16. Fixes with documentation and installation/update/uninstall scripts @anonymousWP
17. Fix file directory popup not showing in detailed scan #912 @psyray
@AnonymousWP and @psyray have been phenomenal in fixing these bugs. Thanks to both of you! :heart: :rocket:
@AnonymousWP and @psyray have been phenomenal in fixing these bugs. Thanks to both of you! :heart: :rocket:
## 2.0.0
+1 -1
View File
@@ -1 +1 @@
# rengine 3.0
# rengine 3.0
+1 -1
View File
@@ -1 +1 @@
3.0.0
3.0.0
+1 -1
View File
@@ -126,4 +126,4 @@ dmypy.json
.DS_Store
# FastAPI specific
.uvicorn_cache/
.uvicorn_cache/
+1 -1
View File
@@ -15,4 +15,4 @@ RUN uv sync --no-install-project
# Expose port
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
+5 -6
View File
@@ -1,16 +1,15 @@
from typing import Annotated
from uuid import UUID
from fastapi import Depends, HTTPException, status, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi import Depends, HTTPException, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select
from app.core.security import decode_token
from app.core.database import get_session
from app.core.security import decode_token
from app.models.user import User
security = HTTPBearer(auto_error=False)
@@ -70,12 +69,12 @@ async def get_current_user(
try:
user_id = UUID(user_id_str)
except ValueError:
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid user ID in token",
headers={"WWW-Authenticate": "Bearer"},
)
) from e
# Fetch user
result = await session.execute(select(User).where(User.id == user_id))
+1
View File
@@ -1,4 +1,5 @@
from fastapi import APIRouter
from app.api.v1 import auth, users
router = APIRouter()
+9 -10
View File
@@ -1,23 +1,22 @@
from typing import Annotated
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status, Response, Request
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select
from app.api.deps import CurrentSuperuser, CurrentUser
from app.config import settings
from app.core.database import get_session
from app.core.security import (
verify_password,
hash_password,
create_access_token,
create_refresh_token,
decode_token,
hash_password,
verify_password,
)
from app.config import settings
from app.core.database import get_session
from app.models.user import User, UserCreate, UserRead
from app.schemas.auth import LoginRequest, TokenResponse
from app.api.deps import CurrentUser, CurrentSuperuser
router = APIRouter(prefix="/auth", tags=["authentication"])
@@ -127,12 +126,12 @@ async def refresh_access_token(
try:
user_id = UUID(user_id_str)
except ValueError:
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid user ID in token",
headers={"WWW-Authenticate": "Bearer"},
)
) from e
result = await session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
@@ -180,7 +179,7 @@ async def get_current_user_info(current_user: CurrentUser):
async def register_user(
user_in: UserCreate,
session: Annotated[AsyncSession, Depends(get_session)],
current_user: CurrentSuperuser,
current_user: CurrentSuperuser, # noqa: ARG001
):
"""
Register a new user. **Admin only**.
+9 -13
View File
@@ -1,13 +1,13 @@
from typing import Annotated
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select
from app.api.deps import CurrentSuperuser
from app.core.database import get_session
from app.models.user import User, UserRead
from app.api.deps import CurrentSuperuser
router = APIRouter(prefix="/users", tags=["users"])
@@ -15,7 +15,7 @@ router = APIRouter(prefix="/users", tags=["users"])
@router.get("/", response_model=list[UserRead])
async def list_users(
session: Annotated[AsyncSession, Depends(get_session)],
current_user: CurrentSuperuser, # Only superusers can list all users
current_user: CurrentSuperuser, # noqa: ARG001
skip: int = 0,
limit: int = 100,
):
@@ -26,29 +26,26 @@ async def list_users(
# Get paginated users
query = select(User).offset(skip).limit(limit)
result = await session.execute(query)
users = result.scalars().all()
return users
return result.scalars().all()
@router.get("/{user_id}", response_model=UserRead)
async def get_user(
user_id: str,
session: Annotated[AsyncSession, Depends(get_session)],
current_user: CurrentSuperuser, # superuser only
current_user: CurrentSuperuser, # noqa: ARG001
):
"""
Get user by ID. **Admin only**.
"""
from uuid import UUID
try:
uuid_id = UUID(user_id)
except ValueError:
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid user ID format",
)
) from e
result = await session.execute(select(User).where(User.id == uuid_id))
user = result.scalar_one_or_none()
@@ -71,15 +68,14 @@ async def delete_user(
"""
Delete user by ID. **Admin only**.
"""
from uuid import UUID
try:
uuid_id = UUID(user_id)
except ValueError:
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid user ID format",
)
) from e
# Prevent self-deletion
if uuid_id == current_user.id:
+31 -3
View File
@@ -1,6 +1,8 @@
from pydantic_settings import BaseSettings
from functools import lru_cache
from pydantic import field_validator
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
################################################################
@@ -29,7 +31,7 @@ class Settings(BaseSettings):
###############################################################
# Auth configs
###############################################################
SECRET_KEY: str = "change-me-in-production-use-openssl-rand-hex-32"
SECRET_KEY: str = "change-me-in-production-use-openssl-rand-hex-32" # noqa: S105
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
@@ -39,7 +41,33 @@ class Settings(BaseSettings):
###############################################################
ADMIN_EMAIL: str = "admin@rengine.local"
ADMIN_USERNAME: str = "rengine"
ADMIN_PASSWORD: str = "rengine@123"
ADMIN_PASSWORD: str = "rengine@123" # noqa: S105
@field_validator("SECRET_KEY")
@classmethod
def validate_secret_key(cls, v: str, info) -> str:
"""Ensure SECRET_KEY is not using default value in production."""
if (
not info.data.get("DEBUG", False)
and v == "change-me-in-production-use-openssl-rand-hex-32"
):
return_error = (
"SECRET_KEY must be set in production. "
"Generate one with: openssl rand -hex 32"
)
raise ValueError(return_error)
return v
@field_validator("ADMIN_PASSWORD")
@classmethod
def validate_admin_password(cls, v: str, info) -> str:
"""Ensure ADMIN_PASSWORD is not using default value in production."""
if not info.data.get("DEBUG", False) and v == "rengine@123":
return_error = (
"ADMIN_PASSWORD must be changed from default value in production"
)
raise ValueError(return_error)
return v
class Config:
env_file = ".env"
+2 -1
View File
@@ -1,5 +1,6 @@
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlmodel import SQLModel
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from app.config import settings
engine = create_async_engine(
+9
View File
@@ -0,0 +1,9 @@
from shared.logging import setup_logging
from app.config import settings
logger = setup_logging(
name="rengine.backend",
level=settings.LOG_LEVEL if hasattr(settings, "LOG_LEVEL") else "INFO",
colored=True,
)
+6 -8
View File
@@ -1,13 +1,12 @@
from datetime import datetime, timedelta, timezone
from datetime import UTC, datetime, timedelta
from typing import Any
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
from jose import jwt, JWTError
from jose import JWTError, jwt
from app.config import settings
ph = PasswordHasher(
time_cost=2,
memory_cost=65536,
@@ -54,7 +53,7 @@ def create_token(
expires_delta: timedelta,
) -> str:
"""Create a JWT token with the given subject and expiration."""
expire = datetime.now(timezone.utc) + expires_delta
expire = datetime.now(UTC) + expires_delta
to_encode = {
"exp": expire,
"sub": str(subject),
@@ -67,7 +66,7 @@ def create_access_token(subject: str | Any) -> str:
"""Create an access token for the given subject."""
return create_token(
subject=subject,
token_type="access",
token_type="access", # noqa: S106
expires_delta=timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES),
)
@@ -76,7 +75,7 @@ def create_refresh_token(subject: str | Any) -> str:
"""Create a refresh token for the given subject."""
return create_token(
subject=subject,
token_type="refresh",
token_type="refresh", # noqa: S106
expires_delta=timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS),
)
@@ -89,11 +88,10 @@ def decode_token(token: str) -> dict | None:
Decoded payload dict if valid, None otherwise
"""
try:
payload = jwt.decode(
return jwt.decode(
token,
settings.SECRET_KEY,
algorithms=[settings.ALGORITHM],
)
return payload
except JWTError:
return None
+7 -12
View File
@@ -1,22 +1,17 @@
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.config import settings
from app.utils.helpers import create_initial_admin
from app.core.database import init_db
from app.api.router import router as api_router
from shared.logging import setup_logging, get_logger
logger = setup_logging(
name="rengine.backend",
level=settings.LOG_LEVEL if hasattr(settings, "LOG_LEVEL") else "INFO",
colored=True,
)
from app.config import settings
from app.core.database import init_db
from app.core.logging import logger
from app.utils.helpers import create_initial_admin
@asynccontextmanager
async def lifespan(app: FastAPI):
async def lifespan(_app: FastAPI):
# app starts up
logger.info("Starting Backend...")
try:
+2 -1
View File
@@ -1,5 +1,6 @@
import uuid
from sqlmodel import SQLModel, Field
from sqlmodel import Field, SQLModel
class ProjectBase(SQLModel):
+13 -13
View File
@@ -1,16 +1,13 @@
import uuid
from datetime import datetime, timezone
from typing import Optional
import uuid as uuid_pkg
from datetime import UTC, datetime
from pydantic import field_validator, ValidationInfo
from sqlalchemy.dialects.postgresql import UUID
from sqlmodel import SQLModel, Field
from pydantic import ValidationInfo, field_validator
from sqlmodel import Field, SQLModel
from zxcvbn import zxcvbn
from app.config import settings
# Password policy
MIN_PASSWORD_SCORE = 3
MIN_PASSWORD_LENGTH = 10
@@ -31,9 +28,9 @@ class User(UserBase, table=True):
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
hashed_password: str
created_at: datetime = Field(
default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
default_factory=lambda: datetime.now(UTC).replace(tzinfo=None)
)
updated_at: Optional[datetime] = Field(default=None)
updated_at: datetime | None = Field(default=None)
class UserCreate(SQLModel):
@@ -49,9 +46,10 @@ class UserCreate(SQLModel):
return password
if len(password) < MIN_PASSWORD_LENGTH:
raise ValueError(
return_error = (
f"Password must be at least {MIN_PASSWORD_LENGTH} characters long"
)
raise ValueError(return_error)
result = zxcvbn(
password,
@@ -59,13 +57,15 @@ class UserCreate(SQLModel):
)
if result["score"] < MIN_PASSWORD_SCORE:
raise ValueError(
return_error = (
"Password is too weak. Consider using a stronger password with a "
"mix of uppercase, lowercase, numbers, and special characters."
)
raise ValueError(return_error)
if result["guesses_log10"] < 3:
raise ValueError("This password is too common or easily guessable.")
if result["guesses_log10"] < MIN_PASSWORD_SCORE:
return_error = "Password is too guessable. Choose a less common password."
raise ValueError(return_error)
return password
@@ -73,4 +73,4 @@ class UserCreate(SQLModel):
class UserRead(UserBase):
id: uuid_pkg.UUID
created_at: datetime
updated_at: Optional[datetime] = None
updated_at: datetime | None = None
+6 -4
View File
@@ -1,8 +1,10 @@
from sqlmodel import select
from app.core.database import async_db_session
from app.models.user import User
from app.core.security import hash_password
from app.config import settings
from app.core.database import async_db_session
from app.core.logger import logger
from app.core.security import hash_password
from app.models.user import User
async def create_initial_admin() -> None:
@@ -20,4 +22,4 @@ async def create_initial_admin() -> None:
)
session.add(admin)
await session.commit()
print(f"Initial admin created: {settings.ADMIN_USERNAME}")
logger.info(f"Initial admin created: {settings.ADMIN_USERNAME}")
+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

+1 -1
View File
@@ -1 +1 @@
export { default as Navbar } from './navbar.svelte';
export { default as Navbar } from './navbar.svelte';
+1 -1
View File
@@ -1,3 +1,3 @@
export * from './button';
export * from './input';
export * from './card';
export * from './card';
+1 -1
View File
@@ -118,4 +118,4 @@
body {
@apply bg-background text-foreground;
}
}
}
+1 -1
View File
@@ -15,4 +15,4 @@ const config = {
}
};
export default config;
export default config;
+1 -1
View File
@@ -126,4 +126,4 @@ dmypy.json
.DS_Store
# FastAPI specific
.uvicorn_cache/
.uvicorn_cache/
+11 -11
View File
@@ -21,11 +21,11 @@ class ColoredFormatter(logging.Formatter):
'CRITICAL': '\033[35m',
'RESET': '\033[0m',
}
def format(self, record: logging.LogRecord) -> str:
color = self.COLORS.get(record.levelname, self.COLORS['RESET'])
reset = self.COLORS['RESET']
record.levelname = f"{color}{record.levelname}{reset}"
record.levelname = f"{color}{record.levelname}{reset}"
return super().format(record)
@@ -36,24 +36,24 @@ def setup_logging(
) -> logging.Logger:
"""
Set up logging configuration.
Args:
name: Logger name (e.g., "rengine.backend", "rengine.worker")
level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
colored: Whether to use colored output for console
Returns:
Configured logger instance
"""
logger = logging.getLogger(name)
logger.setLevel(getattr(logging, level.upper()))
logger.handlers.clear()
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.DEBUG)
if colored:
console_format = ColoredFormatter(
fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
@@ -64,12 +64,12 @@ def setup_logging(
fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
console_handler.setFormatter(console_format)
logger.addHandler(console_handler)
return logger
def get_logger(name: str) -> logging.Logger:
return logging.getLogger(name)
return logging.getLogger(name)