Repository: ArtalkJS/Artalk

Issue:
文章被评论时通知管理员功能无效
使用的是SMTP发送服务，评论回复提醒功能是正常的，可以根据设置的信息，正常发送提醒邮件给被评论人的邮箱。

但是管理员提醒这个，收不到，预期应该是有人评论了我博客文章，那管理员应该可以收到邮件通知对吧。目前收不到。

**日志显示  WARN Multi push disabled: no admin user found**

管理员肯定是有的，站点的user设置里，是由admin角色的用户的。

配置里enable是true的
```
# Multi-push
admin_notify:
  # Notification template (set to file path to use custom template)
  notify_tpl: "default"
  # Noise mode
  # -- noise_mode is disabled by default. --
  # -- When this option is set to `false`, only messages sent to the administrator will be notified, --
  # -- such as "user A" replies to "user B", the communication between these two users will not be notified to the administrator. --
  noise_mode: false
  # Notify admin
  email:
    # Enable (can be disabled when using other push methods)
    enabled: true
    # Email subject (email subject sent to admin)
    mail_subject: '[{{site_name}}] Post "{{page_title}}" has new a comment'
    # Admin email template file (set to file path to use custom template)
    mail_tpl: ""
  # Telegram
``` 





Discussion:
@qwqcode: 试过重启 Artalk 吗@toyo2333: > 试过重启 Artalk 吗

我是docker部署的，点击应用后，他就自己重启服务了吧？ 

另外我看文档，这一段https://artalk.js.org/guide/backend/multi-site.html#%E6%8E%A7%E5%88%B6%E7%AE%A1%E7%90%86%E5%91%98%E6%8E%A5%E6%94%B6%E9%82%AE%E4%BB%B6%E9%80%9A%E7%9F%A5

里面提到这个管理员意思是用户名也是admin？才算？

我通过 docker exec -it artalk artalk admin 创建的管理员 用户名输入的是ABC 我靠 这里有点晕了。。难道我创建的不是管理员 所以才报错‘日志显示 WARN Multi push disabled: no admin user found’？@qwqcode: 问题确实存在，感谢反馈。当前的版本创建新管理员用户后，有概率会由于原来的管理员ID列表被缓存而无法刷新缓存。暂时的解决办法：创建管理员用户后，手动重启 Artalk 的 docker 容器 `docker container restart artalk`.

v2.8.0 将修复这个问题

---

另外：docker 容器创建新管理员用户，直接执行命令 `docker exec -it artalk artalk admin` 会提示输入邮箱、用户名、密码。@JadeVane: 我也遇到了这个问题，版本`v2.9.1`，设置了`pending_default: true`和`noise_mode: true`

当我回复其他人的评论时，对方是可以正确收到邮件通知的，但当别人在我的博客上评论，我却没收到邮件通知，而且在debug模式中也没看到任何报错

另外，telegram通知也没生效，手动测试过bot id和用户id都是正确的，同样没有报错


Solution patch:
diff --git a/internal/dao/query_find.go b/internal/dao/query_find.go
index 4096f08c..c6a72676 100644
--- a/internal/dao/query_find.go
+++ b/internal/dao/query_find.go
@@ -219,16 +219,11 @@ func (dao *Dao) GetVoteNumUpDown(targetID uint, voteTo string) (int, int) {
 //#endregion
 
 // #region 管理员账号检测
-var allAdmins *[]entity.User = nil
-
 func (dao *Dao) GetAllAdmins() []entity.User {
-	if allAdmins == nil {
-		var admins []entity.User
-		dao.DB().Where(&entity.User{IsAdmin: true}).Find(&admins)
-		allAdmins = &admins
-	}
-
-	return *allAdmins
+	// TODO add cache and flush cache when admin changed
+	var admins []entity.User
+	dao.DB().Where(&entity.User{IsAdmin: true}).Find(&admins)
+	return admins
 }
 
 func (dao *Dao) GetAllAdminIDs() []uint {



Test patch:
diff --git a/internal/dao/query_find_test.go b/internal/dao/query_find_test.go
index c49aa7d7..96181656 100644
--- a/internal/dao/query_find_test.go
+++ b/internal/dao/query_find_test.go
@@ -3,6 +3,7 @@ package dao_test
 import (
 	"testing"
 
+	"github.com/ArtalkJS/Artalk/internal/entity"
 	"github.com/ArtalkJS/Artalk/test"
 	"github.com/stretchr/testify/assert"
 )
@@ -203,7 +204,47 @@ func TestGetAllAdmins(t *testing.T) {
 	defer app.Cleanup()
 
 	allAdmins := app.Dao().GetAllAdmins()
-	assert.GreaterOrEqual(t, len(allAdmins), 1)
+	assert.GreaterOrEqual(t, len(allAdmins), 1, "GetAllAdmins() not works")
+
+	t.Run("Test modify and get admins", func(t *testing.T) {
+		getContainsUser := func(userID uint) bool {
+			for _, a := range app.Dao().GetAllAdmins() {
+				if a.ID == userID {
+					return true
+				}
+			}
+			return false
+		}
+
+		// create
+		var adminID uint = 0
+		admin := entity.User{
+			Name:    "TestAdmin",
+			Email:   "admin@test.com",
+			IsAdmin: true,
+		}
+		app.Dao().CreateUser(&admin)
+		adminID = admin.ID
+		assert.NotZero(t, adminID, "user create failed")
+
+		assert.Equal(t, true, getContainsUser(adminID), "admin not found after create")
+
+		// update
+		admin.IsAdmin = false
+		app.Dao().UpdateUser(&admin)
+		assert.Equal(t, false, getContainsUser(adminID), "admin still exists after update")
+
+		// re-update
+		admin.IsAdmin = true
+		app.Dao().UpdateUser(&admin)
+		assert.Equal(t, true, getContainsUser(adminID), "admin not found after re-update to admin")
+
+		// delete
+		app.Dao().DelUser(&admin)
+
+		// not contains admin
+		assert.Equal(t, false, getContainsUser(adminID), "admin still exists after delete")
+	})
 }
 
 func TestIsAdminUser(t *testing.T) {