@@ -21,6 +21,7 @@ import ( |
| 21 | 21 | "github.com/jackc/pgx/v5/pgtype" |
| 22 | 22 | "github.com/jackc/pgx/v5/pgxpool" |
| 23 | 23 | |
| 24 | + "github.com/tenseleyFlow/shithub/internal/auth/audit" |
| 24 | 25 | "github.com/tenseleyFlow/shithub/internal/auth/throttle" |
| 25 | 26 | issuesdb "github.com/tenseleyFlow/shithub/internal/issues/sqlc" |
| 26 | 27 | mdrender "github.com/tenseleyFlow/shithub/internal/markdown" |
@@ -33,6 +34,12 @@ type Deps struct { |
| 33 | 34 | Pool *pgxpool.Pool |
| 34 | 35 | Limiter *throttle.Limiter |
| 35 | 36 | Logger *slog.Logger |
| 37 | + // Audit is optional; when non-nil, state-changing orchestrator |
| 38 | + // calls (SetState, SetLock, AddComment) record an audit row. The |
| 39 | + // repo lifecycle package writes audit rows directly via deps.Audit; |
| 40 | + // this field ensures issues/PR mutations are equally traceable |
| 41 | + // (S00-S25 audit, M). |
| 42 | + Audit *audit.Recorder |
| 36 | 43 | } |
| 37 | 44 | |
| 38 | 45 | // Errors returned by the orchestrator. Handlers map these to status |
@@ -117,7 +124,7 @@ func Create(ctx context.Context, deps Deps, p CreateParams) (issuesdb.Issue, err |
| 117 | 124 | } |
| 118 | 125 | |
| 119 | 126 | // Render markdown for the cached body html. |
| 120 | | - html, _ := mdrender.RenderHTML([]byte(p.Body)) |
| 127 | + html := renderBodyHTML(ctx, deps, p.Body) |
| 121 | 128 | row.BodyHtmlCached = pgtype.Text{String: html, Valid: html != ""} |
| 122 | 129 | |
| 123 | 130 | if err := q.UpdateIssueTitleBody(ctx, tx, issuesdb.UpdateIssueTitleBodyParams{ |
@@ -178,7 +185,7 @@ func AddComment(ctx context.Context, deps Deps, p CommentCreateParams) (issuesdb |
| 178 | 185 | return issuesdb.IssueComment{}, ErrIssueLocked |
| 179 | 186 | } |
| 180 | 187 | |
| 181 | | - html, _ := mdrender.RenderHTML([]byte(body)) |
| 188 | + html := renderBodyHTML(ctx, deps, body) |
| 182 | 189 | |
| 183 | 190 | tx, err := deps.Pool.Begin(ctx) |
| 184 | 191 | if err != nil { |
@@ -209,6 +216,11 @@ func AddComment(ctx context.Context, deps Deps, p CommentCreateParams) (issuesdb |
| 209 | 216 | return issuesdb.IssueComment{}, err |
| 210 | 217 | } |
| 211 | 218 | committed = true |
| 219 | + if deps.Audit != nil { |
| 220 | + _ = deps.Audit.Record(ctx, deps.Pool, p.AuthorUserID, |
| 221 | + audit.ActionIssueCommentCreated, audit.TargetIssue, p.IssueID, |
| 222 | + map[string]any{"comment_id": c.ID}) |
| 223 | + } |
| 212 | 224 | return c, nil |
| 213 | 225 | } |
| 214 | 226 | |
@@ -262,6 +274,11 @@ func SetState(ctx context.Context, deps Deps, actorUserID, issueID int64, newSta |
| 262 | 274 | return err |
| 263 | 275 | } |
| 264 | 276 | committed = true |
| 277 | + if deps.Audit != nil { |
| 278 | + _ = deps.Audit.Record(ctx, deps.Pool, actorUserID, |
| 279 | + audit.ActionIssueStateChanged, audit.TargetIssue, issueID, |
| 280 | + map[string]any{"state": newState, "reason": reason}) |
| 281 | + } |
| 265 | 282 | return nil |
| 266 | 283 | } |
| 267 | 284 | |
@@ -308,5 +325,25 @@ func SetLock(ctx context.Context, deps Deps, actorUserID, issueID int64, locked |
| 308 | 325 | return err |
| 309 | 326 | } |
| 310 | 327 | committed = true |
| 328 | + if deps.Audit != nil { |
| 329 | + _ = deps.Audit.Record(ctx, deps.Pool, actorUserID, |
| 330 | + audit.ActionIssueLockChanged, audit.TargetIssue, issueID, |
| 331 | + map[string]any{"locked": locked, "reason": reason}) |
| 332 | + } |
| 311 | 333 | return nil |
| 312 | 334 | } |
| 335 | + |
| 336 | +// renderBodyHTML wraps markdown.RenderHTML with a logger-aware error |
| 337 | +// path. Body length is bounded upstream (orchestrator validation + |
| 338 | +// DB CHECK at 65535), so ErrInputTooLarge is structurally impossible |
| 339 | +// here — but if it ever fires, log loudly: it means a precondition |
| 340 | +// somewhere upstream regressed. The audit (S00-S25, M) flagged the |
| 341 | +// `_`-discard pattern as the kind of slop where a real bug could hide. |
| 342 | +func renderBodyHTML(ctx context.Context, deps Deps, body string) string { |
| 343 | + html, err := mdrender.RenderHTML([]byte(body)) |
| 344 | + if err != nil && deps.Logger != nil { |
| 345 | + deps.Logger.WarnContext(ctx, "issues: markdown render failed", |
| 346 | + "error", err, "body_bytes", len(body)) |
| 347 | + } |
| 348 | + return html |
| 349 | +} |