feat: chat bootstrap, members and group sync
GET /_bm/api/v1/chat gives a chat-group member ogólny (the bm/1 group conversation, adopted once as the system default channel under an advisory lock), its unread and mention counts, the member's DMs newest message first, the moderator flag and the composer limits. Every member bootstrap syncs participants with the group: missing members join ogólny, users no longer in the group lose their rows in every bm/1 conversation. A non-member gets only member:false and is pruned. GET /_bm/api/v1/chat/members lists members (id, name, surname, handle, is_bot from golem15.chatbots when active) and assigns missing handles; non-members get 404. The chatchannels access policy (ChatAccess) is installed from a database hook: bm/1 for members only, nobody manages channels. bm:chat-sync creates the groups when missing, syncs and assigns handles. Migration 20261026130000_seed_jz_bm_chat_groups creates studenci and moderatorzy without members; its rollback keeps them.
This commit is contained in:
47
chat.go
47
chat.go
@@ -2,10 +2,13 @@ package bm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
chatcontracts "git.golem15.com/golem15/sm-chat-plugin/contracts"
|
||||
chanclasses "git.golem15.com/golem15/sm-chatchannels-plugin/classes"
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/lagoon"
|
||||
"git.golem15.com/jakub/sm-bm-plugin/classes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -24,9 +27,10 @@ const (
|
||||
)
|
||||
|
||||
// registerChat makes jz.bm the host of the BM chat: it registers the bm
|
||||
// context alias on golem15.chat's registry. Without golem15.chat it does
|
||||
// nothing, so jz.bm keeps working (and its tests keep activating) without
|
||||
// the chat plugins; jz.bm does not Require them.
|
||||
// context alias on golem15.chat's registry and installs the channel access
|
||||
// policy. Without golem15.chat it does nothing, so jz.bm keeps working (and
|
||||
// its tests keep activating) without the chat plugins; jz.bm does not
|
||||
// Require them.
|
||||
func (p *Plugin) registerChat(app *backpack.App) error {
|
||||
if app == nil || !app.HasPlugin("golem15.chat") {
|
||||
return nil
|
||||
@@ -35,17 +39,44 @@ func (p *Plugin) registerChat(app *backpack.App) error {
|
||||
if !ok || reg == nil {
|
||||
return errors.New("jz.bm: golem15.chat must be listed before jz.bm in summer.yaml")
|
||||
}
|
||||
return reg.Add(p.ID(), chatcontracts.Registration{Alias: classes.ChatContextType, Loader: p.loadChatContext})
|
||||
if err := reg.Add(p.ID(), chatcontracts.Registration{Alias: classes.ChatContextType, Loader: p.loadChatContext}); err != nil {
|
||||
return err
|
||||
}
|
||||
return p.installChatAccess(app)
|
||||
}
|
||||
|
||||
// loadChatContext is the loader of the bm alias. The only context is id 1;
|
||||
// any other id is unknown, which golem15.chat answers with its not-a-member
|
||||
// error.
|
||||
func (p *Plugin) loadChatContext(_ context.Context, db *gorm.DB, id uint) (chatcontracts.Context, error) {
|
||||
if id != classes.ChatContextID {
|
||||
return nil, nil
|
||||
func (p *Plugin) loadChatContext(ctx context.Context, db *gorm.DB, id uint) (chatcontracts.Context, error) {
|
||||
return classes.ChatContextLoader(p.chatGroup, p.chatModeratorGroup)(ctx, db, id)
|
||||
}
|
||||
|
||||
// installChatAccess installs BM's access policy on golem15.chatchannels'
|
||||
// channel service. The service is published from chatchannels' own
|
||||
// database hook, which runs before this one because chatchannels boots
|
||||
// first; under a database-less boot (tests, route:list) neither runs.
|
||||
func (p *Plugin) installChatAccess(app *backpack.App) error {
|
||||
if app == nil || !app.HasPlugin("golem15.chatchannels") {
|
||||
return nil
|
||||
}
|
||||
return classes.NewChatContext(db, p.chatGroup(), p.chatModeratorGroup()), nil
|
||||
return lagoon.OnDatabase(app, func(_ *sql.DB, _ *gorm.DB) error {
|
||||
svc, ok := app.Lookup[*chanclasses.ChannelService]()
|
||||
if !ok || svc == nil {
|
||||
return errors.New("jz.bm: golem15.chatchannels must be listed before jz.bm in summer.yaml")
|
||||
}
|
||||
svc.SetAccessResolver(classes.NewChatAccess(p.database, p.chatGroup(), nil))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// database returns the published application database, or nil.
|
||||
func (p *Plugin) database() *gorm.DB {
|
||||
if p.app == nil {
|
||||
return nil
|
||||
}
|
||||
db, _ := p.app.Lookup[*gorm.DB]()
|
||||
return db
|
||||
}
|
||||
|
||||
// chatGroup is the code of the user group whose members are in the chat.
|
||||
|
||||
Reference in New Issue
Block a user