merge origin/main: adopt env-var secrets fix, keep no-SQLite structure

This commit is contained in:
chunyu
2026-09-04 12:50:38 +08:00
5 changed files with 36 additions and 33 deletions
+7 -5
View File
@@ -417,8 +417,9 @@ class ArticleLikeToggleView(APIView):
like, created = ArticleLike.objects.get_or_create(user=request.user, article=article)
if not created:
like.delete()
article.likes = max(0, article.likes - 1)
article.save(update_fields=['likes'])
Article.objects.filter(pk=pk).update(likes=F("likes") - 1)
article.refresh_from_db()
article.likes = max(0, article.likes)
return create_standardized_response(
data={'liked': False, 'likes_count': article.likes},
code=ResponseCode.SUCCESS
@@ -498,8 +499,9 @@ class ArticleCommentLikeToggleView(APIView):
like, created = ArticleCommentLike.objects.get_or_create(user=request.user, comment=comment)
if not created:
like.delete()
comment.likes = max(0, comment.likes - 1)
comment.save(update_fields=['likes'])
ArticleComment.objects.filter(pk=pk).update(likes=F("likes") - 1)
comment.refresh_from_db()
comment.likes = max(0, comment.likes)
return create_standardized_response(
data={'liked': False, 'likes_count': comment.likes},
code=ResponseCode.SUCCESS
@@ -626,4 +628,4 @@ class ArticleToggleTopView(APIView):
data={'is_top': article.is_top},
code=ResponseCode.SUCCESS,
message='置顶状态已更新'
)
)
+5 -5
View File
@@ -21,7 +21,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-j+-psq=m++l7hup73k1eg+wm-b&2_)!+_^o=6(=xc$1px*kqt@'
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'django-insecure-dev-only-key-change-in-production')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get('DJANGO_DEBUG', 'True') == 'True'
@@ -66,7 +66,7 @@ CORS_ALLOWED_ORIGINS = [
]
CORS_ALLOW_ALL_ORIGINS = False
ALLOWED_HOSTS = ['*']
ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = [
@@ -127,7 +127,7 @@ EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 465
EMAIL_USE_SSL = True
EMAIL_HOST_USER = 'cs10086086@qq.com'
EMAIL_HOST_PASSWORD = 'ldzpymytsinkfjhb'
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', '')
EMAIL_USE_TLS = False
AUTH_USER_MODEL = 'user.FUser'
@@ -173,7 +173,7 @@ DATABASES = {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'chunyu_project',
'USER': 'root',
'PASSWORD': 'mysql_NpyMCS',
'PASSWORD': os.environ.get('MYSQL_PASSWORD', ''),
'HOST': '192.168.5.7',
'PORT': '3306',
'OPTIONS': {
@@ -189,7 +189,7 @@ DATABASES = {
REDIS_HOST = '192.168.5.7'
REDIS_PORT = 6379
REDIS_DB = 0
REDIS_PASSWORD = 'jhkdjhkjdhsIUTYURTU_sx86Jf'
REDIS_PASSWORD = os.environ.get('REDIS_PASSWORD', '')
SESSION_DB = 1
CELERY_BROKER_DB = 2
CELERY_RESULT_BACKEND_DB = 2
+13 -6
View File
@@ -500,10 +500,14 @@ class CDNStaticFileView(APIView):
)
if not os.path.exists(file_path):
raise Http404
response = FileResponse(open(file_path, 'rb'), content_type='text/markdown; charset=utf-8')
response['Cache-Control'] = 'max-age=86400'
return response
fh = open(file_path, "rb")
try:
response = FileResponse(fh, content_type="text/markdown; charset=utf-8")
response["Cache-Control"] = "max-age=86400"
return response
except Exception:
fh.close()
raise
class CourseFavoriteToggleView(APIView):
permission_classes = [IsAuthenticated]
@@ -745,7 +749,10 @@ class MyProgressView(APIView):
'last_studied_at': read.completed_at,
}
course_map[course_id]['completed_chapters'] += 1
if read.completed_at > course_map[course_id]['last_studied_at']:
if read.completed_at and (
not course_map[course_id]['last_studied_at'] or
read.completed_at > course_map[course_id]['last_studied_at']
):
course_map[course_id]['last_studied_at'] = read.completed_at
courses = []
@@ -765,4 +772,4 @@ class MyProgressView(APIView):
'total_courses': len(courses),
'total_completed': total_completed,
}
)
)
+10 -16
View File
@@ -67,8 +67,8 @@ class GlobalSearchView(APIView):
for article in articles:
cover_url = ''
if article.cover and hasattr(article.cover, 'url'):
cover_url = request.build_absolute_uri(article.cover.url)
if article.cover_image and hasattr(article.cover_image, 'url'):
cover_url = request.build_absolute_uri(article.cover_image.url)
excerpt = article.excerpt or ''
if not excerpt and article.content:
@@ -91,21 +91,16 @@ class GlobalSearchView(APIView):
).order_by('-created_at')[:100]
for tool in tools:
cover_url = ''
if tool.icon and hasattr(tool.icon, 'url'):
cover_url = request.build_absolute_uri(tool.icon.url)
results.append({
'id': tool.id,
'type': '工具',
'title': tool.name,
'desc': tool.description or '',
'cover': cover_url,
'views': tool.views or 0,
'likes': tool.likes or 0,
'cover': '',
'views': tool.usage_count or 0,
'likes': 0,
'url': f'/use?tool={tool.id}',
})
if content_type == 'all' or content_type == 'course':
courses = Course.objects.filter(
Q(title__icontains=q) | Q(description__icontains=q)
@@ -113,8 +108,8 @@ class GlobalSearchView(APIView):
for course in courses:
cover_url = ''
if course.cover and hasattr(course.cover, 'url'):
cover_url = request.build_absolute_uri(course.cover.url)
if course.cover_image and hasattr(course.cover_image, 'url'):
cover_url = request.build_absolute_uri(course.cover_image.url)
results.append({
'id': course.id,
@@ -122,9 +117,8 @@ class GlobalSearchView(APIView):
'title': course.title,
'desc': course.description or '',
'cover': cover_url,
'views': course.views or 0,
'likes': course.likes or 0,
'url': f'/courses/{course.id}',
'views': 0,
'likes': 0,
})
if content_type == 'all' or content_type == 'api':
@@ -236,4 +230,4 @@ class HotKeywordsView(APIView):
'Vue3',
'Python',
]
return create_standardized_response(data=hot_keywords, code=ResponseCode.SUCCESS)
return create_standardized_response(data=hot_keywords, code=ResponseCode.SUCCESS)
+1 -1
View File
@@ -17,7 +17,7 @@ from django.core.cache import caches
default_cache = caches['default']
session_cache = caches['session']
celery_cache = caches['celery']
from ..models import FUser, LoginRecord
from ..models import FUser, LoginRecord, Follow
from rest_framework_simplejwt.tokens import RefreshToken
from ..serializers.user_serializers import UserSerializer, ChangePasswordSerializer, UserUpdateSerializer
from ..tasks import send_verification_email_task, send_reset_password_email_task