chore: ran both frontend and backend linting

This commit is contained in:
Anish Sarkar 2026-01-03 00:18:17 +05:30
parent 45489423d1
commit 645e849d93
21 changed files with 148 additions and 155 deletions

View file

@ -125,13 +125,17 @@ class GoogleCalendarConnector:
creds_dict["token"] creds_dict["token"]
) )
if creds_dict.get("refresh_token"): if creds_dict.get("refresh_token"):
creds_dict["refresh_token"] = token_encryption.encrypt_token( creds_dict["refresh_token"] = (
token_encryption.encrypt_token(
creds_dict["refresh_token"] creds_dict["refresh_token"]
) )
)
if creds_dict.get("client_secret"): if creds_dict.get("client_secret"):
creds_dict["client_secret"] = token_encryption.encrypt_token( creds_dict["client_secret"] = (
token_encryption.encrypt_token(
creds_dict["client_secret"] creds_dict["client_secret"]
) )
)
creds_dict["_token_encrypted"] = True creds_dict["_token_encrypted"] = True
connector.config = creds_dict connector.config = creds_dict
@ -209,9 +213,15 @@ class GoogleCalendarConnector:
try: try:
# Validate date strings # Validate date strings
if not start_date or start_date.lower() in ("undefined", "null", "none"): if not start_date or start_date.lower() in ("undefined", "null", "none"):
return [], "Invalid start_date: must be a valid date string in YYYY-MM-DD format" return (
[],
"Invalid start_date: must be a valid date string in YYYY-MM-DD format",
)
if not end_date or end_date.lower() in ("undefined", "null", "none"): if not end_date or end_date.lower() in ("undefined", "null", "none"):
return [], "Invalid end_date: must be a valid date string in YYYY-MM-DD format" return (
[],
"Invalid end_date: must be a valid date string in YYYY-MM-DD format",
)
service = await self._get_service() service = await self._get_service()

View file

@ -43,7 +43,9 @@ async def get_valid_credentials(
if not connector: if not connector:
raise ValueError(f"Connector {connector_id} not found") raise ValueError(f"Connector {connector_id} not found")
config_data = connector.config.copy() # Work with a copy to avoid modifying original config_data = (
connector.config.copy()
) # Work with a copy to avoid modifying original
# Decrypt credentials if they are encrypted # Decrypt credentials if they are encrypted
token_encrypted = config_data.get("_token_encrypted", False) token_encrypted = config_data.get("_token_encrypted", False)

View file

@ -44,9 +44,14 @@ class LinearConnector:
ValueError: If no Linear access token has been set ValueError: If no Linear access token has been set
""" """
if not self.access_token: if not self.access_token:
raise ValueError("Linear access token not initialized. Call set_token() first.") raise ValueError(
"Linear access token not initialized. Call set_token() first."
)
return {"Content-Type": "application/json", "Authorization": f"Bearer {self.access_token}"} return {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.access_token}",
}
def execute_graphql_query( def execute_graphql_query(
self, query: str, variables: dict[str, Any] | None = None self, query: str, variables: dict[str, Any] | None = None
@ -66,7 +71,9 @@ class LinearConnector:
Exception: If the API request fails Exception: If the API request fails
""" """
if not self.access_token: if not self.access_token:
raise ValueError("Linear access token not initialized. Call set_token() first.") raise ValueError(
"Linear access token not initialized. Call set_token() first."
)
headers = self.get_headers() headers = self.get_headers()
payload = {"query": query} payload = {"query": query}
@ -174,9 +181,15 @@ class LinearConnector:
""" """
# Validate date strings # Validate date strings
if not start_date or start_date.lower() in ("undefined", "null", "none"): if not start_date or start_date.lower() in ("undefined", "null", "none"):
return [], "Invalid start_date: must be a valid date string in YYYY-MM-DD format" return (
[],
"Invalid start_date: must be a valid date string in YYYY-MM-DD format",
)
if not end_date or end_date.lower() in ("undefined", "null", "none"): if not end_date or end_date.lower() in ("undefined", "null", "none"):
return [], "Invalid end_date: must be a valid date string in YYYY-MM-DD format" return (
[],
"Invalid end_date: must be a valid date string in YYYY-MM-DD format",
)
# Convert date strings to ISO format # Convert date strings to ISO format
try: try:

View file

@ -15,13 +15,13 @@ from .google_drive_add_connector_route import (
from .google_gmail_add_connector_route import ( from .google_gmail_add_connector_route import (
router as google_gmail_add_connector_router, router as google_gmail_add_connector_router,
) )
from .linear_add_connector_route import router as linear_add_connector_router
from .logs_routes import router as logs_router from .logs_routes import router as logs_router
from .luma_add_connector_route import router as luma_add_connector_router from .luma_add_connector_route import router as luma_add_connector_router
from .new_chat_routes import router as new_chat_router from .new_chat_routes import router as new_chat_router
from .linear_add_connector_route import router as linear_add_connector_router
from .notion_add_connector_route import router as notion_add_connector_router
from .new_llm_config_routes import router as new_llm_config_router from .new_llm_config_routes import router as new_llm_config_router
from .notes_routes import router as notes_router from .notes_routes import router as notes_router
from .notion_add_connector_route import router as notion_add_connector_router
from .podcasts_routes import router as podcasts_router from .podcasts_routes import router as podcasts_router
from .rbac_routes import router as rbac_router from .rbac_routes import router as rbac_router
from .search_source_connectors_routes import router as search_source_connectors_router from .search_source_connectors_routes import router as search_source_connectors_router

View file

@ -204,13 +204,9 @@ async def airtable_callback(
# Validate required parameters for successful flow # Validate required parameters for successful flow
if not code: if not code:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing authorization code")
status_code=400, detail="Missing authorization code"
)
if not state: if not state:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing state parameter")
status_code=400, detail="Missing state parameter"
)
# Validate and decode state with signature verification # Validate and decode state with signature verification
state_manager = get_state_manager() state_manager = get_state_manager()
@ -453,7 +449,9 @@ async def refresh_airtable_token(
# Update credentials object with encrypted tokens # Update credentials object with encrypted tokens
credentials.access_token = token_encryption.encrypt_token(access_token) credentials.access_token = token_encryption.encrypt_token(access_token)
if new_refresh_token: if new_refresh_token:
credentials.refresh_token = token_encryption.encrypt_token(new_refresh_token) credentials.refresh_token = token_encryption.encrypt_token(
new_refresh_token
)
credentials.expires_in = token_json.get("expires_in") credentials.expires_in = token_json.get("expires_in")
credentials.expires_at = expires_at credentials.expires_at = expires_at
credentials.scope = token_json.get("scope") credentials.scope = token_json.get("scope")

View file

@ -142,13 +142,9 @@ async def calendar_callback(
# Validate required parameters for successful flow # Validate required parameters for successful flow
if not code: if not code:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing authorization code")
status_code=400, detail="Missing authorization code"
)
if not state: if not state:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing state parameter")
status_code=400, detail="Missing state parameter"
)
# Validate and decode state with signature verification # Validate and decode state with signature verification
state_manager = get_state_manager() state_manager = get_state_manager()

View file

@ -68,6 +68,7 @@ def get_token_encryption() -> TokenEncryption:
_token_encryption = TokenEncryption(config.SECRET_KEY) _token_encryption = TokenEncryption(config.SECRET_KEY)
return _token_encryption return _token_encryption
# Google Drive OAuth scopes # Google Drive OAuth scopes
SCOPES = [ SCOPES = [
"https://www.googleapis.com/auth/drive.readonly", # Read-only access to Drive "https://www.googleapis.com/auth/drive.readonly", # Read-only access to Drive
@ -191,13 +192,9 @@ async def drive_callback(
# Validate required parameters for successful flow # Validate required parameters for successful flow
if not code: if not code:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing authorization code")
status_code=400, detail="Missing authorization code"
)
if not state: if not state:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing state parameter")
status_code=400, detail="Missing state parameter"
)
# Validate and decode state with signature verification # Validate and decode state with signature verification
state_manager = get_state_manager() state_manager = get_state_manager()

View file

@ -173,13 +173,9 @@ async def gmail_callback(
# Validate required parameters for successful flow # Validate required parameters for successful flow
if not code: if not code:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing authorization code")
status_code=400, detail="Missing authorization code"
)
if not state: if not state:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing state parameter")
status_code=400, detail="Missing state parameter"
)
# Validate and decode state with signature verification # Validate and decode state with signature verification
state_manager = get_state_manager() state_manager = get_state_manager()

View file

@ -4,7 +4,6 @@ Linear Connector OAuth Routes.
Handles OAuth 2.0 authentication flow for Linear connector. Handles OAuth 2.0 authentication flow for Linear connector.
""" """
import json
import logging import logging
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from uuid import UUID from uuid import UUID
@ -89,9 +88,7 @@ async def connect_linear(space_id: int, user: User = Depends(current_active_user
raise HTTPException(status_code=400, detail="space_id is required") raise HTTPException(status_code=400, detail="space_id is required")
if not config.LINEAR_CLIENT_ID: if not config.LINEAR_CLIENT_ID:
raise HTTPException( raise HTTPException(status_code=500, detail="Linear OAuth not configured.")
status_code=500, detail="Linear OAuth not configured."
)
if not config.SECRET_KEY: if not config.SECRET_KEY:
raise HTTPException( raise HTTPException(
@ -115,9 +112,7 @@ async def connect_linear(space_id: int, user: User = Depends(current_active_user
auth_url = f"{AUTHORIZATION_URL}?{urlencode(auth_params)}" auth_url = f"{AUTHORIZATION_URL}?{urlencode(auth_params)}"
logger.info( logger.info(f"Generated Linear OAuth URL for user {user.id}, space {space_id}")
f"Generated Linear OAuth URL for user {user.id}, space {space_id}"
)
return {"auth_url": auth_url} return {"auth_url": auth_url}
except Exception as e: except Exception as e:
@ -175,13 +170,9 @@ async def linear_callback(
# Validate required parameters for successful flow # Validate required parameters for successful flow
if not code: if not code:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing authorization code")
status_code=400, detail="Missing authorization code"
)
if not state: if not state:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing state parameter")
status_code=400, detail="Missing state parameter"
)
# Validate and decode state with signature verification # Validate and decode state with signature verification
state_manager = get_state_manager() state_manager = get_state_manager()
@ -337,4 +328,3 @@ async def linear_callback(
raise HTTPException( raise HTTPException(
status_code=500, detail=f"Failed to complete Linear OAuth: {e!s}" status_code=500, detail=f"Failed to complete Linear OAuth: {e!s}"
) from e ) from e

View file

@ -4,7 +4,6 @@ Notion Connector OAuth Routes.
Handles OAuth 2.0 authentication flow for Notion connector. Handles OAuth 2.0 authentication flow for Notion connector.
""" """
import json
import logging import logging
from uuid import UUID from uuid import UUID
@ -85,9 +84,7 @@ async def connect_notion(space_id: int, user: User = Depends(current_active_user
raise HTTPException(status_code=400, detail="space_id is required") raise HTTPException(status_code=400, detail="space_id is required")
if not config.NOTION_CLIENT_ID: if not config.NOTION_CLIENT_ID:
raise HTTPException( raise HTTPException(status_code=500, detail="Notion OAuth not configured.")
status_code=500, detail="Notion OAuth not configured."
)
if not config.SECRET_KEY: if not config.SECRET_KEY:
raise HTTPException( raise HTTPException(
@ -111,9 +108,7 @@ async def connect_notion(space_id: int, user: User = Depends(current_active_user
auth_url = f"{AUTHORIZATION_URL}?{urlencode(auth_params)}" auth_url = f"{AUTHORIZATION_URL}?{urlencode(auth_params)}"
logger.info( logger.info(f"Generated Notion OAuth URL for user {user.id}, space {space_id}")
f"Generated Notion OAuth URL for user {user.id}, space {space_id}"
)
return {"auth_url": auth_url} return {"auth_url": auth_url}
except Exception as e: except Exception as e:
@ -171,13 +166,9 @@ async def notion_callback(
# Validate required parameters for successful flow # Validate required parameters for successful flow
if not code: if not code:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing authorization code")
status_code=400, detail="Missing authorization code"
)
if not state: if not state:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing state parameter")
status_code=400, detail="Missing state parameter"
)
# Validate and decode state with signature verification # Validate and decode state with signature verification
state_manager = get_state_manager() state_manager = get_state_manager()
@ -325,4 +316,3 @@ async def notion_callback(
raise HTTPException( raise HTTPException(
status_code=500, detail=f"Failed to complete Notion OAuth: {e!s}" status_code=500, detail=f"Failed to complete Notion OAuth: {e!s}"
) from e ) from e

View file

@ -12,13 +12,13 @@ from app.routes.airtable_add_connector_route import refresh_airtable_token
from app.schemas.airtable_auth_credentials import AirtableAuthCredentialsBase from app.schemas.airtable_auth_credentials import AirtableAuthCredentialsBase
from app.services.llm_service import get_user_long_context_llm from app.services.llm_service import get_user_long_context_llm
from app.services.task_logging_service import TaskLoggingService from app.services.task_logging_service import TaskLoggingService
from app.utils.oauth_security import TokenEncryption
from app.utils.document_converters import ( from app.utils.document_converters import (
create_document_chunks, create_document_chunks,
generate_content_hash, generate_content_hash,
generate_document_summary, generate_document_summary,
generate_unique_identifier_hash, generate_unique_identifier_hash,
) )
from app.utils.oauth_security import TokenEncryption
from .base import ( from .base import (
calculate_date_range, calculate_date_range,
@ -86,7 +86,9 @@ async def index_airtable_records(
return 0, f"Connector with ID {connector_id} not found" return 0, f"Connector with ID {connector_id} not found"
# Create credentials from connector config # Create credentials from connector config
config_data = connector.config.copy() # Work with a copy to avoid modifying original config_data = (
connector.config.copy()
) # Work with a copy to avoid modifying original
# Decrypt tokens if they are encrypted (for backward compatibility) # Decrypt tokens if they are encrypted (for backward compatibility)
token_encrypted = config_data.get("_token_encrypted", False) token_encrypted = config_data.get("_token_encrypted", False)
@ -100,7 +102,9 @@ async def index_airtable_records(
config_data["access_token"] = token_encryption.decrypt_token( config_data["access_token"] = token_encryption.decrypt_token(
config_data["access_token"] config_data["access_token"]
) )
logger.info(f"Decrypted Airtable access token for connector {connector_id}") logger.info(
f"Decrypted Airtable access token for connector {connector_id}"
)
# Decrypt refresh_token if present # Decrypt refresh_token if present
if config_data.get("refresh_token"): if config_data.get("refresh_token"):
@ -108,7 +112,9 @@ async def index_airtable_records(
config_data["refresh_token"] = token_encryption.decrypt_token( config_data["refresh_token"] = token_encryption.decrypt_token(
config_data["refresh_token"] config_data["refresh_token"]
) )
logger.info(f"Decrypted Airtable refresh token for connector {connector_id}") logger.info(
f"Decrypted Airtable refresh token for connector {connector_id}"
)
except Exception as e: except Exception as e:
await task_logger.log_task_failure( await task_logger.log_task_failure(
log_entry, log_entry,

View file

@ -8,7 +8,6 @@ from google.oauth2.credentials import Credentials
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.config import config
from app.connectors.google_calendar_connector import GoogleCalendarConnector from app.connectors.google_calendar_connector import GoogleCalendarConnector
from app.db import Document, DocumentType, SearchSourceConnectorType from app.db import Document, DocumentType, SearchSourceConnectorType
from app.services.llm_service import get_user_long_context_llm from app.services.llm_service import get_user_long_context_llm

View file

@ -8,7 +8,6 @@ from google.oauth2.credentials import Credentials
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.config import config
from app.connectors.google_gmail_connector import GoogleGmailConnector from app.connectors.google_gmail_connector import GoogleGmailConnector
from app.db import ( from app.db import (
Document, Document,

View file

@ -7,7 +7,6 @@ from datetime import datetime
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.config import config
from app.connectors.linear_connector import LinearConnector from app.connectors.linear_connector import LinearConnector
from app.db import Document, DocumentType, SearchSourceConnectorType from app.db import Document, DocumentType, SearchSourceConnectorType
from app.services.llm_service import get_user_long_context_llm from app.services.llm_service import get_user_long_context_llm
@ -114,7 +113,9 @@ async def index_linear_issues(
): ):
try: try:
token_encryption = TokenEncryption(config.SECRET_KEY) token_encryption = TokenEncryption(config.SECRET_KEY)
linear_access_token = token_encryption.decrypt_token(linear_access_token) linear_access_token = token_encryption.decrypt_token(
linear_access_token
)
logger.info( logger.info(
f"Decrypted Linear access token for connector {connector_id}" f"Decrypted Linear access token for connector {connector_id}"
) )

View file

@ -107,11 +107,16 @@ async def index_notion_pages(
# Decrypt token if it's encrypted (for backward compatibility) # Decrypt token if it's encrypted (for backward compatibility)
token_encrypted = connector.config.get("_token_encrypted", False) token_encrypted = connector.config.get("_token_encrypted", False)
if token_encrypted or (config.SECRET_KEY and TokenEncryption(config.SECRET_KEY).is_encrypted(notion_token)): if token_encrypted or (
config.SECRET_KEY
and TokenEncryption(config.SECRET_KEY).is_encrypted(notion_token)
):
try: try:
token_encryption = TokenEncryption(config.SECRET_KEY) token_encryption = TokenEncryption(config.SECRET_KEY)
notion_token = token_encryption.decrypt_token(notion_token) notion_token = token_encryption.decrypt_token(notion_token)
logger.info(f"Decrypted Notion access token for connector {connector_id}") logger.info(
f"Decrypted Notion access token for connector {connector_id}"
)
except Exception as e: except Exception as e:
await task_logger.log_task_failure( await task_logger.log_task_failure(
log_entry, log_entry,

View file

@ -35,7 +35,9 @@ class OAuthStateManager:
self.secret_key = secret_key self.secret_key = secret_key
self.max_age_seconds = max_age_seconds self.max_age_seconds = max_age_seconds
def generate_secure_state(self, space_id: int, user_id: UUID, **extra_fields) -> str: def generate_secure_state(
self, space_id: int, user_id: UUID, **extra_fields
) -> str:
""" """
Generate cryptographically signed state parameter. Generate cryptographically signed state parameter.
@ -97,9 +99,7 @@ class OAuthStateManager:
# Verify signature exists # Verify signature exists
signature = data.pop("signature", None) signature = data.pop("signature", None)
if not signature: if not signature:
raise HTTPException( raise HTTPException(status_code=400, detail="Missing state signature")
status_code=400, detail="Missing state signature"
)
# Verify signature # Verify signature
payload_str = json.dumps(data, sort_keys=True) payload_str = json.dumps(data, sort_keys=True)
@ -120,9 +120,7 @@ class OAuthStateManager:
age = current_time - timestamp age = current_time - timestamp
if age < 0: if age < 0:
raise HTTPException( raise HTTPException(status_code=400, detail="Invalid state timestamp")
status_code=400, detail="Invalid state timestamp"
)
if age > self.max_age_seconds: if age > self.max_age_seconds:
raise HTTPException( raise HTTPException(
@ -147,15 +145,11 @@ class TokenEncryption:
raise ValueError("secret_key is required for token encryption") raise ValueError("secret_key is required for token encryption")
# Derive Fernet key from secret using SHA256 # Derive Fernet key from secret using SHA256
# Note: In production, consider using HKDF for key derivation # Note: In production, consider using HKDF for key derivation
key = base64.urlsafe_b64encode( key = base64.urlsafe_b64encode(hashlib.sha256(secret_key.encode()).digest())
hashlib.sha256(secret_key.encode()).digest()
)
try: try:
self.cipher = Fernet(key) self.cipher = Fernet(key)
except Exception as e: except Exception as e:
raise ValueError( raise ValueError(f"Failed to initialize encryption cipher: {e!s}") from e
f"Failed to initialize encryption cipher: {e!s}"
) from e
def encrypt_token(self, token: str) -> str: def encrypt_token(self, token: str) -> str:
""" """

View file

@ -1,13 +1,7 @@
"use client"; "use client";
import { IconBrandYoutube } from "@tabler/icons-react"; import { IconBrandYoutube } from "@tabler/icons-react";
import { import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns";
differenceInDays,
differenceInMinutes,
format,
isToday,
isYesterday,
} from "date-fns";
import { FileText, Loader2 } from "lucide-react"; import { FileText, Loader2 } from "lucide-react";
import type { FC } from "react"; import type { FC } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";

View file

@ -1,12 +1,6 @@
"use client"; "use client";
import { import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns";
differenceInDays,
differenceInMinutes,
format,
isToday,
isYesterday,
} from "date-fns";
import { ArrowRight, Cable, Loader2 } from "lucide-react"; import { ArrowRight, Cable, Loader2 } from "lucide-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import type { FC } from "react"; import type { FC } from "react";

View file

@ -110,7 +110,11 @@ const FILE_TYPE_CONFIG: Record<string, Record<string, string[]>> = {
const cardClass = "border border-border bg-slate-400/5 dark:bg-white/5"; const cardClass = "border border-border bg-slate-400/5 dark:bg-white/5";
export function DocumentUploadTab({ searchSpaceId, onSuccess, onAccordionStateChange }: DocumentUploadTabProps) { export function DocumentUploadTab({
searchSpaceId,
onSuccess,
onAccordionStateChange,
}: DocumentUploadTabProps) {
const t = useTranslations("upload_documents"); const t = useTranslations("upload_documents");
const router = useRouter(); const router = useRouter();
const [files, setFiles] = useState<File[]>([]); const [files, setFiles] = useState<File[]>([]);
@ -157,10 +161,13 @@ export function DocumentUploadTab({ searchSpaceId, onSuccess, onAccordionStateCh
const totalFileSize = files.reduce((total, file) => total + file.size, 0); const totalFileSize = files.reduce((total, file) => total + file.size, 0);
// Track accordion state changes // Track accordion state changes
const handleAccordionChange = useCallback((value: string) => { const handleAccordionChange = useCallback(
(value: string) => {
setAccordionValue(value); setAccordionValue(value);
onAccordionStateChange?.(value === "supported-file-types"); onAccordionStateChange?.(value === "supported-file-types");
}, [onAccordionStateChange]); },
[onAccordionStateChange]
);
const handleUpload = async () => { const handleUpload = async () => {
setUploadProgress(0); setUploadProgress(0);
@ -202,7 +209,9 @@ export function DocumentUploadTab({ searchSpaceId, onSuccess, onAccordionStateCh
> >
<Alert className="border border-border bg-slate-400/5 dark:bg-white/5 flex items-start gap-3 [&>svg]:relative [&>svg]:left-0 [&>svg]:top-0 [&>svg~*]:pl-0"> <Alert className="border border-border bg-slate-400/5 dark:bg-white/5 flex items-start gap-3 [&>svg]:relative [&>svg]:left-0 [&>svg]:top-0 [&>svg~*]:pl-0">
<Info className="h-4 w-4 shrink-0 mt-0.5" /> <Info className="h-4 w-4 shrink-0 mt-0.5" />
<AlertDescription className="text-xs sm:text-sm leading-relaxed pt-0.5">{t("file_size_limit")}</AlertDescription> <AlertDescription className="text-xs sm:text-sm leading-relaxed pt-0.5">
{t("file_size_limit")}
</AlertDescription>
</Alert> </Alert>
<Card className={`relative overflow-hidden ${cardClass}`}> <Card className={`relative overflow-hidden ${cardClass}`}>