Renamed and updated roles and privileges.
This commit is contained in:
parent
cd8e1e3b3b
commit
d30ee18e77
13 changed files with 112 additions and 72 deletions
|
|
@ -95,7 +95,7 @@ func TestAuthMiddlewareRoleEnforcement(t *testing.T) {
|
|||
mux := testMux(app)
|
||||
|
||||
// Create a gate user — should not be able to access /api/users (admin only)
|
||||
gate := testUserWithRole(t, app, "gateuser", "gate", []int{})
|
||||
gate := testUserWithRole(t, app, "gateuser", "gatekeeper", []int{})
|
||||
token := testToken(t, app, gate)
|
||||
|
||||
req := testAuthRequest("GET", "/api/users", nil, token)
|
||||
|
|
|
|||
42
db.go
42
db.go
|
|
@ -44,7 +44,7 @@ func migrate(db *sql.DB) error {
|
|||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK(role IN ('admin','coordinator','gate','ticketing','volunteer_lead')),
|
||||
role TEXT NOT NULL CHECK(role IN ('admin','ticketing','staffing','colead','gatekeeper')),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
|
|
@ -260,6 +260,46 @@ func migrateV3(db *sql.DB) error {
|
|||
AND v.deleted_at IS NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM tickets t WHERE t.participant_id = v.participant_id AND t.deleted_at IS NULL)`)
|
||||
|
||||
return migrateV4(db)
|
||||
}
|
||||
|
||||
// migrateV4 renames roles: volunteer_lead→colead, coordinator→staffing, gate→gatekeeper.
|
||||
func migrateV4(db *sql.DB) error {
|
||||
var count int
|
||||
if err := db.QueryRow(`SELECT COUNT(*) FROM users WHERE role IN ('volunteer_lead','coordinator','gate')`).Scan(&count); err != nil || count == 0 {
|
||||
return nil
|
||||
}
|
||||
if _, err := db.Exec(`PRAGMA foreign_keys = OFF`); err != nil {
|
||||
return err
|
||||
}
|
||||
stmts := []string{
|
||||
`CREATE TABLE users_v4 (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK(role IN ('admin','ticketing','staffing','colead','gatekeeper')),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)`,
|
||||
`INSERT INTO users_v4 (id, username, password_hash, role, created_at)
|
||||
SELECT id, username, password_hash,
|
||||
CASE role
|
||||
WHEN 'volunteer_lead' THEN 'colead'
|
||||
WHEN 'coordinator' THEN 'staffing'
|
||||
WHEN 'gate' THEN 'gatekeeper'
|
||||
ELSE role
|
||||
END,
|
||||
created_at
|
||||
FROM users`,
|
||||
`DROP TABLE users`,
|
||||
`ALTER TABLE users_v4 RENAME TO users`,
|
||||
`PRAGMA foreign_keys = ON`,
|
||||
}
|
||||
for _, s := range stmts {
|
||||
if _, err := db.Exec(s); err != nil {
|
||||
db.Exec(`PRAGMA foreign_keys = ON`)
|
||||
return fmt.Errorf("migrateV4: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
import { syncPull, startSSE, startSyncLoop } from './sync.js'
|
||||
import Login from './pages/Login.svelte'
|
||||
import Dashboard from './pages/Dashboard.svelte'
|
||||
import Attendees from './pages/Attendees.svelte'
|
||||
import Participants from './pages/Participants.svelte'
|
||||
import Volunteers from './pages/Volunteers.svelte'
|
||||
import Departments from './pages/Departments.svelte'
|
||||
|
|
@ -104,7 +103,7 @@
|
|||
<ConfirmEmail />
|
||||
{:else if !session}
|
||||
<Login onlogin={onLogin} />
|
||||
{:else if role === 'gate'}
|
||||
{:else if role === 'gatekeeper'}
|
||||
<!-- Gate users get the full-screen GateUI instead of the standard layout -->
|
||||
<GateUI {session} {onLogout} />
|
||||
{:else}
|
||||
|
|
@ -122,13 +121,11 @@
|
|||
<span class="mobile-brand">Turn<span class="accent">pike</span></span>
|
||||
</header>
|
||||
{#if path === '/' || path === ''}
|
||||
{#if role === 'volunteer_lead'}
|
||||
{#if role === 'colead'}
|
||||
<ScheduleBoard {session} />
|
||||
{:else}
|
||||
<Dashboard {session} />
|
||||
{/if}
|
||||
{:else if path.startsWith('/attendees')}
|
||||
<Attendees {session} />
|
||||
{:else if path.startsWith('/participants')}
|
||||
<Participants {session} />
|
||||
{:else if path.startsWith('/volunteers')}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { LayoutDashboard, ClipboardCheck, Heart, Hexagon, CalendarDays, Upload, Users, Settings, LogOut, Ticket } from 'lucide-svelte'
|
||||
import { LayoutDashboard, Heart, Hexagon, CalendarDays, Upload, Users, Settings, LogOut, Ticket } from 'lucide-svelte'
|
||||
|
||||
let { session, active, onLogout, navigate, open = false } = $props()
|
||||
|
||||
|
|
@ -8,17 +8,12 @@
|
|||
const iconProps = { size: 18, strokeWidth: 1.75 }
|
||||
|
||||
const links = $derived.by(() => {
|
||||
if (role === 'ticketing') return [
|
||||
{ href: '/participants', label: 'Participants', icon: Ticket },
|
||||
{ href: '/attendees', label: 'Attendees', icon: ClipboardCheck },
|
||||
{ href: '/import', label: 'Import', icon: Upload },
|
||||
]
|
||||
if (role === 'volunteer_lead') return [
|
||||
if (role === 'colead') return [
|
||||
{ href: '/', label: 'Schedule', icon: CalendarDays },
|
||||
{ href: '/volunteers', label: 'Volunteers', icon: Heart },
|
||||
{ href: '/departments', label: 'Departments', icon: Hexagon },
|
||||
]
|
||||
if (role === 'coordinator') return [
|
||||
if (role === 'staffing') return [
|
||||
{ href: '/', label: 'Dashboard', icon: LayoutDashboard },
|
||||
{ href: '/schedule', label: 'Schedule', icon: CalendarDays },
|
||||
{ href: '/volunteers', label: 'Volunteers', icon: Heart },
|
||||
|
|
@ -27,7 +22,6 @@
|
|||
return [
|
||||
{ href: '/', label: 'Dashboard', icon: LayoutDashboard },
|
||||
{ href: '/participants', label: 'Participants', icon: Ticket },
|
||||
{ href: '/attendees', label: 'Attendees', icon: ClipboardCheck },
|
||||
{ href: '/volunteers', label: 'Volunteers', icon: Heart },
|
||||
{ href: '/departments', label: 'Departments', icon: Hexagon },
|
||||
{ href: '/schedule', label: 'Schedule', icon: CalendarDays },
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
let saving = $state(false)
|
||||
|
||||
const role = $derived(session?.user?.role ?? '')
|
||||
const canCreate = $derived(['admin', 'coordinator'].includes(role))
|
||||
const canDelete = $derived(role === 'admin')
|
||||
const canCreate = $derived(['admin', 'ticketing', 'staffing'].includes(role))
|
||||
const canDelete = $derived(['admin', 'ticketing'].includes(role))
|
||||
|
||||
const allDepts = liveQuery(() =>
|
||||
db.departments.filter(d => !d.deleted_at).toArray()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
let assigning = $state(false)
|
||||
|
||||
const role = $derived(session?.user?.role ?? '')
|
||||
const canManage = $derived(['admin', 'coordinator', 'volunteer_lead'].includes(role))
|
||||
const canManage = $derived(['admin', 'ticketing', 'staffing', 'colead'].includes(role))
|
||||
const myDeptIDs = $derived(session?.user?.department_ids ?? [])
|
||||
|
||||
const allDepts = liveQuery(() =>
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
// Departments visible to this user
|
||||
const visibleDepts = $derived.by(() => {
|
||||
const depts = $allDepts ?? []
|
||||
if (role === 'volunteer_lead') return depts.filter(d => myDeptIDs.includes(d.id))
|
||||
if (role === 'colead') return depts.filter(d => myDeptIDs.includes(d.id))
|
||||
return depts
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
.then(arr => arr.sort((a, b) => a.name.localeCompare(b.name)))
|
||||
)
|
||||
|
||||
const roles = ['admin', 'coordinator', 'ticketing', 'gate', 'volunteer_lead']
|
||||
const roles = ['admin', 'ticketing', 'staffing', 'colead', 'gatekeeper']
|
||||
|
||||
const me = $derived(session?.user?.id)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
let newNote = $state('')
|
||||
|
||||
const role = $derived(session?.user?.role ?? '')
|
||||
const canManage = $derived(['admin', 'coordinator', 'volunteer_lead'].includes(role))
|
||||
const canManage = $derived(['admin', 'ticketing', 'staffing', 'colead'].includes(role))
|
||||
|
||||
const allVolunteers = liveQuery(() =>
|
||||
db.volunteers.filter(v => !v.deleted_at).toArray()
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ func TestCheckInAttendeeHandler(t *testing.T) {
|
|||
|
||||
func TestGateRoleCanCheckIn(t *testing.T) {
|
||||
app := testApp(t)
|
||||
gate := testUserWithRole(t, app, "gateuser", "gate", []int{})
|
||||
gate := testUserWithRole(t, app, "gateuser", "gatekeeper", []int{})
|
||||
token := testToken(t, app, gate)
|
||||
mux := testMux(app)
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ func TestGateRoleCanCheckIn(t *testing.T) {
|
|||
|
||||
func TestGateRoleCannotDelete(t *testing.T) {
|
||||
app := testApp(t)
|
||||
gate := testUserWithRole(t, app, "gateuser", "gate", []int{})
|
||||
gate := testUserWithRole(t, app, "gateuser", "gatekeeper", []int{})
|
||||
token := testToken(t, app, gate)
|
||||
mux := testMux(app)
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func TestResetAttendees(t *testing.T) {
|
|||
|
||||
func TestResetAttendeesRequiresAdmin(t *testing.T) {
|
||||
app := testApp(t)
|
||||
gate := testUserWithRole(t, app, "gate1", "gate", []int{})
|
||||
gate := testUserWithRole(t, app, "gate1", "gatekeeper", []int{})
|
||||
token := testToken(t, app, gate)
|
||||
mux := testMux(app)
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ func TestResetDepartmentsCascadesShifts(t *testing.T) {
|
|||
|
||||
func TestSettingsNonAdminRejected(t *testing.T) {
|
||||
app := testApp(t)
|
||||
gate := testUserWithRole(t, app, "gateuser", "gate", []int{})
|
||||
gate := testUserWithRole(t, app, "gateuser", "gatekeeper", []int{})
|
||||
token := testToken(t, app, gate)
|
||||
mux := testMux(app)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ func (app *App) handleListShifts(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
claims := claimsFromContext(r)
|
||||
if claims.Role == "volunteer_lead" && deptID == nil && len(claims.DeptIDs) > 0 {
|
||||
if claims.Role == "colead" && deptID == nil && len(claims.DeptIDs) > 0 {
|
||||
deptID = &claims.DeptIDs[0]
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ func (app *App) handleCreateShift(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
claims := claimsFromContext(r)
|
||||
if claims.Role == "volunteer_lead" && !inSlice(s.DepartmentID, claims.DeptIDs) {
|
||||
if claims.Role == "colead" && !inSlice(s.DepartmentID, claims.DeptIDs) {
|
||||
writeError(w, "forbidden: outside your department", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ func (app *App) handleUpdateShift(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
claims := claimsFromContext(r)
|
||||
if claims.Role == "volunteer_lead" {
|
||||
if claims.Role == "colead" {
|
||||
existing, _ := app.getShift(id)
|
||||
if existing == nil || !inSlice(existing.DepartmentID, claims.DeptIDs) {
|
||||
writeError(w, "forbidden: outside your department", http.StatusForbidden)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func (app *App) handleListVolunteers(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
claims := claimsFromContext(r)
|
||||
if claims.Role == "volunteer_lead" && deptID == nil && len(claims.DeptIDs) > 0 {
|
||||
if claims.Role == "colead" && deptID == nil && len(claims.DeptIDs) > 0 {
|
||||
deptID = &claims.DeptIDs[0]
|
||||
}
|
||||
|
||||
|
|
@ -43,12 +43,21 @@ func (app *App) handleCreateVolunteer(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
claims := claimsFromContext(r)
|
||||
if claims.Role == "volunteer_lead" {
|
||||
if claims.Role == "colead" {
|
||||
if v.DepartmentID == nil || !inSlice(*v.DepartmentID, claims.DeptIDs) {
|
||||
writeError(w, "forbidden: outside your department", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
if v.Email != "" && v.ParticipantID == nil {
|
||||
p, _ := app.getParticipantByEmail(v.Email)
|
||||
if p == nil {
|
||||
p, _ = app.createParticipant(Participant{PreferredName: v.Name, Email: v.Email})
|
||||
}
|
||||
if p != nil {
|
||||
v.ParticipantID = &p.ID
|
||||
}
|
||||
}
|
||||
created, err := app.createVolunteer(v)
|
||||
if err != nil {
|
||||
writeError(w, err.Error(), http.StatusInternalServerError)
|
||||
|
|
@ -88,7 +97,7 @@ func (app *App) handleUpdateVolunteer(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
claims := claimsFromContext(r)
|
||||
if claims.Role == "volunteer_lead" {
|
||||
if claims.Role == "colead" {
|
||||
existing, _ := app.getVolunteer(id)
|
||||
if existing == nil || existing.DepartmentID == nil || !inSlice(*existing.DepartmentID, claims.DeptIDs) {
|
||||
writeError(w, "forbidden: outside your department", http.StatusForbidden)
|
||||
|
|
|
|||
80
main.go
80
main.go
|
|
@ -97,71 +97,71 @@ func (app *App) registerRoutes(mux *http.ServeMux) {
|
|||
mux.HandleFunc("GET /api/me", auth(app.handleMe))
|
||||
|
||||
mux.HandleFunc("GET /api/event", auth(app.handleGetEvent))
|
||||
mux.HandleFunc("PUT /api/event", auth(app.handleUpdateEvent, "admin"))
|
||||
mux.HandleFunc("PUT /api/event", auth(app.handleUpdateEvent, "admin", "ticketing"))
|
||||
|
||||
mux.HandleFunc("GET /api/attendees", auth(app.handleListAttendees, "admin", "ticketing", "gate"))
|
||||
mux.HandleFunc("GET /api/attendees", auth(app.handleListAttendees, "admin", "ticketing", "gatekeeper"))
|
||||
mux.HandleFunc("POST /api/attendees", auth(app.handleCreateAttendee, "admin", "ticketing"))
|
||||
mux.HandleFunc("GET /api/attendees/export", auth(app.handleExportAttendees, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/attendees/generate-tokens", auth(app.handleGenerateTokens, "admin", "ticketing"))
|
||||
mux.HandleFunc("GET /api/attendees/export-tokens", auth(app.handleExportTokenLinks, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/attendees/email-tokens", auth(app.handleEmailAllTokens, "admin", "ticketing"))
|
||||
mux.HandleFunc("GET /api/attendees/{id}", auth(app.handleGetAttendee, "admin", "ticketing", "gate"))
|
||||
mux.HandleFunc("GET /api/attendees/{id}", auth(app.handleGetAttendee, "admin", "ticketing", "gatekeeper"))
|
||||
mux.HandleFunc("PUT /api/attendees/{id}", auth(app.handleUpdateAttendee, "admin", "ticketing"))
|
||||
mux.HandleFunc("DELETE /api/attendees/{id}", auth(app.handleDeleteAttendee, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/attendees/{id}/checkin", auth(app.handleCheckInAttendee, "admin", "ticketing", "gate"))
|
||||
mux.HandleFunc("POST /api/attendees/{id}/checkin", auth(app.handleCheckInAttendee, "admin", "ticketing", "gatekeeper"))
|
||||
mux.HandleFunc("POST /api/attendees/{id}/email-token", auth(app.handleEmailToken, "admin", "ticketing"))
|
||||
|
||||
mux.HandleFunc("GET /api/participants", auth(app.handleListParticipants, "admin", "ticketing", "gate"))
|
||||
mux.HandleFunc("GET /api/participants", auth(app.handleListParticipants, "admin", "ticketing", "gatekeeper"))
|
||||
mux.HandleFunc("POST /api/participants", auth(app.handleCreateParticipant, "admin", "ticketing"))
|
||||
mux.HandleFunc("GET /api/participants/export", auth(app.handleExportParticipants, "admin", "ticketing"))
|
||||
mux.HandleFunc("GET /api/participants/{id}", auth(app.handleGetParticipant, "admin", "ticketing", "gate"))
|
||||
mux.HandleFunc("GET /api/participants/{id}", auth(app.handleGetParticipant, "admin", "ticketing", "gatekeeper"))
|
||||
mux.HandleFunc("PUT /api/participants/{id}", auth(app.handleUpdateParticipant, "admin", "ticketing"))
|
||||
mux.HandleFunc("DELETE /api/participants/{id}", auth(app.handleDeleteParticipant, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/participants/{id}/merge/{other_id}", auth(app.handleMergeParticipants, "admin", "ticketing"))
|
||||
|
||||
mux.HandleFunc("GET /api/tickets", auth(app.handleListTickets, "admin", "ticketing", "gate"))
|
||||
mux.HandleFunc("POST /api/tickets/{id}/checkin", auth(app.handleCheckInTicket, "admin", "ticketing", "gate"))
|
||||
mux.HandleFunc("GET /api/tickets", auth(app.handleListTickets, "admin", "ticketing", "gatekeeper"))
|
||||
mux.HandleFunc("POST /api/tickets/{id}/checkin", auth(app.handleCheckInTicket, "admin", "ticketing", "gatekeeper"))
|
||||
mux.HandleFunc("POST /api/tickets/generate-codes", auth(app.handleGenerateTokens, "admin", "ticketing"))
|
||||
mux.HandleFunc("GET /api/tickets/export-links", auth(app.handleExportTokenLinks, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/tickets/email-codes", auth(app.handleEmailAllTokens, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/tickets/{id}/email-code", auth(app.handleEmailToken, "admin", "ticketing"))
|
||||
|
||||
mux.HandleFunc("GET /api/departments", auth(app.handleListDepartments))
|
||||
mux.HandleFunc("POST /api/departments", auth(app.handleCreateDepartment, "admin", "coordinator"))
|
||||
mux.HandleFunc("PUT /api/departments/{id}", auth(app.handleUpdateDepartment, "admin", "coordinator"))
|
||||
mux.HandleFunc("DELETE /api/departments/{id}", auth(app.handleDeleteDepartment, "admin"))
|
||||
mux.HandleFunc("POST /api/departments", auth(app.handleCreateDepartment, "admin", "ticketing", "staffing"))
|
||||
mux.HandleFunc("PUT /api/departments/{id}", auth(app.handleUpdateDepartment, "admin", "ticketing", "staffing"))
|
||||
mux.HandleFunc("DELETE /api/departments/{id}", auth(app.handleDeleteDepartment, "admin", "ticketing"))
|
||||
|
||||
mux.HandleFunc("GET /api/volunteers", auth(app.handleListVolunteers, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("POST /api/volunteers", auth(app.handleCreateVolunteer, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("GET /api/volunteers/{id}", auth(app.handleGetVolunteer, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("PUT /api/volunteers/{id}", auth(app.handleUpdateVolunteer, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("DELETE /api/volunteers/{id}", auth(app.handleDeleteVolunteer, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("POST /api/volunteers/{id}/checkin", auth(app.handleCheckInVolunteer, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("POST /api/volunteers/{id}/shifts", auth(app.handleAssignShift, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("DELETE /api/volunteers/{id}/shifts/{shift_id}", auth(app.handleUnassignShift, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("GET /api/volunteers", auth(app.handleListVolunteers, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("POST /api/volunteers", auth(app.handleCreateVolunteer, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("GET /api/volunteers/{id}", auth(app.handleGetVolunteer, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("PUT /api/volunteers/{id}", auth(app.handleUpdateVolunteer, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("DELETE /api/volunteers/{id}", auth(app.handleDeleteVolunteer, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("POST /api/volunteers/{id}/checkin", auth(app.handleCheckInVolunteer, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("POST /api/volunteers/{id}/shifts", auth(app.handleAssignShift, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("DELETE /api/volunteers/{id}/shifts/{shift_id}", auth(app.handleUnassignShift, "admin", "ticketing", "staffing", "colead"))
|
||||
|
||||
mux.HandleFunc("GET /api/shifts", auth(app.handleListShifts, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("POST /api/shifts", auth(app.handleCreateShift, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("POST /api/shifts/reorder", auth(app.handleReorderShifts, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("PUT /api/shifts/{id}", auth(app.handleUpdateShift, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("DELETE /api/shifts/{id}", auth(app.handleDeleteShift, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("POST /api/shifts/{id}/volunteers", auth(app.handleAssignShiftVolunteer, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("DELETE /api/shifts/{id}/volunteers/{volunteer_id}", auth(app.handleUnassignShiftVolunteer, "admin", "coordinator", "volunteer_lead"))
|
||||
mux.HandleFunc("GET /api/shifts", auth(app.handleListShifts, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("POST /api/shifts", auth(app.handleCreateShift, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("POST /api/shifts/reorder", auth(app.handleReorderShifts, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("PUT /api/shifts/{id}", auth(app.handleUpdateShift, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("DELETE /api/shifts/{id}", auth(app.handleDeleteShift, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("POST /api/shifts/{id}/volunteers", auth(app.handleAssignShiftVolunteer, "admin", "ticketing", "staffing", "colead"))
|
||||
mux.HandleFunc("DELETE /api/shifts/{id}/volunteers/{volunteer_id}", auth(app.handleUnassignShiftVolunteer, "admin", "ticketing", "staffing", "colead"))
|
||||
|
||||
mux.HandleFunc("GET /api/users", auth(app.handleListUsers, "admin"))
|
||||
mux.HandleFunc("POST /api/users", auth(app.handleCreateUser, "admin"))
|
||||
mux.HandleFunc("PUT /api/users/{id}", auth(app.handleUpdateUser, "admin"))
|
||||
mux.HandleFunc("DELETE /api/users/{id}", auth(app.handleDeleteUser, "admin"))
|
||||
mux.HandleFunc("GET /api/users", auth(app.handleListUsers, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/users", auth(app.handleCreateUser, "admin", "ticketing"))
|
||||
mux.HandleFunc("PUT /api/users/{id}", auth(app.handleUpdateUser, "admin", "ticketing"))
|
||||
mux.HandleFunc("DELETE /api/users/{id}", auth(app.handleDeleteUser, "admin", "ticketing"))
|
||||
|
||||
mux.HandleFunc("GET /api/settings", auth(app.handleGetSettings, "admin"))
|
||||
mux.HandleFunc("PUT /api/settings", auth(app.handleUpdateSettings, "admin"))
|
||||
mux.HandleFunc("POST /api/settings/test-email", auth(app.handleTestEmail, "admin"))
|
||||
mux.HandleFunc("POST /api/settings/reset-attendees", auth(app.handleResetAttendees, "admin"))
|
||||
mux.HandleFunc("POST /api/settings/reset-tickets", auth(app.handleResetTickets, "admin"))
|
||||
mux.HandleFunc("POST /api/settings/reset-volunteers", auth(app.handleResetVolunteers, "admin"))
|
||||
mux.HandleFunc("POST /api/settings/reset-shifts", auth(app.handleResetShifts, "admin"))
|
||||
mux.HandleFunc("POST /api/settings/reset-departments", auth(app.handleResetDepartments, "admin"))
|
||||
mux.HandleFunc("POST /api/settings/reset-volunteer-shifts", auth(app.handleResetVolunteerShifts, "admin"))
|
||||
mux.HandleFunc("GET /api/settings", auth(app.handleGetSettings, "admin", "ticketing"))
|
||||
mux.HandleFunc("PUT /api/settings", auth(app.handleUpdateSettings, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/settings/test-email", auth(app.handleTestEmail, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/settings/reset-attendees", auth(app.handleResetAttendees, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/settings/reset-tickets", auth(app.handleResetTickets, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/settings/reset-volunteers", auth(app.handleResetVolunteers, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/settings/reset-shifts", auth(app.handleResetShifts, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/settings/reset-departments", auth(app.handleResetDepartments, "admin", "ticketing"))
|
||||
mux.HandleFunc("POST /api/settings/reset-volunteer-shifts", auth(app.handleResetVolunteerShifts, "admin", "ticketing"))
|
||||
|
||||
mux.HandleFunc("POST /api/import", auth(app.handleImport, "admin", "ticketing"))
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ func (app *App) registerRoutes(mux *http.ServeMux) {
|
|||
writeJSON(w, map[string]string{"build": buildID})
|
||||
})
|
||||
|
||||
mux.HandleFunc("POST /api/settings/shift-signups", auth(app.handleToggleShiftSignups, "admin", "volunteer_lead"))
|
||||
mux.HandleFunc("POST /api/settings/shift-signups", auth(app.handleToggleShiftSignups, "admin", "ticketing", "staffing"))
|
||||
|
||||
// Public endpoints — no JWT required.
|
||||
mux.HandleFunc("GET /api/public/signup-config", app.handlePublicSignupConfig)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue