Commit 118c3180 authored by Hoanganhvu123's avatar Hoanganhvu123

update

parent 1732c42d
VITE_CLERK_PUBLISHABLE_KEY=pk_test_Y29tbXVuYWwtc3VuYmVhbS0wLmNsZXJrLmFjY291bnRzLmRldiQ
CLERK_SECRET_KEY=sk_test_ek7ozVR80Qi9UdvhGaTmlXovS16GDuBDlDrpH1rkyQ
\ No newline at end of file
......@@ -120,8 +120,8 @@ async def get_user_openai_key(
@router.put("/{user_id}/openai-key", summary="Update user's OpenAI API key")
async def update_user_openai_key(
user_id: str,
payload: dict = Body(...),
request: Request,
payload: dict = Body(...),
settings_service=Depends(get_user_settings_service),
):
"""
......
......@@ -39,7 +39,28 @@ class AuthService:
async def get_me(self, token: str | None = None) -> schemas.AuthMeResponse:
if not token:
raise ValueError("Missing authentication token")
return schemas.AuthMeResponse(id="1", email="demo@example.com")
# Verify Clerk JWT token
import logging
try:
from config import CLERK_JWKS_URL, CLERK_ISSUER
# Only verify if Clerk is configured
if CLERK_JWKS_URL and CLERK_ISSUER:
from common.clerk_auth import verify_clerk_jwt
payload = verify_clerk_jwt(token)
# Extract user info from Clerk token
user_id = payload.get("sub") or payload.get("user_id") or "1"
email = payload.get("email") or "demo@example.com"
logging.info(f"✅ Clerk token verified for user: {user_id}")
return schemas.AuthMeResponse(id=user_id, email=email)
else:
# Clerk not configured - accept any token (dev mode)
logging.warning("⚠️ Clerk not configured, accepting token without verification")
return schemas.AuthMeResponse(id="1", email="demo@example.com")
except Exception as e:
# If Clerk verification fails, log and raise
logging.error(f"❌ Clerk token verification failed: {e}")
raise ValueError(f"Invalid authentication token: {str(e)}") from e
class UserService:
......
......@@ -3,14 +3,15 @@ set -e
echo "🚀 Starting CuCu Note Backend..."
# Wait for MongoDB to be ready
# Try to connect to MongoDB (non-blocking, app will retry on startup)
if [ -n "$MONGODB_URI" ]; then
echo "⏳ Waiting for MongoDB..."
until python -c "import motor.motor_asyncio; import asyncio; asyncio.run(motor.motor_asyncio.AsyncIOMotorClient('$MONGODB_URI').admin.command('ping'))" 2>/dev/null; do
echo "MongoDB is unavailable - sleeping"
sleep 2
done
echo "✅ MongoDB is ready!"
echo "⏳ Testing MongoDB connection..."
if python -c "import motor.motor_asyncio; import asyncio; asyncio.run(motor.motor_asyncio.AsyncIOMotorClient('$MONGODB_URI', serverSelectionTimeoutMS=3000).admin.command('ping'))" 2>/dev/null; then
echo "✅ MongoDB connection successful!"
else
echo "⚠️ MongoDB connection failed. App will start and retry on first request."
echo "⚠️ Please ensure Network Access in MongoDB Atlas allows your IP (or 0.0.0.0/0 for testing)."
fi
fi
# Wait for Redis to be ready (if configured)
......@@ -23,23 +24,33 @@ if [ -n "$REDIS_HOST" ]; then
echo "✅ Redis is ready!"
fi
# Run database migrations/indexes (if needed)
# Run database migrations/indexes (if needed) - skip if MongoDB not available
echo "📊 Setting up database indexes..."
python -c "
import asyncio
import sys
from common.mongo_client import mongodb_client
async def setup():
await mongodb_client.connect()
print('✅ Database indexes ready')
try:
await mongodb_client.connect()
print('✅ Database indexes ready')
except Exception as e:
print(f'⚠️ Could not set up indexes: {e}')
sys.exit(0) # Don't fail startup
asyncio.run(setup())
" || echo "⚠️ Could not set up indexes (may already exist)"
" || echo "⚠️ Could not set up indexes (will retry on first request)"
# Start the server
echo "🌟 Starting Gunicorn server..."
# Allow overriding number of workers via env, default to 1 for simplicity
WORKERS="${GUNICORN_WORKERS:-1}"
echo "🔧 Using Gunicorn workers: $WORKERS"
exec gunicorn \
--workers 4 \
--workers "$WORKERS" \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:5000 \
--timeout 120 \
......
......@@ -135,4 +135,3 @@ zope.event==6.1
zope.interface==8.1.1
zstandard==0.25.0
gunicorn==23.0.0
copilotkit
version: '3.8'
services:
# MongoDB Database
mongodb:
image: mongo:7.0
container_name: cuccu_mongodb
restart: unless-stopped
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
- mongodb_config:/data/configdb
environment:
MONGO_INITDB_DATABASE: cucu_note
networks:
- cuccu_network
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
# Redis Cache
redis:
image: redis:7-alpine
container_name: cuccu_redis
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
networks:
- cuccu_network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# Backend API
backend:
build:
......@@ -50,14 +12,13 @@ services:
- "5000:5000"
env_file:
- ./backend/.env
environment:
# MongoDB connection string from Atlas
MONGODB_URI: "mongodb+srv://20010841:vuhoanganh1704@cluster0.h6qro.mongodb.net/cucu_note?retryWrites=true&w=majority&appName=Cluster0"
MONGODB_DB_NAME: "cucu_note"
volumes:
- ./backend:/app
- backend_data:/app/data
depends_on:
mongodb:
condition: service_healthy
redis:
condition: service_healthy
networks:
- cuccu_network
healthcheck:
......@@ -80,6 +41,11 @@ services:
build:
context: ./frontend
dockerfile: Dockerfile.prod
args:
# Build-time envs for Vite
# Browser (người dùng) gọi trực tiếp vào host, nên dùng localhost:5000
VITE_API_BASE_URL: "http://localhost:5000"
VITE_CLERK_PUBLISHABLE_KEY: ${VITE_CLERK_PUBLISHABLE_KEY}
container_name: cuccu_frontend
restart: unless-stopped
ports:
......@@ -97,12 +63,6 @@ services:
retries: 3
volumes:
mongodb_data:
driver: local
mongodb_config:
driver: local
redis_data:
driver: local
backend_data:
driver: local
......
......@@ -20,10 +20,6 @@ build/
*.swp
*.swo
# Environment
.env
.env.local
# Testing
tests/
coverage/
......
......@@ -7,9 +7,10 @@ WORKDIR /app
COPY package*.json ./
COPY pnpm-lock.yaml* ./
# Install pnpm if pnpm-lock.yaml exists, otherwise use npm
# Install dependencies
# Use pnpm if lockfile exists, but allow it to update (no --frozen-lockfile to avoid ERR_PNPM_OUTDATED_LOCKFILE)
RUN if [ -f pnpm-lock.yaml ]; then \
npm install -g pnpm && pnpm install --frozen-lockfile; \
npm install -g pnpm && pnpm install --no-frozen-lockfile; \
else \
npm ci; \
fi
......@@ -21,6 +22,10 @@ COPY . .
ARG VITE_API_BASE_URL=http://localhost:5000
ENV VITE_API_BASE_URL=$VITE_API_BASE_URL
# Clerk publishable key for frontend (must start with VITE_ to be exposed)
ARG VITE_CLERK_PUBLISHABLE_KEY
ENV VITE_CLERK_PUBLISHABLE_KEY=$VITE_CLERK_PUBLISHABLE_KEY
RUN if [ -f pnpm-lock.yaml ]; then \
pnpm build; \
else \
......
VITE_CLERK_PUBLISHABLE_KEY=pk_test_Y29tbXVuYWwtc3VuYmVhbS0wLmNsZXJrLmFjY291bnRzLmRldiQ
CLERK_SECRET_KEY=sk_test_ek7ozVR80Qi9UdvhGaTmlXovS16GDuBDlDrpH1rkyQ
\ No newline at end of file
......@@ -54,6 +54,24 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const initialize = useCallback(async () => {
setState((prev) => ({ ...prev, isLoading: true }));
try {
// Check if we have a token/session before making API call
// This prevents unnecessary 401 errors when user is not logged in
const hasToken = localStorage.getItem("accessToken") || sessionStorage.getItem("accessToken");
if (!hasToken) {
// No token means user is not logged in - skip API call
clearAccessToken();
setState({
currentUser: undefined,
userGeneralSetting: undefined,
userWebhooksSetting: undefined,
shortcuts: [],
isInitialized: true,
isLoading: false,
});
return;
}
const { user: currentUser } = await authServiceClient.getCurrentUser({});
if (!currentUser) {
......@@ -82,12 +100,21 @@ export function AuthProvider({ children }: { children: ReactNode }) {
queryClient.setQueryData(userKeys.currentUser(), currentUser);
queryClient.setQueryData(userKeys.detail(currentUser.name), currentUser);
} catch (error) {
// Silently handle 401/403 - user is just not logged in
const errorMessage = error instanceof Error ? error.message : String(error);
const isUnauthorized = errorMessage.includes("401") ||
errorMessage.includes("403") ||
errorMessage.includes("Unauthorized") ||
errorMessage.includes("Missing authentication token");
if (!isUnauthorized) {
// Only log/show errors for actual network issues
console.error("Failed to initialize auth:", error);
const errorMessage = error instanceof Error ? error.message : "Không thể khởi tạo xác thực";
// Only show toast for network errors, not for 401/403 (user just not logged in)
if (errorMessage.includes("NetworkError") || errorMessage.includes("kết nối")) {
toast.error(errorMessage);
}
}
clearAccessToken();
setState({
currentUser: undefined,
......
......@@ -100,12 +100,20 @@ export function InstanceProvider({ children }: { children: ReactNode }) {
isLoading: false,
});
} catch (error) {
// Silently handle errors - instance initialization failure shouldn't block app
const errorMessage = error instanceof Error ? error.message : String(error);
// Only log/show errors for actual network issues, not auth errors
const isAuthError = errorMessage.includes("401") ||
errorMessage.includes("403") ||
errorMessage.includes("Unauthorized");
if (!isAuthError) {
console.error("Failed to initialize instance:", error);
const errorMessage = error instanceof Error ? error.message : "Không thể khởi tạo instance";
// Show toast for network errors
if (errorMessage.includes("NetworkError") || errorMessage.includes("kết nối")) {
toast.error(errorMessage);
}
}
setState((prev) => ({
...prev,
isInitialized: true,
......
......@@ -25,6 +25,8 @@ export function useInstanceProfile() {
return profile;
},
staleTime: 1000 * 60 * 10, // 10 minutes - instance profile rarely changes
refetchOnWindowFocus: false, // Don't refetch on window focus - instance rarely changes
refetchOnReconnect: false, // Don't refetch on reconnect - instance rarely changes
});
}
......
......@@ -31,7 +31,10 @@ export function useCurrentUserQuery() {
} catch (error) {
// 401 is expected when not logged in - return undefined instead of throwing
const errorMessage = error instanceof Error ? error.message : String(error);
if (errorMessage.includes("401") || errorMessage.includes("Unauthorized")) {
if (errorMessage.includes("401") ||
errorMessage.includes("403") ||
errorMessage.includes("Unauthorized") ||
errorMessage.includes("Missing authentication token")) {
return undefined;
}
throw error;
......@@ -39,6 +42,8 @@ export function useCurrentUserQuery() {
},
staleTime: 1000 * 60 * 5, // 5 minutes - auth doesn't change often
retry: false, // Don't retry on 401 - user is simply not logged in
refetchOnWindowFocus: false, // Don't refetch on window focus - reduces lag
refetchOnReconnect: false, // Don't refetch on reconnect - reduces unnecessary requests
});
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment