feat: add admin user management with role-based access and self-protection

- Add AdminRequired middleware for admin-role routes
- Add login status check to reject disabled/locked accounts
- Add CRUD handlers (list, create, edit, delete) for users
- Add user_list and user_form templates with role/status badges
- Add nav entry and dashboard button for user management
- Add dynamic user count on admin dashboard
- Fix userIDFromSession to handle all numeric session types
- Self-protection: cannot delete/disable self or demote last admin
- Add EN/ZH i18n keys for all user management strings
This commit is contained in:
2026-06-22 18:11:39 +08:00
parent 6139b3ef2c
commit cefaeac618
11 changed files with 723 additions and 4 deletions
+15 -3
View File
@@ -36,11 +36,23 @@ func bytesToMB(b int64) string {
return fmt.Sprintf("%.1f", float64(b)/float64(1024*1024))
}
// userIDFromSession extracts the logged-in user's ID, or 0 if absent.
// userIDFromSession extracts the logged-in user's ID, or 0 if absent. It
// defends against int/uint/int64/float64 storage in the session.
func userIDFromSession(c *gin.Context) uint {
session := sessions.Default(c)
if id, ok := session.Get("user_id").(uint); ok {
return id
userID := session.Get("user_id")
if userID == nil {
return 0
}
switch v := userID.(type) {
case uint:
return v
case int:
return uint(v)
case int64:
return uint(v)
case float64:
return uint(v)
}
return 0
}