fix: route workbench requests to tenant database

This commit is contained in:
richjohnrambo
2026-09-17 18:32:26 +08:00
parent dd5986d57a
commit 9cfeb5f2c7
3 changed files with 224 additions and 72 deletions
+73 -68
View File
@@ -72,7 +72,7 @@ func (a *API) Healthz(w http.ResponseWriter, r *http.Request) {
writeSuccess(w, r, map[string]any{"status": "ok", "service": "im_workbench"})
}
type handlerFunc func(http.ResponseWriter, *http.Request, *auth.Principal)
type handlerFunc func(http.ResponseWriter, *http.Request, *auth.Principal, *store.Store)
func (a *API) withAuth(next handlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -86,22 +86,27 @@ func (a *API) withAuth(next handlerFunc) http.HandlerFunc {
}
return
}
next(w, r, principal)
st, err := a.store.ForTenant(r.Context(), principal.TenantID, principal.TenantCode)
if err != nil {
writeStoreError(w, r, err)
return
}
next(w, r, principal, st)
}
}
func (a *API) workbench(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) workbench(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
}
modules, version, err := a.store.WorkbenchModules(r.Context(), p.UserID)
modules, version, err := st.WorkbenchModules(r.Context(), p.UserID)
if err != nil {
writeStoreError(w, r, err)
return
}
items := workbenchModulePayloads(modules)
if err := a.populateUnread(r, p, items); err != nil {
if err := a.populateUnread(st, r, p, items); err != nil {
writeStoreError(w, r, err)
return
}
@@ -122,7 +127,7 @@ func (a *API) workbench(w http.ResponseWriter, r *http.Request, p *auth.Principa
})
}
func (a *API) organizationDepartments(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) organizationDepartments(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
@@ -131,7 +136,7 @@ func (a *API) organizationDepartments(w http.ResponseWriter, r *http.Request, p
if parentID == "" {
parentID = "root"
}
items, next, err := a.store.Departments(r.Context(), p.UserID, parentID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.Departments(r.Context(), p.UserID, parentID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -139,7 +144,7 @@ func (a *API) organizationDepartments(w http.ResponseWriter, r *http.Request, p
writeSuccess(w, r, pagePayload(organizationDepartmentPayloads(items), next))
}
func (a *API) organizationDepartmentResource(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) organizationDepartmentResource(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
departmentID, tail, ok := resourceTail(r.URL.Path, "/v1/workbench/organization/departments/")
if !ok || tail != "members" {
writeError(w, r, http.StatusNotFound, 30004, reasonMalformedRequest, "organization route not found", nil)
@@ -149,7 +154,7 @@ func (a *API) organizationDepartmentResource(w http.ResponseWriter, r *http.Requ
methodNotAllowed(w, r)
return
}
items, next, err := a.store.DepartmentMembers(r.Context(), p.UserID, departmentID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.DepartmentMembers(r.Context(), p.UserID, departmentID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -159,13 +164,13 @@ func (a *API) organizationDepartmentResource(w http.ResponseWriter, r *http.Requ
writeSuccess(w, r, payload)
}
func (a *API) organizationMembers(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) organizationMembers(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
}
query := firstNonEmpty(r.URL.Query().Get("q"), r.URL.Query().Get("keyword"))
items, next, err := a.store.Members(r.Context(), p.UserID, r.URL.Query().Get("department_id"), strings.TrimSpace(query), r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.Members(r.Context(), p.UserID, r.URL.Query().Get("department_id"), strings.TrimSpace(query), r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -175,7 +180,7 @@ func (a *API) organizationMembers(w http.ResponseWriter, r *http.Request, p *aut
writeSuccess(w, r, payload)
}
func (a *API) organizationSearch(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) organizationSearch(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
@@ -185,12 +190,12 @@ func (a *API) organizationSearch(w http.ResponseWriter, r *http.Request, p *auth
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "q"})
return
}
departments, _, err := a.store.Departments(r.Context(), p.UserID, "root", "", limit(r, 10, 50))
departments, _, err := st.Departments(r.Context(), p.UserID, "root", "", limit(r, 10, 50))
if err != nil {
writeStoreError(w, r, err)
return
}
members, _, err := a.store.Members(r.Context(), p.UserID, "", query, "", limit(r, 20, 100))
members, _, err := st.Members(r.Context(), p.UserID, "", query, "", limit(r, 20, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -207,7 +212,7 @@ func (a *API) organizationSearch(w http.ResponseWriter, r *http.Request, p *auth
writeSuccess(w, r, map[string]any{"items": items, "next_cursor": "", "has_more": false})
}
func (a *API) attendanceConfig(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) attendanceConfig(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
@@ -217,7 +222,7 @@ func (a *API) attendanceConfig(w http.ResponseWriter, r *http.Request, p *auth.P
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "date"})
return
}
cfg, err := a.store.AttendanceConfig(r.Context(), p.UserID, date)
cfg, err := st.AttendanceConfig(r.Context(), p.UserID, date)
if err != nil {
writeStoreError(w, r, err)
return
@@ -225,12 +230,12 @@ func (a *API) attendanceConfig(w http.ResponseWriter, r *http.Request, p *auth.P
writeSuccess(w, r, attendanceConfigPayload(cfg))
}
func (a *API) attendanceRecords(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) attendanceRecords(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
}
records, next, stats, err := a.store.AttendanceRecords(r.Context(), p.UserID, store.AttendanceListOptions{
records, next, stats, err := st.AttendanceRecords(r.Context(), p.UserID, store.AttendanceListOptions{
Scope: r.URL.Query().Get("scope"),
From: r.URL.Query().Get("from"),
To: r.URL.Query().Get("to"),
@@ -250,7 +255,7 @@ func (a *API) attendanceRecords(w http.ResponseWriter, r *http.Request, p *auth.
writeSuccess(w, r, payload)
}
func (a *API) attendanceCheckins(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) attendanceCheckins(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodPost {
methodNotAllowed(w, r)
return
@@ -316,7 +321,7 @@ func (a *API) attendanceCheckins(w http.ResponseWriter, r *http.Request, p *auth
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "device"})
return
}
record, checkin, err := a.store.CreateAttendanceCheckin(r.Context(), p.UserID, "attc_"+randomToken(12), store.AttendanceCheckinRequest{
record, checkin, err := st.CreateAttendanceCheckin(r.Context(), p.UserID, "attc_"+randomToken(12), store.AttendanceCheckinRequest{
Date: date, Action: action, RuleID: ruleID, ShiftID: shiftID,
ClientTime: clientTime, Location: location, Wifi: wifi, Device: device, IdempotencyKey: idempotencyKey,
})
@@ -332,10 +337,10 @@ func (a *API) attendanceCheckins(w http.ResponseWriter, r *http.Request, p *auth
writeSuccess(w, r, map[string]any{"record": attendanceRecordPayload(record), "checkin": attendanceCheckinPayload(*checkin)})
}
func (a *API) attendanceAppeals(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) attendanceAppeals(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
switch r.Method {
case http.MethodGet:
items, next, err := a.store.AttendanceAppeals(r.Context(), p.UserID, r.URL.Query().Get("status"), r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.AttendanceAppeals(r.Context(), p.UserID, r.URL.Query().Get("status"), r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -367,7 +372,7 @@ func (a *API) attendanceAppeals(w http.ResponseWriter, r *http.Request, p *auth.
targetTime = &parsed
}
attachments, _ := json.Marshal(body.AttachmentFileIDs)
appeal, err := a.store.CreateAttendanceAppeal(r.Context(), p.UserID, store.AttendanceAppeal{
appeal, err := st.CreateAttendanceAppeal(r.Context(), p.UserID, store.AttendanceAppeal{
AppealID: "atta_" + randomToken(12), RecordID: strings.TrimSpace(body.RecordID), UserID: p.UserID, AppealType: strings.TrimSpace(body.AppealType),
Reason: strings.TrimSpace(body.Reason), TargetAction: strings.TrimSpace(body.TargetAction), TargetTime: targetTime, AttachmentFileIDs: string(attachments),
}, idempotencyKey)
@@ -386,10 +391,10 @@ func (a *API) attendanceAppeals(w http.ResponseWriter, r *http.Request, p *auth.
}
}
func (a *API) meetings(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) meetings(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
switch r.Method {
case http.MethodGet:
items, next, err := a.store.Meetings(r.Context(), p.UserID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.Meetings(r.Context(), p.UserID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -423,7 +428,7 @@ func (a *API) meetings(w http.ResponseWriter, r *http.Request, p *auth.Principal
return
}
reminders, _ := json.Marshal(body.ReminderMinutes)
meeting, err := a.store.CreateMeeting(r.Context(), p.UserID, store.Meeting{
meeting, err := st.CreateMeeting(r.Context(), p.UserID, store.Meeting{
MeetingID: "mtg_" + randomToken(12), RecorderID: int64(body.RecorderUserID), Title: strings.TrimSpace(body.Title), StartAt: startAt, EndAt: endAt,
Location: strings.TrimSpace(body.Location), ReminderMinutes: string(reminders), IdempotencyKey: idempotencyKey,
}, parseUserIDList(body.AttendeeUserIDs))
@@ -443,7 +448,7 @@ func (a *API) meetings(w http.ResponseWriter, r *http.Request, p *auth.Principal
}
}
func (a *API) meetingResource(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) meetingResource(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
meetingID, tail, ok := resourceTail(r.URL.Path, "/v1/workbench/meetings/")
if !ok {
writeError(w, r, http.StatusNotFound, 30004, reasonMalformedRequest, "meeting route not found", nil)
@@ -451,7 +456,7 @@ func (a *API) meetingResource(w http.ResponseWriter, r *http.Request, p *auth.Pr
}
switch {
case tail == "" && r.Method == http.MethodGet:
meeting, err := a.store.Meeting(r.Context(), p.UserID, meetingID)
meeting, err := st.Meeting(r.Context(), p.UserID, meetingID)
if err != nil {
writeStoreError(w, r, err)
return
@@ -466,7 +471,7 @@ func (a *API) meetingResource(w http.ResponseWriter, r *http.Request, p *auth.Pr
if !decodeBody(w, r, &body) {
return
}
meeting, err := a.store.UpdateMeeting(r.Context(), p.UserID, meetingID, store.MeetingPatch{Title: body.Title, RecorderID: body.RecorderUserID.ptr(), Version: body.Version})
meeting, err := st.UpdateMeeting(r.Context(), p.UserID, meetingID, store.MeetingPatch{Title: body.Title, RecorderID: body.RecorderUserID.ptr(), Version: body.Version})
if err != nil {
writeStoreError(w, r, err)
return
@@ -478,7 +483,7 @@ func (a *API) meetingResource(w http.ResponseWriter, r *http.Request, p *auth.Pr
}
writeSuccess(w, r, meetingPayload(*meeting, p.UserID))
case tail == "cancel" && r.Method == http.MethodPost:
meeting, err := a.store.CancelMeeting(r.Context(), p.UserID, meetingID)
meeting, err := st.CancelMeeting(r.Context(), p.UserID, meetingID)
if err != nil {
writeStoreError(w, r, err)
return
@@ -497,7 +502,7 @@ func (a *API) meetingResource(w http.ResponseWriter, r *http.Request, p *auth.Pr
if !decodeBody(w, r, &body) {
return
}
meeting, err := a.store.RespondMeeting(r.Context(), p.UserID, meetingID, strings.TrimSpace(body.Response), body.Version)
meeting, err := st.RespondMeeting(r.Context(), p.UserID, meetingID, strings.TrimSpace(body.Response), body.Version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -509,22 +514,22 @@ func (a *API) meetingResource(w http.ResponseWriter, r *http.Request, p *auth.Pr
}
writeSuccess(w, r, meetingPayload(*meeting, p.UserID))
case tail == "notes" && r.Method == http.MethodGet:
a.meetingNotesDetail(w, r, p, meetingID)
a.meetingNotesDetail(w, r, p, st, meetingID)
case tail == "notes/files" && r.Method == http.MethodPost:
a.meetingNoteFiles(w, r, p, meetingID)
a.meetingNoteFiles(w, r, p, st, meetingID)
case strings.HasPrefix(tail, "notes/files/"):
a.meetingNoteFileResource(w, r, p, meetingID, strings.TrimPrefix(tail, "notes/files/"))
a.meetingNoteFileResource(w, r, p, st, meetingID, strings.TrimPrefix(tail, "notes/files/"))
default:
methodNotAllowed(w, r)
}
}
func (a *API) meetingNotes(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) meetingNotes(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
}
items, next, err := a.store.MeetingNotes(r.Context(), p.UserID, r.URL.Query().Get("query"), r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.MeetingNotes(r.Context(), p.UserID, r.URL.Query().Get("query"), r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -532,13 +537,13 @@ func (a *API) meetingNotes(w http.ResponseWriter, r *http.Request, p *auth.Princ
writeSuccess(w, r, pagePayload(meetingNoteListPayloads(items, p.UserID), next))
}
func (a *API) meetingNotesDetail(w http.ResponseWriter, r *http.Request, p *auth.Principal, meetingID string) {
meeting, err := a.store.Meeting(r.Context(), p.UserID, meetingID)
func (a *API) meetingNotesDetail(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store, meetingID string) {
meeting, err := st.Meeting(r.Context(), p.UserID, meetingID)
if err != nil {
writeStoreError(w, r, err)
return
}
files, err := a.store.MeetingNoteFiles(r.Context(), p.UserID, meetingID)
files, err := st.MeetingNoteFiles(r.Context(), p.UserID, meetingID)
if err != nil {
writeStoreError(w, r, err)
return
@@ -546,7 +551,7 @@ func (a *API) meetingNotesDetail(w http.ResponseWriter, r *http.Request, p *auth
writeSuccess(w, r, meetingNotesDetailPayload(*meeting, files, p.UserID))
}
func (a *API) meetingNoteFiles(w http.ResponseWriter, r *http.Request, p *auth.Principal, meetingID string) {
func (a *API) meetingNoteFiles(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store, meetingID string) {
idempotencyKey, ok := requireIdempotencyKey(w, r)
if !ok {
return
@@ -557,7 +562,7 @@ func (a *API) meetingNoteFiles(w http.ResponseWriter, r *http.Request, p *auth.P
if !decodeBody(w, r, &body) {
return
}
file, err := a.store.CreateMeetingNoteFile(r.Context(), p.UserID, meetingID, "mnote_"+randomToken(12), strings.TrimSpace(body.FileID), idempotencyKey)
file, err := st.CreateMeetingNoteFile(r.Context(), p.UserID, meetingID, "mnote_"+randomToken(12), strings.TrimSpace(body.FileID), idempotencyKey)
if err != nil {
writeStoreError(w, r, err)
return
@@ -570,7 +575,7 @@ func (a *API) meetingNoteFiles(w http.ResponseWriter, r *http.Request, p *auth.P
writeSuccess(w, r, meetingNoteFilePayload(*file, p.UserID))
}
func (a *API) meetingNoteFileResource(w http.ResponseWriter, r *http.Request, p *auth.Principal, meetingID, noteFileID string) {
func (a *API) meetingNoteFileResource(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store, meetingID, noteFileID string) {
idempotencyKey, ok := requireIdempotencyKey(w, r)
if !ok {
return
@@ -584,7 +589,7 @@ func (a *API) meetingNoteFileResource(w http.ResponseWriter, r *http.Request, p
if !decodeBody(w, r, &body) {
return
}
file, err := a.store.ReplaceMeetingNoteFile(r.Context(), p.UserID, meetingID, noteFileID, strings.TrimSpace(body.FileID), idempotencyKey, body.Version)
file, err := st.ReplaceMeetingNoteFile(r.Context(), p.UserID, meetingID, noteFileID, strings.TrimSpace(body.FileID), idempotencyKey, body.Version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -601,7 +606,7 @@ func (a *API) meetingNoteFileResource(w http.ResponseWriter, r *http.Request, p
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "version"})
return
}
file, err := a.store.DeleteMeetingNoteFile(r.Context(), p.UserID, meetingID, noteFileID, idempotencyKey, version)
file, err := st.DeleteMeetingNoteFile(r.Context(), p.UserID, meetingID, noteFileID, idempotencyKey, version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -617,10 +622,10 @@ func (a *API) meetingNoteFileResource(w http.ResponseWriter, r *http.Request, p
}
}
func (a *API) dailyReports(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) dailyReports(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
switch r.Method {
case http.MethodGet:
items, next, err := a.store.DailyReports(r.Context(), p.UserID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.DailyReports(r.Context(), p.UserID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -638,7 +643,7 @@ func (a *API) dailyReports(w http.ResponseWriter, r *http.Request, p *auth.Princ
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", map[string]any{"field": "report_date"})
return
}
report, err := a.store.CreateDailyDraft(r.Context(), p.UserID, "dr_"+randomToken(12), date)
report, err := st.CreateDailyDraft(r.Context(), p.UserID, "dr_"+randomToken(12), date)
if err != nil {
writeStoreError(w, r, err)
return
@@ -654,7 +659,7 @@ func (a *API) dailyReports(w http.ResponseWriter, r *http.Request, p *auth.Princ
}
}
func (a *API) dailyReportResource(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) dailyReportResource(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
reportID, tail, ok := resourceTail(r.URL.Path, "/v1/workbench/reports/daily/")
if !ok {
writeError(w, r, http.StatusNotFound, 30004, reasonMalformedRequest, "daily report route not found", nil)
@@ -662,7 +667,7 @@ func (a *API) dailyReportResource(w http.ResponseWriter, r *http.Request, p *aut
}
switch {
case tail == "" && r.Method == http.MethodGet:
report, err := a.store.DailyReport(r.Context(), p.UserID, reportID)
report, err := st.DailyReport(r.Context(), p.UserID, reportID)
if err != nil {
writeStoreError(w, r, err)
return
@@ -682,7 +687,7 @@ func (a *API) dailyReportResource(w http.ResponseWriter, r *http.Request, p *aut
if !decodeBody(w, r, &body) {
return
}
report, err := a.store.SaveDailyDraft(r.Context(), p.UserID, reportID, body.Content, body.Attachments, body.Version)
report, err := st.SaveDailyDraft(r.Context(), p.UserID, reportID, body.Content, body.Attachments, body.Version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -700,7 +705,7 @@ func (a *API) dailyReportResource(w http.ResponseWriter, r *http.Request, p *aut
if !decodeBody(w, r, &body) {
return
}
report, err := a.store.SubmitDaily(r.Context(), p.UserID, reportID, body.Version)
report, err := st.SubmitDaily(r.Context(), p.UserID, reportID, body.Version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -711,7 +716,7 @@ func (a *API) dailyReportResource(w http.ResponseWriter, r *http.Request, p *aut
}
}
func (a *API) dailyReportStatistics(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) dailyReportStatistics(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
@@ -719,7 +724,7 @@ func (a *API) dailyReportStatistics(w http.ResponseWriter, r *http.Request, p *a
writeSuccess(w, r, map[string]any{"expected_count": 0, "submitted_count": 0, "missing_count": 0, "scope": map[string]any{"department_id": r.URL.Query().Get("department_id")}})
}
func (a *API) dailyReportReminders(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) dailyReportReminders(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodPost {
methodNotAllowed(w, r)
return
@@ -743,10 +748,10 @@ func (a *API) dailyReportReminders(w http.ResponseWriter, r *http.Request, p *au
writeSuccess(w, r, map[string]any{"status": "queued"})
}
func (a *API) monthlyReports(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) monthlyReports(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
switch r.Method {
case http.MethodGet:
items, next, err := a.store.MonthlyReports(r.Context(), p.UserID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
items, next, err := st.MonthlyReports(r.Context(), p.UserID, r.URL.Query().Get("cursor"), limit(r, 50, 100))
if err != nil {
writeStoreError(w, r, err)
return
@@ -759,7 +764,7 @@ func (a *API) monthlyReports(w http.ResponseWriter, r *http.Request, p *auth.Pri
if !decodeBody(w, r, &body) {
return
}
report, err := a.store.CreateMonthlyDraft(r.Context(), p.UserID, "mr_"+randomToken(12), strings.TrimSpace(body.Month))
report, err := st.CreateMonthlyDraft(r.Context(), p.UserID, "mr_"+randomToken(12), strings.TrimSpace(body.Month))
if err != nil {
writeStoreError(w, r, err)
return
@@ -775,7 +780,7 @@ func (a *API) monthlyReports(w http.ResponseWriter, r *http.Request, p *auth.Pri
}
}
func (a *API) monthlyReportResource(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) monthlyReportResource(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
reportID, tail, ok := resourceTail(r.URL.Path, "/v1/workbench/reports/monthly/")
if !ok {
writeError(w, r, http.StatusNotFound, 30004, reasonMalformedRequest, "monthly report route not found", nil)
@@ -783,7 +788,7 @@ func (a *API) monthlyReportResource(w http.ResponseWriter, r *http.Request, p *a
}
switch {
case tail == "" && r.Method == http.MethodGet:
report, err := a.store.MonthlyReport(r.Context(), p.UserID, reportID)
report, err := st.MonthlyReport(r.Context(), p.UserID, reportID)
if err != nil {
writeStoreError(w, r, err)
return
@@ -804,7 +809,7 @@ func (a *API) monthlyReportResource(w http.ResponseWriter, r *http.Request, p *a
if !decodeBody(w, r, &body) {
return
}
report, err := a.store.SaveMonthlyDraft(r.Context(), p.UserID, reportID, body.WorkSummary, body.NextPlan, body.Attachments, body.Version)
report, err := st.SaveMonthlyDraft(r.Context(), p.UserID, reportID, body.WorkSummary, body.NextPlan, body.Attachments, body.Version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -822,7 +827,7 @@ func (a *API) monthlyReportResource(w http.ResponseWriter, r *http.Request, p *a
if !decodeBody(w, r, &body) {
return
}
report, err := a.store.SubmitMonthly(r.Context(), p.UserID, reportID, body.Version)
report, err := st.SubmitMonthly(r.Context(), p.UserID, reportID, body.Version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -840,7 +845,7 @@ func (a *API) monthlyReportResource(w http.ResponseWriter, r *http.Request, p *a
if !decodeBody(w, r, &body) {
return
}
report, err := a.store.ReopenMonthly(r.Context(), p.UserID, reportID, body.Version)
report, err := st.ReopenMonthly(r.Context(), p.UserID, reportID, body.Version)
if err != nil {
writeStoreError(w, r, err)
return
@@ -851,7 +856,7 @@ func (a *API) monthlyReportResource(w http.ResponseWriter, r *http.Request, p *a
}
}
func (a *API) monthlyReportStatistics(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) monthlyReportStatistics(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
@@ -859,12 +864,12 @@ func (a *API) monthlyReportStatistics(w http.ResponseWriter, r *http.Request, p
writeSuccess(w, r, map[string]any{"expected_count": 0, "submitted_count": 0, "missing_count": 0, "scope": map[string]any{"department_id": r.URL.Query().Get("department_id")}})
}
func (a *API) mailboxConfig(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) mailboxConfig(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodGet {
methodNotAllowed(w, r)
return
}
cfg, err := a.store.MailboxConfig(r.Context())
cfg, err := st.MailboxConfig(r.Context())
if err != nil {
writeStoreError(w, r, err)
return
@@ -872,7 +877,7 @@ func (a *API) mailboxConfig(w http.ResponseWriter, r *http.Request, p *auth.Prin
writeSuccess(w, r, mailboxConfigPayload(*cfg))
}
func (a *API) mailboxSubmissions(w http.ResponseWriter, r *http.Request, p *auth.Principal) {
func (a *API) mailboxSubmissions(w http.ResponseWriter, r *http.Request, p *auth.Principal, st *store.Store) {
if r.Method != http.MethodPost {
methodNotAllowed(w, r)
return
@@ -897,7 +902,7 @@ func (a *API) mailboxSubmissions(w http.ResponseWriter, r *http.Request, p *auth
return
}
attachments, _ := json.Marshal(body.AttachmentFileIDs)
submission, err := a.store.CreateMailboxSubmission(r.Context(), p.UserID, store.MailboxSubmission{
submission, err := st.CreateMailboxSubmission(r.Context(), p.UserID, store.MailboxSubmission{
SubmissionID: "mb_" + randomToken(12), Subject: subject, Content: content,
Anonymous: body.Anonymous, AttachmentFileIDs: string(attachments), NoticeVersion: strings.TrimSpace(body.NoticeVersion),
}, idempotencyKey)
@@ -954,10 +959,10 @@ func meetingTargetUsers(meeting store.Meeting) []int64 {
return targets
}
func (a *API) populateUnread(r *http.Request, p *auth.Principal, modules []map[string]any) error {
func (a *API) populateUnread(st *store.Store, r *http.Request, p *auth.Principal, modules []map[string]any) error {
for _, module := range modules {
if module["code"] == "meetings" {
count, err := a.store.MeetingUnreadCount(r.Context(), p.UserID)
count, err := st.MeetingUnreadCount(r.Context(), p.UserID)
if err != nil {
return err
}