feat: add API rules and hooks
Add PocketBase hooks for group management and real-time notifications: - Groups collection with owner/members permissions - Team Sessions with group member verification - Invitations with real-time notifications - Helper functions for group ownership and membership checks Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
module gamegroup-hooks
|
||||
|
||||
go 1.21
|
||||
|
||||
require github.com/pocketbase/pocketbase v0.22.4
|
||||
@@ -0,0 +1,144 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
// isGroupMember checks if a user is a member of a group
|
||||
func isGroupMember(app *pocketbase.PocketBase, groupId string, userId string) (bool, error) {
|
||||
group, err := app.Dao().FindRecordById("groups", groupId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
members := group.GetStringSlice("members")
|
||||
for _, member := range members {
|
||||
if member == userId {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// isGroupOwner checks if a user is the owner of a group
|
||||
func isGroupOwner(app *pocketbase.PocketBase, groupId string, userId string) (bool, error) {
|
||||
group, err := app.Dao().FindRecordById("groups", groupId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return group.GetString("owner") == userId, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := pocketbase.New()
|
||||
|
||||
// Groups API Rules
|
||||
app.OnRecordBeforeCreateRequest("groups").Add(func(e *core.RecordCreateEvent) next func() {
|
||||
// Set the owner to the current user
|
||||
e.Record.Set("owner", e.HttpContext.Request().Context().Value("user_id"))
|
||||
// Initialize members array with the owner
|
||||
e.Record.Set("members", []string{e.Record.GetString("owner")})
|
||||
return next
|
||||
})
|
||||
|
||||
app.OnRecordBeforeUpdateRequest("groups").Add(func(e *core.RecordUpdateEvent) next func() {
|
||||
userId := e.HttpContext.Request().Context().Value("user_id").(string)
|
||||
|
||||
// Only owner can update the group
|
||||
isOwner, err := isGroupOwner(app, e.Record.Id, userId)
|
||||
if err != nil || !isOwner {
|
||||
e.HttpContext.Response().WriteHeader(403)
|
||||
return nil
|
||||
}
|
||||
return next
|
||||
})
|
||||
|
||||
app.OnRecordBeforeDeleteRequest("groups").Add(func(e *core.RecordDeleteEvent) next func() {
|
||||
userId := e.HttpContext.Request().Context().Value("user_id").(string)
|
||||
|
||||
// Only owner can delete the group
|
||||
isOwner, err := isGroupOwner(app, e.Record.Id, userId)
|
||||
if err != nil || !isOwner {
|
||||
e.HttpContext.Response().WriteHeader(403)
|
||||
return nil
|
||||
}
|
||||
return next
|
||||
})
|
||||
|
||||
// Team Sessions API Rules
|
||||
app.OnRecordBeforeCreateRequest("team_sessions").Add(func(e *core.RecordCreateEvent) next func() {
|
||||
userId := e.HttpContext.Request().Context().Value("user_id").(string)
|
||||
groupId := e.Record.GetString("group")
|
||||
|
||||
// Check if user is a member of the group
|
||||
isMember, err := isGroupMember(app, groupId, userId)
|
||||
if err != nil || !isMember {
|
||||
e.HttpContext.Response().WriteHeader(403)
|
||||
return nil
|
||||
}
|
||||
return next
|
||||
})
|
||||
|
||||
// Invitations API Rules
|
||||
app.OnRecordBeforeCreateRequest("invitations").Add(func(e *core.RecordCreateEvent) next func() {
|
||||
userId := e.HttpContext.Request().Context().Value("user_id").(string)
|
||||
groupId := e.Record.GetString("group")
|
||||
|
||||
// Only group owner can create invitations
|
||||
isOwner, err := isGroupOwner(app, groupId, userId)
|
||||
if err != nil || !isOwner {
|
||||
e.HttpContext.Response().WriteHeader(403)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set status to pending
|
||||
e.Record.Set("status", "pending")
|
||||
return next
|
||||
})
|
||||
|
||||
app.OnRecordAfterCreateRequest("invitations").Add(func(e *core.RecordCreateEvent) next func() {
|
||||
// Send real-time notification to the invited user
|
||||
message := map[string]interface{}{
|
||||
"action": "invitation",
|
||||
"data": map[string]interface{}{
|
||||
"id": e.Record.Id,
|
||||
"group": e.Record.GetString("group"),
|
||||
"invited_by": e.Record.GetString("invited_by"),
|
||||
"status": e.Record.GetString("status"),
|
||||
"created": e.Record.Created.Time(),
|
||||
},
|
||||
}
|
||||
|
||||
// Broadcast to the invited user's channel
|
||||
if err := app.Realtime().Broadcast("invitations", message); err != nil {
|
||||
log.Printf("Error broadcasting invitation: %v", err)
|
||||
}
|
||||
return next
|
||||
})
|
||||
|
||||
// Real-time subscription rules
|
||||
app.OnRecordAfterAuthWithTokenRequest().Add(func(e *core.RecordAuthEvent) next func() {
|
||||
// Subscribe to invitations channel
|
||||
e.HttpContext.Request().Context().Set("realtime_subscriptions", []string{
|
||||
"invitations",
|
||||
"groups:" + e.Record.Id,
|
||||
"team_sessions",
|
||||
})
|
||||
return next
|
||||
})
|
||||
|
||||
// Custom API endpoint for accepting invitations
|
||||
app.OnServe().Bind(&ServeEvent{
|
||||
App: app,
|
||||
})
|
||||
}
|
||||
|
||||
type ServeEvent struct {
|
||||
App *pocketbase.PocketBase
|
||||
}
|
||||
Reference in New Issue
Block a user