github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/forum_perms_store.go (about) 1 package common 2 3 import ( 4 "database/sql" 5 "encoding/json" 6 "sync" 7 8 qgen "github.com/Azareal/Gosora/query_gen" 9 ) 10 11 var FPStore ForumPermsStore 12 13 type ForumPermsStore interface { 14 Init() error 15 GetAllMap() (bigMap map[int]map[int]*ForumPerms) 16 Get(fid, gid int) (fp *ForumPerms, err error) 17 GetCopy(fid, gid int) (fp ForumPerms, err error) 18 ReloadAll() error 19 Reload(id int) error 20 } 21 22 type ForumPermsCache interface { 23 } 24 25 type MemoryForumPermsStore struct { 26 getByForum *sql.Stmt 27 getByForumGroup *sql.Stmt 28 29 evenForums map[int]map[int]*ForumPerms 30 oddForums map[int]map[int]*ForumPerms // [fid][gid]*ForumPerms 31 evenLock sync.RWMutex 32 oddLock sync.RWMutex 33 } 34 35 func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) { 36 acc := qgen.NewAcc() 37 fp := "forums_permissions" 38 return &MemoryForumPermsStore{ 39 getByForum: acc.Select(fp).Columns("gid,permissions").Where("fid=?").Orderby("gid ASC").Prepare(), 40 getByForumGroup: acc.Select(fp).Columns("permissions").Where("fid=? AND gid=?").Prepare(), 41 42 evenForums: make(map[int]map[int]*ForumPerms), 43 oddForums: make(map[int]map[int]*ForumPerms), 44 }, acc.FirstError() 45 } 46 47 func (s *MemoryForumPermsStore) Init() error { 48 DebugLog("Initialising the forum perms store") 49 return s.ReloadAll() 50 } 51 52 // TODO: Optimise this? 53 func (s *MemoryForumPermsStore) ReloadAll() error { 54 DebugLog("Reloading the forum perms") 55 fids, e := Forums.GetAllIDs() 56 if e != nil { 57 return e 58 } 59 for _, fid := range fids { 60 if e := s.reload(fid); e != nil { 61 return e 62 } 63 } 64 if e := s.recalcCanSeeAll(); e != nil { 65 return e 66 } 67 TopicListThaw.Thaw() 68 return nil 69 } 70 71 func (s *MemoryForumPermsStore) parseForumPerm(perms []byte) (pperms *ForumPerms, e error) { 72 DebugDetail("perms: ", string(perms)) 73 pperms = BlankForumPerms() 74 e = json.Unmarshal(perms, &pperms) 75 pperms.ExtData = make(map[string]bool) 76 pperms.Overrides = true 77 return pperms, e 78 } 79 80 func (s *MemoryForumPermsStore) Reload(fid int) error { 81 e := s.reload(fid) 82 if e != nil { 83 return e 84 } 85 if e = s.recalcCanSeeAll(); e != nil { 86 return e 87 } 88 TopicListThaw.Thaw() 89 return nil 90 } 91 92 // TODO: Need a more thread-safe way of doing this. Possibly with sync.Map? 93 func (s *MemoryForumPermsStore) reload(fid int) error { 94 DebugLogf("Reloading the forum permissions for forum #%d", fid) 95 rows, err := s.getByForum.Query(fid) 96 if err != nil { 97 return err 98 } 99 defer rows.Close() 100 101 forumPerms := make(map[int]*ForumPerms) 102 for rows.Next() { 103 var gid int 104 var perms []byte 105 err := rows.Scan(&gid, &perms) 106 if err != nil { 107 return err 108 } 109 110 DebugLog("gid:", gid) 111 DebugLogf("perms: %+v\n", perms) 112 pperms, err := s.parseForumPerm(perms) 113 if err != nil { 114 return err 115 } 116 DebugLogf("pperms: %+v\n", pperms) 117 forumPerms[gid] = pperms 118 } 119 DebugLogf("forumPerms: %+v\n", forumPerms) 120 121 if fid%2 == 0 { 122 s.evenLock.Lock() 123 s.evenForums[fid] = forumPerms 124 s.evenLock.Unlock() 125 } else { 126 s.oddLock.Lock() 127 s.oddForums[fid] = forumPerms 128 s.oddLock.Unlock() 129 } 130 return nil 131 } 132 133 func (s *MemoryForumPermsStore) recalcCanSeeAll() error { 134 groups, err := Groups.GetAll() 135 if err != nil { 136 return err 137 } 138 fids, err := Forums.GetAllIDs() 139 if err != nil { 140 return err 141 } 142 143 gc, ok := Groups.(GroupCache) 144 if !ok { 145 TopicListThaw.Thaw() 146 return nil 147 } 148 149 // A separate loop to avoid contending on the odd-even locks as much 150 fForumPerms := make(map[int]map[int]*ForumPerms) 151 for _, fid := range fids { 152 var forumPerms map[int]*ForumPerms 153 var ok bool 154 if fid%2 == 0 { 155 s.evenLock.RLock() 156 forumPerms, ok = s.evenForums[fid] 157 s.evenLock.RUnlock() 158 } else { 159 s.oddLock.RLock() 160 forumPerms, ok = s.oddForums[fid] 161 s.oddLock.RUnlock() 162 } 163 if ok { 164 fForumPerms[fid] = forumPerms 165 } 166 } 167 168 // TODO: Can we recalculate CanSee without calculating every other forum? 169 for _, g := range groups { 170 DebugLogf("Updating the forum permissions for Group #%d", g.ID) 171 canSee := []int{} 172 for _, fid := range fids { 173 DebugDetailf("Forum #%+v\n", fid) 174 forumPerms, ok := fForumPerms[fid] 175 if !ok { 176 continue 177 } 178 fp, ok := forumPerms[g.ID] 179 if !ok { 180 if g.Perms.ViewTopic { 181 canSee = append(canSee, fid) 182 } 183 continue 184 } 185 186 if fp.Overrides { 187 if fp.ViewTopic { 188 canSee = append(canSee, fid) 189 } 190 } else if g.Perms.ViewTopic { 191 canSee = append(canSee, fid) 192 } 193 //DebugDetail("g.ID: ", g.ID) 194 DebugDetailf("forumPerm: %+v\n", fp) 195 DebugDetail("canSee: ", canSee) 196 } 197 DebugDetailf("canSee (length %d): %+v \n", len(canSee), canSee) 198 gc.SetCanSee(g.ID, canSee) 199 } 200 201 return nil 202 } 203 204 // ! Throughput on this might be bad due to the excessive locking 205 func (s *MemoryForumPermsStore) GetAllMap() (bigMap map[int]map[int]*ForumPerms) { 206 bigMap = make(map[int]map[int]*ForumPerms) 207 s.evenLock.RLock() 208 for fid, subMap := range s.evenForums { 209 bigMap[fid] = subMap 210 } 211 s.evenLock.RUnlock() 212 s.oddLock.RLock() 213 for fid, subMap := range s.oddForums { 214 bigMap[fid] = subMap 215 } 216 s.oddLock.RUnlock() 217 return bigMap 218 } 219 220 // TODO: Add a hook here and have plugin_guilds use it 221 // TODO: Check if the forum exists? 222 // TODO: Fix the races 223 // TODO: Return BlankForumPerms() when the forum permission set doesn't exist? 224 func (s *MemoryForumPermsStore) Get(fid, gid int) (fp *ForumPerms, err error) { 225 var fmap map[int]*ForumPerms 226 var ok bool 227 if fid%2 == 0 { 228 s.evenLock.RLock() 229 fmap, ok = s.evenForums[fid] 230 s.evenLock.RUnlock() 231 } else { 232 s.oddLock.RLock() 233 fmap, ok = s.oddForums[fid] 234 s.oddLock.RUnlock() 235 } 236 if !ok { 237 return fp, ErrNoRows 238 } 239 240 fp, ok = fmap[gid] 241 if !ok { 242 return fp, ErrNoRows 243 } 244 return fp, nil 245 } 246 247 // TODO: Check if the forum exists? 248 // TODO: Fix the races 249 func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fp ForumPerms, e error) { 250 fPermsPtr, e := s.Get(fid, gid) 251 if e != nil { 252 return fp, e 253 } 254 return *fPermsPtr, nil 255 }