cleanup message retention from orion api
This commit is contained in:
@@ -127,7 +127,6 @@ func main() {
|
|||||||
mux.Handle("GET /networks", withAuth(h.ListNetworks))
|
mux.Handle("GET /networks", withAuth(h.ListNetworks))
|
||||||
mux.Handle("GET /networks/{id}", withAuth(h.GetNetwork))
|
mux.Handle("GET /networks/{id}", withAuth(h.GetNetwork))
|
||||||
mux.Handle("POST /networks/{id}/members", withAuth(h.AddMembersToNetwork))
|
mux.Handle("POST /networks/{id}/members", withAuth(h.AddMembersToNetwork))
|
||||||
mux.Handle("PUT /networks/{id}/message-retention", withAuth(h.SetMessageRetentionHours))
|
|
||||||
// mux.Handle("DELETE /networks/{id}/members/{humanId}", withAuth(h.RemoveMemberFromNetwork))
|
// mux.Handle("DELETE /networks/{id}/members/{humanId}", withAuth(h.RemoveMemberFromNetwork))
|
||||||
|
|
||||||
// Network Invitations
|
// Network Invitations
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ type Network struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
AdminHuman Human `json:"admin_human"`
|
AdminHuman Human `json:"admin_human"`
|
||||||
Humans []Human `json:"humans"`
|
Humans []Human `json:"humans"`
|
||||||
MessageRetentionHours int `json:"message_retention_hours"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,14 +90,6 @@ type AddMembersToNetworkRequest struct {
|
|||||||
EmailAddresses []string `json:"email_addresses"`
|
EmailAddresses []string `json:"email_addresses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOpenStreamCapacityRequest struct {
|
|
||||||
Capacity int `json:"capacity"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SetMessageRetentionHoursRequest struct {
|
|
||||||
Hours int `json:"hours"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MembersRequest struct {
|
type MembersRequest struct {
|
||||||
Emails []string `json:"emails"`
|
Emails []string `json:"emails"`
|
||||||
}
|
}
|
||||||
@@ -675,72 +666,6 @@ func (h *Handler) RevokeInvitation(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMessageRetentionHours updates the message retention window for a network (admin-only)
|
|
||||||
func (h *Handler) SetMessageRetentionHours(w http.ResponseWriter, r *http.Request) {
|
|
||||||
humanId, ok := middleware.HumanIdFromContext(r.Context())
|
|
||||||
if !ok {
|
|
||||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
networkID := r.PathValue("id")
|
|
||||||
if networkID == "" {
|
|
||||||
http.Error(w, "network id is required", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch network to verify admin
|
|
||||||
net, err := h.networkSvc.GetByID(r.Context(), networkID)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, network.ErrNotFound) {
|
|
||||||
http.Error(w, "network not found", http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
slog.Error("failed to get network", "error", err, "network_id", networkID)
|
|
||||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if net.AdminHumanId != humanId {
|
|
||||||
http.Error(w, "only the network admin can change this setting", http.StatusForbidden)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var req SetMessageRetentionHoursRequest
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
||||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := h.networkSvc.SetMessageRetentionHours(r.Context(), networkID, req.Hours); err != nil {
|
|
||||||
if errors.Is(err, network.ErrInvalidRetentionHours) {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
slog.Error("failed to set message retention hours", "error", err, "network_id", networkID)
|
|
||||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return updated network
|
|
||||||
updatedNet, err := h.networkSvc.GetByID(r.Context(), networkID)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("failed to get network after update", "error", err, "network_id", networkID)
|
|
||||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := h.networkToDTO(r.Context(), updatedNet)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("failed to convert network to DTO", "error", err, "network_id", networkID)
|
|
||||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(w).Encode(resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DownloadParticleMedia returns a fresh signed download URL for media/file particles
|
// DownloadParticleMedia returns a fresh signed download URL for media/file particles
|
||||||
func (h *Handler) DownloadParticleMedia(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) DownloadParticleMedia(w http.ResponseWriter, r *http.Request) {
|
||||||
_, ok := middleware.EmailFromContext(r.Context())
|
_, ok := middleware.EmailFromContext(r.Context())
|
||||||
@@ -1056,7 +981,6 @@ func (h *Handler) networkToDTO(ctx context.Context, n *network.Network) (Network
|
|||||||
Name: n.Name,
|
Name: n.Name,
|
||||||
AdminHuman: humanToDTO(adminHuman),
|
AdminHuman: humanToDTO(adminHuman),
|
||||||
Humans: humans,
|
Humans: humans,
|
||||||
MessageRetentionHours: n.MessageRetentionHours,
|
|
||||||
CreatedAt: n.CreatedAt,
|
CreatedAt: n.CreatedAt,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ type Network struct {
|
|||||||
Name string
|
Name string
|
||||||
AdminHumanId string
|
AdminHumanId string
|
||||||
MemberHumanIds []string
|
MemberHumanIds []string
|
||||||
OpenStreamCapacity int
|
|
||||||
OpenStreamCount int
|
|
||||||
MessageRetentionHours int
|
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ type repository interface {
|
|||||||
getMemberHumanIds(ctx context.Context, networkID string) ([]string, error)
|
getMemberHumanIds(ctx context.Context, networkID string) ([]string, error)
|
||||||
getNetworksForHuman(ctx context.Context, humanId string) ([]*Network, error)
|
getNetworksForHuman(ctx context.Context, humanId string) ([]*Network, error)
|
||||||
isMember(ctx context.Context, networkID, humanId string) (bool, error)
|
isMember(ctx context.Context, networkID, humanId string) (bool, error)
|
||||||
updateMessageRetentionHours(ctx context.Context, id string, hours int) error
|
|
||||||
|
|
||||||
// Invitations
|
// Invitations
|
||||||
createInvitation(ctx context.Context, networkID, email string) error
|
createInvitation(ctx context.Context, networkID, email string) error
|
||||||
@@ -61,9 +60,9 @@ func (r *repositoryImpl) create(ctx context.Context, name, adminHumanId string)
|
|||||||
var n Network
|
var n Network
|
||||||
err = r.pool.QueryRow(ctx,
|
err = r.pool.QueryRow(ctx,
|
||||||
`INSERT INTO networks (id, name, admin_human_id) VALUES ($1, $2, $3)
|
`INSERT INTO networks (id, name, admin_human_id) VALUES ($1, $2, $3)
|
||||||
RETURNING id, name, admin_human_id, open_stream_capacity, open_stream_count, message_retention_hours, created_at`,
|
RETURNING id, name, admin_human_id, created_at`,
|
||||||
id.String(), name, adminHumanId,
|
id.String(), name, adminHumanId,
|
||||||
).Scan(&n.ID, &n.Name, &n.AdminHumanId, &n.OpenStreamCapacity, &n.OpenStreamCount, &n.MessageRetentionHours, &n.CreatedAt)
|
).Scan(&n.ID, &n.Name, &n.AdminHumanId, &n.CreatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -75,9 +74,9 @@ func (r *repositoryImpl) create(ctx context.Context, name, adminHumanId string)
|
|||||||
func (r *repositoryImpl) getByID(ctx context.Context, id string) (*Network, error) {
|
func (r *repositoryImpl) getByID(ctx context.Context, id string) (*Network, error) {
|
||||||
var n Network
|
var n Network
|
||||||
err := r.pool.QueryRow(ctx,
|
err := r.pool.QueryRow(ctx,
|
||||||
`SELECT id, name, admin_human_id, open_stream_capacity, open_stream_count, message_retention_hours, created_at FROM networks WHERE id = $1`,
|
`SELECT id, name, admin_human_id, created_at FROM networks WHERE id = $1`,
|
||||||
id,
|
id,
|
||||||
).Scan(&n.ID, &n.Name, &n.AdminHumanId, &n.OpenStreamCapacity, &n.OpenStreamCount, &n.MessageRetentionHours, &n.CreatedAt)
|
).Scan(&n.ID, &n.Name, &n.AdminHumanId, &n.CreatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, pgx.ErrNoRows) {
|
if errors.Is(err, pgx.ErrNoRows) {
|
||||||
return nil, errNotFound
|
return nil, errNotFound
|
||||||
@@ -158,7 +157,7 @@ func (r *repositoryImpl) getMemberHumanIds(ctx context.Context, networkID string
|
|||||||
|
|
||||||
func (r *repositoryImpl) getNetworksForHuman(ctx context.Context, humanId string) ([]*Network, error) {
|
func (r *repositoryImpl) getNetworksForHuman(ctx context.Context, humanId string) ([]*Network, error) {
|
||||||
rows, err := r.pool.Query(ctx,
|
rows, err := r.pool.Query(ctx,
|
||||||
`SELECT n.id, n.name, n.admin_human_id, n.open_stream_capacity, n.open_stream_count, n.message_retention_hours, n.created_at
|
`SELECT n.id, n.name, n.admin_human_id, n.created_at
|
||||||
FROM networks n
|
FROM networks n
|
||||||
WHERE n.admin_human_id = $1
|
WHERE n.admin_human_id = $1
|
||||||
OR EXISTS (SELECT 1 FROM network_members nm WHERE nm.network_id = n.id AND nm.human_id = $1)`,
|
OR EXISTS (SELECT 1 FROM network_members nm WHERE nm.network_id = n.id AND nm.human_id = $1)`,
|
||||||
@@ -172,7 +171,7 @@ func (r *repositoryImpl) getNetworksForHuman(ctx context.Context, humanId string
|
|||||||
var networks []*Network
|
var networks []*Network
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var n Network
|
var n Network
|
||||||
if err := rows.Scan(&n.ID, &n.Name, &n.AdminHumanId, &n.OpenStreamCapacity, &n.OpenStreamCount, &n.MessageRetentionHours, &n.CreatedAt); err != nil {
|
if err := rows.Scan(&n.ID, &n.Name, &n.AdminHumanId, &n.CreatedAt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
networks = append(networks, &n)
|
networks = append(networks, &n)
|
||||||
@@ -269,17 +268,3 @@ func (r *repositoryImpl) deleteInvitation(ctx context.Context, networkID, email
|
|||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repositoryImpl) updateMessageRetentionHours(ctx context.Context, id string, hours int) error {
|
|
||||||
result, err := r.pool.Exec(ctx,
|
|
||||||
`UPDATE networks SET message_retention_hours = $1 WHERE id = $2`,
|
|
||||||
hours, id,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if result.RowsAffected() == 0 {
|
|
||||||
return errNotFound
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ type Service interface {
|
|||||||
RemoveMember(ctx context.Context, networkID, humanId string) error
|
RemoveMember(ctx context.Context, networkID, humanId string) error
|
||||||
ListForHuman(ctx context.Context, humanId string) ([]*Network, error)
|
ListForHuman(ctx context.Context, humanId string) ([]*Network, error)
|
||||||
IsMember(ctx context.Context, networkID, humanId string) (bool, error)
|
IsMember(ctx context.Context, networkID, humanId string) (bool, error)
|
||||||
// SetMessageRetentionHours sets how long messages remain visible (24–336 hours).
|
|
||||||
SetMessageRetentionHours(ctx context.Context, id string, hours int) error
|
|
||||||
|
|
||||||
// Invitations (email-based, for users who haven't registered yet)
|
// Invitations (email-based, for users who haven't registered yet)
|
||||||
InviteByEmail(ctx context.Context, networkID string, emails []string) error
|
InviteByEmail(ctx context.Context, networkID string, emails []string) error
|
||||||
@@ -118,17 +116,6 @@ func (s *serviceImpl) IsMember(ctx context.Context, networkID, humanId string) (
|
|||||||
return s.repo.isMember(ctx, networkID, humanId)
|
return s.repo.isMember(ctx, networkID, humanId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceImpl) SetMessageRetentionHours(ctx context.Context, id string, hours int) error {
|
|
||||||
if hours < 24 || hours > 336 {
|
|
||||||
return ErrInvalidRetentionHours
|
|
||||||
}
|
|
||||||
err := s.repo.updateMessageRetentionHours(ctx, id, hours)
|
|
||||||
if errors.Is(err, errNotFound) {
|
|
||||||
return ErrNotFound
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invitation methods
|
// Invitation methods
|
||||||
|
|
||||||
func (s *serviceImpl) InviteByEmail(ctx context.Context, networkID string, emails []string) error {
|
func (s *serviceImpl) InviteByEmail(ctx context.Context, networkID string, emails []string) error {
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE networks
|
|
||||||
DROP COLUMN IF EXISTS message_retention_hours;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE networks
|
|
||||||
ADD COLUMN message_retention_hours INTEGER NOT NULL DEFAULT 24;
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE networks
|
||||||
|
ADD COLUMN open_stream_capacity INTEGER NOT NULL DEFAULT 5,
|
||||||
|
ADD COLUMN open_stream_count INTEGER NOT NULL DEFAULT 0;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE networks
|
||||||
|
DROP COLUMN IF EXISTS open_stream_capacity,
|
||||||
|
DROP COLUMN IF EXISTS open_stream_count;
|
||||||
Reference in New Issue
Block a user