Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note_extension
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note_extension
Commits
118c3180
Commit
118c3180
authored
Feb 02, 2026
by
Hoanganhvu123
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update
parent
1732c42d
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
113 additions
and
75 deletions
+113
-75
.env
.env
+2
-0
user_routes.py
backend/api/memos/user_routes.py
+1
-1
services.py
backend/common/memos_core/services.py
+22
-1
entrypoint.sh
backend/entrypoint.sh
+23
-12
requirements.txt
backend/requirements.txt
+0
-1
docker-compose.yml
docker-compose.yml
+9
-49
.dockerignore
frontend/.dockerignore
+0
-4
Dockerfile.prod
frontend/Dockerfile.prod
+7
-2
env.txt
frontend/env.txt
+2
-0
AuthContext.tsx
frontend/src/contexts/AuthContext.tsx
+29
-2
InstanceContext.tsx
frontend/src/contexts/InstanceContext.tsx
+10
-2
useInstanceQueries.ts
frontend/src/hooks/useInstanceQueries.ts
+2
-0
useUserQueries.ts
frontend/src/hooks/useUserQueries.ts
+6
-1
No files found.
.env
0 → 100644
View file @
118c3180
VITE_CLERK_PUBLISHABLE_KEY=pk_test_Y29tbXVuYWwtc3VuYmVhbS0wLmNsZXJrLmFjY291bnRzLmRldiQ
CLERK_SECRET_KEY=sk_test_ek7ozVR80Qi9UdvhGaTmlXovS16GDuBDlDrpH1rkyQ
\ No newline at end of file
backend/api/memos/user_routes.py
View file @
118c3180
...
...
@@ -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
),
):
"""
...
...
backend/common/memos_core/services.py
View file @
118c3180
...
...
@@ -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
:
...
...
backend/entrypoint.sh
View file @
118c3180
...
...
@@ -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 exi
st)"
"
||
echo
"⚠️ Could not set up indexes (
will retry on first reque
st)"
# 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
\
...
...
backend/requirements.txt
View file @
118c3180
...
...
@@ -135,4 +135,3 @@ zope.event==6.1
zope.interface==8.1.1
zstandard==0.25.0
gunicorn==23.0.0
copilotkit
docker-compose.yml
View file @
118c3180
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
...
...
frontend/.dockerignore
View file @
118c3180
...
...
@@ -20,10 +20,6 @@ build/
*.swp
*.swo
# Environment
.env
.env.local
# Testing
tests/
coverage/
...
...
frontend/Dockerfile.prod
View file @
118c3180
...
...
@@ -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 \
...
...
frontend/env.txt
0 → 100644
View file @
118c3180
VITE_CLERK_PUBLISHABLE_KEY=pk_test_Y29tbXVuYWwtc3VuYmVhbS0wLmNsZXJrLmFjY291bnRzLmRldiQ
CLERK_SECRET_KEY=sk_test_ek7ozVR80Qi9UdvhGaTmlXovS16GDuBDlDrpH1rkyQ
\ No newline at end of file
frontend/src/contexts/AuthContext.tsx
View file @
118c3180
...
...
@@ -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
,
...
...
frontend/src/contexts/InstanceContext.tsx
View file @
118c3180
...
...
@@ -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
,
...
...
frontend/src/hooks/useInstanceQueries.ts
View file @
118c3180
...
...
@@ -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
});
}
...
...
frontend/src/hooks/useUserQueries.ts
View file @
118c3180
...
...
@@ -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
});
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment