|
|
|
internal/community/config.go raw
| 1 | package community | | 2 | | | 3 | import ( | | 4 | "context" | | 5 | "database/sql" | | 6 | "time" | | 7 | ) | | 8 | | | 9 | // ConfigSnapshot is the community-owned portion of relay configuration. | | 10 | // The type deliberately contains no configport types so the domain owns its | | 11 | // storage contract. | | 12 | type ConfigSnapshot struct { | | 13 | Members []Member | | 14 | Bans []ConfigBan | | 15 | IPBlocks []ConfigIPBlock | | 16 | EventBans []ConfigEventBan | | 17 | KindRules []ConfigKindRule | | 18 | Retention []ConfigRetention | | 19 | } | | 20 | | | 21 | type ConfigBan struct { | | 22 | PubKey string | | 23 | Reason string | | 24 | } | | 25 | | | 26 | type ConfigIPBlock struct { | | 27 | IP string | | 28 | Reason string | | 29 | } | | 30 | | | 31 | type ConfigEventBan struct { | | 32 | ID string | | 33 | Reason string | | 34 | } | | 35 | | | 36 | type ConfigKindRule struct { | | 37 | Kind int | | 38 | Rule string | | 39 | } | | 40 | | | 41 | type ConfigRetention struct { | | 42 | Kind *int | | 43 | Days int | | 44 | } | | 45 | | | 46 | type ConfigMemberRetention struct { | | 47 | PubKey string | | 48 | Days int | | 49 | } | | 50 | | | 51 | // RetentionRules returns the community-owned retention settings. | | 52 | func (s *Service) RetentionRules(ctx context.Context) ([]ConfigRetention, error) { | | 53 | rows, err := s.store.DB().QueryContext(ctx, `SELECT kind,days FROM community_retention ORDER BY kind` ) | | 54 | if err != nil { | | 55 | return nil, err | | 56 | } | | 57 | defer rows.Close() | | 58 | var out []ConfigRetention | | 59 | for rows.Next() { | | 60 | var kind, days int | | 61 | if err := rows.Scan(&kind, &days); err != nil { | | 62 | return nil, err | | 63 | } | | 64 | out = append(out, ConfigRetention{Kind: &kind, Days: days}) | | 65 | } | | 66 | return out, rows.Err() | | 67 | } | | 68 | | | 69 | // MembersForRetention returns members with an explicit retention override. | | 70 | func (s *Service) MembersForRetention(ctx context.Context) ([]ConfigMemberRetention, error) { | | 71 | rows, err := s.store.DB().QueryContext(ctx, `SELECT pubkey,keep_days FROM community_members WHERE keep_days>0 ORDER BY pubkey` ) | | 72 | if err != nil { | | 73 | return nil, err | | 74 | } | | 75 | defer rows.Close() | | 76 | var out []ConfigMemberRetention | | 77 | for rows.Next() { | | 78 | var member ConfigMemberRetention | | 79 | if err := rows.Scan(&member.PubKey, &member.Days); err != nil { | | 80 | return nil, err | | 81 | } | | 82 | out = append(out, member) | | 83 | } | | 84 | return out, rows.Err() | | 85 | } | | 86 | | | 87 | // ExportConfig reads the community-owned configuration tables. | | 88 | func ExportConfig(ctx context.Context, store interface { | | 89 | DB() *sql.DB | | 90 | }) (ConfigSnapshot, error) { | | 91 | var out ConfigSnapshot | | 92 | rows, err := store.DB().QueryContext(ctx, `SELECT pubkey,name,note,role,invited_by,via,created_at,keep_days FROM community_members WHERE role<>'owner' ORDER BY pubkey` ) | | 93 | if err != nil { | | 94 | return out, err | | 95 | } | | 96 | defer rows.Close() | | 97 | for rows.Next() { | | 98 | var member Member | | 99 | if err := rows.Scan(&member.PubKey, &member.Name, &member.Note, &member.Role, &member.InvitedBy, &member.Via, &member.CreatedAt, &member.KeepDays); err != nil { | | 100 | return out, err | | 101 | } | | 102 | out.Members = append(out.Members, member) | | 103 | } | | 104 | if err := rows.Err(); err != nil { | | 105 | return out, err | | 106 | } | | 107 | if err := readConfigRows(ctx, store.DB(), `SELECT pubkey,reason FROM community_bans ORDER BY pubkey` , func(rows *sql.Rows) error { | | 108 | var item ConfigBan | | 109 | if err := rows.Scan(&item.PubKey, &item.Reason); err != nil { | | 110 | return err | | 111 | } | | 112 | out.Bans = append(out.Bans, item) | | 113 | return nil | | 114 | }); err != nil { | | 115 | return out, err | | 116 | } | | 117 | if err := readConfigRows(ctx, store.DB(), `SELECT ip,reason FROM community_ip_blocks ORDER BY ip` , func(rows *sql.Rows) error { | | 118 | var item ConfigIPBlock | | 119 | if err := rows.Scan(&item.IP, &item.Reason); err != nil { | | 120 | return err | | 121 | } | | 122 | out.IPBlocks = append(out.IPBlocks, item) | | 123 | return nil | | 124 | }); err != nil { | | 125 | return out, err | | 126 | } | | 127 | if err := readConfigRows(ctx, store.DB(), `SELECT id,reason FROM community_event_bans ORDER BY id` , func(rows *sql.Rows) error { | | 128 | var item ConfigEventBan | | 129 | if err := rows.Scan(&item.ID, &item.Reason); err != nil { | | 130 | return err | | 131 | } | | 132 | out.EventBans = append(out.EventBans, item) | | 133 | return nil | | 134 | }); err != nil { | | 135 | return out, err | | 136 | } | | 137 | if err := readConfigRows(ctx, store.DB(), `SELECT kind,rule FROM community_kind_rules ORDER BY kind` , func(rows *sql.Rows) error { | | 138 | var item ConfigKindRule | | 139 | if err := rows.Scan(&item.Kind, &item.Rule); err != nil { | | 140 | return err | | 141 | } | | 142 | out.KindRules = append(out.KindRules, item) | | 143 | return nil | | 144 | }); err != nil { | | 145 | return out, err | | 146 | } | | 147 | if err := readConfigRows(ctx, store.DB(), `SELECT kind,days FROM community_retention ORDER BY kind` , func(rows *sql.Rows) error { | | 148 | var kind, days int | | 149 | if err := rows.Scan(&kind, &days); err != nil { | | 150 | return err | | 151 | } | | 152 | if kind < 0 { | | 153 | out.Retention = append(out.Retention, ConfigRetention{Days: days}) | | 154 | return nil | | 155 | } | | 156 | out.Retention = append(out.Retention, ConfigRetention{Kind: &kind, Days: days}) | | 157 | return nil | | 158 | }); err != nil { | | 159 | return out, err | | 160 | } | | 161 | return out, nil | | 162 | } | | 163 | | | 164 | func readConfigRows(ctx context.Context, db *sql.DB, query string, scan func(*sql.Rows) error) error { | | 165 | rows, err := db.QueryContext(ctx, query) | | 166 | if err != nil { | | 167 | return err | | 168 | } | | 169 | defer rows.Close() | | 170 | for rows.Next() { | | 171 | if err := scan(rows); err != nil { | | 172 | return err | | 173 | } | | 174 | } | | 175 | return rows.Err() | | 176 | } | | 177 | | | 178 | // ApplyConfigTx replaces each selected community configuration section inside | | 179 | // the caller-owned transaction. A selected empty section clears that section; | | 180 | // an unselected section is left unchanged. The caller commits or rolls back | | 181 | // the transaction after all related configuration writes succeed. | | 182 | func ApplyConfigTx(ctx context.Context, tx *sql.Tx, c ConfigSnapshot, members, bans, addresses, eventBans, kinds, retention bool) error { | | 183 | now := time.Now().Unix() | | 184 | if members { | | 185 | if _, err := tx.ExecContext(ctx, `DELETE FROM community_members WHERE role<>'owner'` ); err != nil { | | 186 | return err | | 187 | } | | 188 | for _, member := range c.Members { | | 189 | if _, err := tx.ExecContext(ctx, `INSERT INTO community_members(pubkey,name,note,role,invited_by,via,created_at,keep_days) VALUES(?,?,?,?,?,?,?,?)` , member.PubKey, member.Name, member.Note, member.Role, member.InvitedBy, member.Via, now, member.KeepDays); err != nil { | | 190 | return err | | 191 | } | | 192 | } | | 193 | } | | 194 | if bans { | | 195 | if _, err := tx.ExecContext(ctx, `DELETE FROM community_bans` ); err != nil { | | 196 | return err | | 197 | } | | 198 | for _, item := range c.Bans { | | 199 | if item.PubKey != "" { | | 200 | if _, err := tx.ExecContext(ctx, `INSERT INTO community_bans(pubkey,reason,at) VALUES(?,?,?)` , item.PubKey, item.Reason, now); err != nil { | | 201 | return err | | 202 | } | | 203 | } | | 204 | } | | 205 | } | | 206 | if addresses { | | 207 | if _, err := tx.ExecContext(ctx, `DELETE FROM community_ip_blocks` ); err != nil { | | 208 | return err | | 209 | } | | 210 | for _, item := range c.IPBlocks { | | 211 | if item.IP != "" { | | 212 | if _, err := tx.ExecContext(ctx, `INSERT INTO community_ip_blocks(ip,reason,at) VALUES(?,?,?)` , item.IP, item.Reason, now); err != nil { | | 213 | return err | | 214 | } | | 215 | } | | 216 | } | | 217 | } | | 218 | if eventBans { | | 219 | if _, err := tx.ExecContext(ctx, `DELETE FROM community_event_bans` ); err != nil { | | 220 | return err | | 221 | } | | 222 | for _, item := range c.EventBans { | | 223 | if item.ID != "" { | | 224 | if _, err := tx.ExecContext(ctx, `INSERT INTO community_event_bans(id,reason,at) VALUES(?,?,?)` , item.ID, item.Reason, now); err != nil { | | 225 | return err | | 226 | } | | 227 | } | | 228 | } | | 229 | } | | 230 | if kinds { | | 231 | if _, err := tx.ExecContext(ctx, `DELETE FROM community_kind_rules` ); err != nil { | | 232 | return err | | 233 | } | | 234 | for _, item := range c.KindRules { | | 235 | if _, err := tx.ExecContext(ctx, `INSERT INTO community_kind_rules(kind,rule) VALUES(?,?)` , item.Kind, item.Rule); err != nil { | | 236 | return err | | 237 | } | | 238 | } | | 239 | } | | 240 | if retention { | | 241 | if _, err := tx.ExecContext(ctx, `DELETE FROM community_retention` ); err != nil { | | 242 | return err | | 243 | } | | 244 | for _, item := range c.Retention { | | 245 | kind := -1 | | 246 | if item.Kind != nil { | | 247 | kind = *item.Kind | | 248 | } | | 249 | if _, err := tx.ExecContext(ctx, `INSERT INTO community_retention(kind,days) VALUES(?,?)` , kind, item.Days); err != nil { | | 250 | return err | | 251 | } | | 252 | } | | 253 | } | | 254 | return nil | | 255 | } |
|
|
About| owner | c01503e8035f |
|---|
| visibility | public |
|---|
| head | d730679639bf |
|---|
Maintainers- c01503e8035f owner
- ba1b5beed5b5 agent hermes
Clonehttps://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git
|