[0.2.8.11][ci]
This commit is contained in:
@@ -48,7 +48,7 @@
|
|||||||
<!-- 侧边栏 -->
|
<!-- 侧边栏 -->
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div class="user-id-sidebar">
|
<div class="user-id-sidebar">
|
||||||
<h3>你好,{{ username|default:"访客" }}</h3>
|
<h3>你好,<span id="sidebarUsername">{{ username|default:"访客" }}</span></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="navigation-links">
|
<div class="navigation-links">
|
||||||
<a href="{% url 'main:home' %}">返回主页</a>
|
<a href="{% url 'main:home' %}">返回主页</a>
|
||||||
@@ -58,6 +58,62 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
|
{% if subpage %}
|
||||||
|
<div class="profile-card">
|
||||||
|
<div class="profile-header">
|
||||||
|
<div class="profile-info">
|
||||||
|
<h2>{{ subpage_title }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 12px;">
|
||||||
|
<a href="{% url 'accounts:profile' %}" style="color:#2d8cf0; text-decoration:none;">返回个人中心</a>
|
||||||
|
</div>
|
||||||
|
{% if subpage == "username" %}
|
||||||
|
<form id="nameForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="newUsername">新用户名</label>
|
||||||
|
<input type="text" id="newUsername" placeholder="请输入新用户名" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn">保存</button>
|
||||||
|
<div id="nameMsg" class="msg"></div>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% if subpage == "password" %}
|
||||||
|
{% if permission_name != "管理员" and not profile_user.manage_key %}
|
||||||
|
<form id="pwdForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="newPassword">新密码</label>
|
||||||
|
<input type="password" id="newPassword" autocomplete="new-password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="confirmPassword">确认密码</label>
|
||||||
|
<input type="password" id="confirmPassword" autocomplete="new-password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn">保存</button>
|
||||||
|
<div id="pwdMsg" class="msg"></div>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<div class="msg error">无权限</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if subpage == "registration-code" %}
|
||||||
|
<form id="rcForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="newRegCode">新注册码</label>
|
||||||
|
<input type="text" id="newRegCode" placeholder="输入新注册码后替换原有 key" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>预览</label>
|
||||||
|
<div id="rcPreview" style="background:#f8fafc; border:1px solid #e5e7eb; border-radius:10px; padding:10px 12px; font-size:13px; color:#334155;">
|
||||||
|
<div style="color:#64748b;">输入注册码后自动显示 key 预览</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn">替换</button>
|
||||||
|
<div id="rcMsg" class="msg"></div>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
<div class="profile-card">
|
<div class="profile-card">
|
||||||
<div class="profile-header">
|
<div class="profile-header">
|
||||||
<div class="profile-info">
|
<div class="profile-info">
|
||||||
@@ -65,7 +121,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-details">
|
<div class="profile-details">
|
||||||
<p><span class="label">用户名:</span> {{ profile_user.username }}</p>
|
<p><span class="label">用户名:</span> <span id="profileUsername">{{ profile_user.username }}</span></p>
|
||||||
<p><span class="label">用户ID:</span> {{ profile_user.user_id }}</p>
|
<p><span class="label">用户ID:</span> {{ profile_user.user_id }}</p>
|
||||||
<p><span class="label">注册码:</span> {{ profile_user.registration_code|default:"无" }}</p>
|
<p><span class="label">注册码:</span> {{ profile_user.registration_code|default:"无" }}</p>
|
||||||
<p><span class="label">所属:</span> {{ profile_user.key|join:"、"|default:"未填写" }}</p>
|
<p><span class="label">所属:</span> {{ profile_user.key|join:"、"|default:"未填写" }}</p>
|
||||||
@@ -74,6 +130,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="profile-card">
|
||||||
|
<div class="profile-header">
|
||||||
|
<div class="profile-info">
|
||||||
|
<h2>账号设置</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex; gap:12px; flex-wrap:wrap;">
|
||||||
|
<a class="btn" href="{% url 'accounts:profile_username' %}">修改用户名</a>
|
||||||
|
{% if permission_name != "管理员" and not profile_user.manage_key %}
|
||||||
|
<a class="btn" href="{% url 'accounts:profile_password' %}">修改密码</a>
|
||||||
|
{% endif %}
|
||||||
|
<a class="btn" href="{% url 'accounts:profile_registration_code' %}">替换注册码</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section-title">我的提交</div>
|
<div class="section-title">我的提交</div>
|
||||||
{% if achievements %}
|
{% if achievements %}
|
||||||
<div class="image-grid">
|
<div class="image-grid">
|
||||||
@@ -96,49 +167,6 @@
|
|||||||
<a href="{% url 'elastic:upload_page' %}" style="color: #2d8cf0; text-decoration: none;">去上传第一张图片吧!</a>
|
<a href="{% url 'elastic:upload_page' %}" style="color: #2d8cf0; text-decoration: none;">去上传第一张图片吧!</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="profile-card rc-card">
|
|
||||||
<div class="profile-header">
|
|
||||||
<div class="profile-info">
|
|
||||||
<h2>替换注册码</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form id="rcForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="newRegCode">新注册码</label>
|
|
||||||
<input type="text" id="newRegCode" placeholder="输入新注册码后替换原有 key" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>预览</label>
|
|
||||||
<div id="rcPreview" style="background:#f8fafc; border:1px solid #e5e7eb; border-radius:10px; padding:10px 12px; font-size:13px; color:#334155;">
|
|
||||||
<div style="color:#64748b;">输入注册码后自动显示 key 预览</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn">替换</button>
|
|
||||||
<div id="rcMsg" class="msg"></div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if permission_name != "管理员" and not profile_user.manage_key %}
|
|
||||||
<div class="profile-card">
|
|
||||||
<div class="profile-header">
|
|
||||||
<div class="profile-info">
|
|
||||||
<h2>修改密码</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form id="pwdForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="newPassword">新密码</label>
|
|
||||||
<input type="password" id="newPassword" autocomplete="new-password" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="confirmPassword">确认密码</label>
|
|
||||||
<input type="password" id="confirmPassword" autocomplete="new-password" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn">保存</button>
|
|
||||||
<div id="pwdMsg" class="msg"></div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -230,6 +258,62 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nameForm = document.getElementById('nameForm');
|
||||||
|
if (nameForm) {
|
||||||
|
nameForm.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const msg = document.getElementById('nameMsg');
|
||||||
|
msg.textContent = '';
|
||||||
|
msg.className = 'msg';
|
||||||
|
const input = document.getElementById('newUsername');
|
||||||
|
const newName = (input.value || '').trim();
|
||||||
|
const currentNameEl = document.getElementById('profileUsername');
|
||||||
|
const currentName = (currentNameEl && currentNameEl.textContent ? currentNameEl.textContent : '').trim();
|
||||||
|
if (!newName) {
|
||||||
|
msg.textContent = '请输入用户名';
|
||||||
|
msg.className = 'msg error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newName.length > 50) {
|
||||||
|
msg.textContent = '用户名过长';
|
||||||
|
msg.className = 'msg error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentName && newName === currentName) {
|
||||||
|
msg.textContent = '用户名未变化';
|
||||||
|
msg.className = 'msg error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const csrftoken = getCookie('csrftoken');
|
||||||
|
const resp = await fetch('/accounts/profile/username/update/', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': csrftoken || ''
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ username: newName })
|
||||||
|
});
|
||||||
|
const data = await resp.json();
|
||||||
|
if (resp.ok && data.ok) {
|
||||||
|
msg.textContent = '修改成功';
|
||||||
|
msg.className = 'msg success';
|
||||||
|
if (currentNameEl) currentNameEl.textContent = data.username || newName;
|
||||||
|
const sidebarName = document.getElementById('sidebarUsername');
|
||||||
|
if (sidebarName) sidebarName.textContent = data.username || newName;
|
||||||
|
input.value = '';
|
||||||
|
} else {
|
||||||
|
msg.textContent = (data && data.message) ? data.message : '操作失败';
|
||||||
|
msg.className = 'msg error';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
msg.textContent = '操作失败';
|
||||||
|
msg.className = 'msg error';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const rcForm = document.getElementById('rcForm');
|
const rcForm = document.getElementById('rcForm');
|
||||||
if (rcForm) {
|
if (rcForm) {
|
||||||
let rcPreviewTimer = null;
|
let rcPreviewTimer = null;
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ urlpatterns = [
|
|||||||
path("register/submit/", views.register_submit, name="register_submit"),
|
path("register/submit/", views.register_submit, name="register_submit"),
|
||||||
path("email/send-code/", views.send_email_code, name="send_email_code"),
|
path("email/send-code/", views.send_email_code, name="send_email_code"),
|
||||||
path("profile/", views.profile_page, name="profile"),
|
path("profile/", views.profile_page, name="profile"),
|
||||||
|
path("profile/username/", views.profile_username_page, name="profile_username"),
|
||||||
|
path("profile/password/", views.profile_password_page, name="profile_password"),
|
||||||
|
path("profile/registration-code/", views.profile_registration_code_page, name="profile_registration_code"),
|
||||||
|
path("profile/username/update/", views.update_profile_username_view, name="update_profile_username"),
|
||||||
path("profile/registration-code/replace/", views.replace_registration_code_view, name="replace_registration_code"),
|
path("profile/registration-code/replace/", views.replace_registration_code_view, name="replace_registration_code"),
|
||||||
path("profile/registration-code/preview/", views.registration_code_preview_view, name="registration_code_preview"),
|
path("profile/registration-code/preview/", views.registration_code_preview_view, name="registration_code_preview"),
|
||||||
path("registration-code/request/submit/", views.submit_registration_code_request_view, name="submit_registration_code_request"),
|
path("registration-code/request/submit/", views.submit_registration_code_request_view, name="submit_registration_code_request"),
|
||||||
|
|||||||
@@ -71,33 +71,62 @@ def set_session_key(request):
|
|||||||
request.session["session_enc_key_b64"] = base64.b64encode(key_bytes).decode("ascii")
|
request.session["session_enc_key_b64"] = base64.b64encode(key_bytes).decode("ascii")
|
||||||
return JsonResponse({"ok": True})
|
return JsonResponse({"ok": True})
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
def _build_profile_context(request):
|
||||||
@ensure_csrf_cookie
|
|
||||||
def profile_page(request):
|
|
||||||
session_user_id = request.session.get("user_id")
|
session_user_id = request.session.get("user_id")
|
||||||
if session_user_id is None:
|
if session_user_id is None:
|
||||||
return redirect("/accounts/login/")
|
return None
|
||||||
|
|
||||||
# 获取用户信息
|
|
||||||
user = get_user_by_id(session_user_id)
|
user = get_user_by_id(session_user_id)
|
||||||
if not user:
|
if not user:
|
||||||
return redirect("/accounts/login/")
|
return None
|
||||||
|
|
||||||
# 获取个人提交的成就(图片)
|
|
||||||
from elastic.es_connect import search_all
|
from elastic.es_connect import search_all
|
||||||
from elastic.views import _attach_image_urls
|
from elastic.views import _attach_image_urls
|
||||||
|
|
||||||
raw_results = [r for r in search_all() if str(r.get("writer_id", "")) == str(session_user_id)]
|
raw_results = [r for r in search_all() if str(r.get("writer_id", "")) == str(session_user_id)]
|
||||||
achievements = _attach_image_urls(request, raw_results)
|
achievements = _attach_image_urls(request, raw_results)
|
||||||
|
|
||||||
permission_name = "管理员" if int(user.get("permission", 1)) == 0 else "普通用户"
|
permission_name = "管理员" if int(user.get("permission", 1)) == 0 else "普通用户"
|
||||||
|
return {
|
||||||
context = {
|
|
||||||
"username": request.session.get("username"),
|
"username": request.session.get("username"),
|
||||||
"profile_user": user,
|
"profile_user": user,
|
||||||
"permission_name": permission_name,
|
"permission_name": permission_name,
|
||||||
"achievements": achievements,
|
"achievements": achievements,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def profile_page(request):
|
||||||
|
context = _build_profile_context(request)
|
||||||
|
if context is None:
|
||||||
|
return redirect("/accounts/login/")
|
||||||
|
context["subpage"] = ""
|
||||||
|
return render(request, "accounts/profile.html", context)
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def profile_username_page(request):
|
||||||
|
context = _build_profile_context(request)
|
||||||
|
if context is None:
|
||||||
|
return redirect("/accounts/login/")
|
||||||
|
context["subpage"] = "username"
|
||||||
|
context["subpage_title"] = "修改用户名"
|
||||||
|
return render(request, "accounts/profile.html", context)
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def profile_password_page(request):
|
||||||
|
context = _build_profile_context(request)
|
||||||
|
if context is None:
|
||||||
|
return redirect("/accounts/login/")
|
||||||
|
context["subpage"] = "password"
|
||||||
|
context["subpage_title"] = "修改密码"
|
||||||
|
return render(request, "accounts/profile.html", context)
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
@ensure_csrf_cookie
|
||||||
|
def profile_registration_code_page(request):
|
||||||
|
context = _build_profile_context(request)
|
||||||
|
if context is None:
|
||||||
|
return redirect("/accounts/login/")
|
||||||
|
context["subpage"] = "registration-code"
|
||||||
|
context["subpage_title"] = "替换注册码"
|
||||||
return render(request, "accounts/profile.html", context)
|
return render(request, "accounts/profile.html", context)
|
||||||
|
|
||||||
@require_http_methods(["POST"])
|
@require_http_methods(["POST"])
|
||||||
@@ -304,6 +333,34 @@ def replace_registration_code_view(request):
|
|||||||
return JsonResponse({"ok": False, "message": "替换失败"}, status=500)
|
return JsonResponse({"ok": False, "message": "替换失败"}, status=500)
|
||||||
return JsonResponse({"ok": True})
|
return JsonResponse({"ok": True})
|
||||||
|
|
||||||
|
@require_http_methods(["POST"])
|
||||||
|
@csrf_protect
|
||||||
|
def update_profile_username_view(request):
|
||||||
|
session_user_id = request.session.get("user_id")
|
||||||
|
if session_user_id is None:
|
||||||
|
return JsonResponse({"ok": False, "message": "未登录"}, status=401)
|
||||||
|
try:
|
||||||
|
payload = json.loads(request.body.decode("utf-8"))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return JsonResponse({"ok": False, "message": "JSON无效"}, status=400)
|
||||||
|
new_username = (payload.get("username") or "").strip()
|
||||||
|
if not new_username:
|
||||||
|
return JsonResponse({"ok": False, "message": "请输入用户名"}, status=400)
|
||||||
|
if len(new_username) > 50:
|
||||||
|
return JsonResponse({"ok": False, "message": "用户名过长"}, status=400)
|
||||||
|
me = get_user_by_id(session_user_id) or {}
|
||||||
|
if str(me.get("username", "")).strip() == new_username:
|
||||||
|
request.session["username"] = new_username
|
||||||
|
return JsonResponse({"ok": True, "username": new_username})
|
||||||
|
existing = es_get_user_by_username(new_username)
|
||||||
|
if existing and str(existing.get("user_id")) != str(session_user_id):
|
||||||
|
return JsonResponse({"ok": False, "message": "用户名已存在"}, status=409)
|
||||||
|
ok = update_user_by_id(session_user_id, username=new_username)
|
||||||
|
if not ok:
|
||||||
|
return JsonResponse({"ok": False, "message": "修改失败"}, status=500)
|
||||||
|
request.session["username"] = new_username
|
||||||
|
return JsonResponse({"ok": True, "username": new_username})
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def registration_code_preview_view(request):
|
def registration_code_preview_view(request):
|
||||||
session_user_id = request.session.get("user_id")
|
session_user_id = request.session.get("user_id")
|
||||||
|
|||||||
Reference in New Issue
Block a user