fix: superadmin course access + navigate to correct course from courses page
Backend: list_students, add_student, import_students now bypass tutor_courses check for superadmins, matching the existing pattern in list_assigned_tutors. A superadmin creating a new course was getting 401 when accessing it because no tutor_courses row exists for them. Frontend: courses page passes ?courseId= to students/sessions links; both pages now pre-select the matching course on mount instead of always defaulting to courses[0].
This commit is contained in:
@@ -126,13 +126,14 @@ async fn list_assigned_tutors(
|
||||
Ok(Json(tutors))
|
||||
}
|
||||
|
||||
// Fix 3: verify tutor has access to this course
|
||||
async fn list_students(
|
||||
claims: TutorClaims,
|
||||
State(pool): State<SqlitePool>,
|
||||
Path(course_id): Path<i64>,
|
||||
) -> Result<Json<Vec<Student>>, AppError> {
|
||||
super::verify_tutor_course_access(&pool, claims.sub, course_id).await?;
|
||||
if !claims.is_superadmin {
|
||||
super::verify_tutor_course_access(&pool, claims.sub, course_id).await?;
|
||||
}
|
||||
let students = sqlx::query_as::<_, Student>(
|
||||
"SELECT id, course_id, name FROM students WHERE course_id = ? ORDER BY id",
|
||||
)
|
||||
@@ -142,14 +143,15 @@ async fn list_students(
|
||||
Ok(Json(students))
|
||||
}
|
||||
|
||||
// Fix 3: verify tutor has access to this course
|
||||
async fn add_student(
|
||||
claims: TutorClaims,
|
||||
State(pool): State<SqlitePool>,
|
||||
Path(course_id): Path<i64>,
|
||||
Json(req): Json<CreateStudent>,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
super::verify_tutor_course_access(&pool, claims.sub, course_id).await?;
|
||||
if !claims.is_superadmin {
|
||||
super::verify_tutor_course_access(&pool, claims.sub, course_id).await?;
|
||||
}
|
||||
let id = sqlx::query("INSERT INTO students (course_id, name) VALUES (?, ?)")
|
||||
.bind(course_id)
|
||||
.bind(&req.name)
|
||||
@@ -162,14 +164,15 @@ async fn add_student(
|
||||
))
|
||||
}
|
||||
|
||||
// Fix 3 + Fix 4: verify access, validate CSV header, wrap in transaction, size check
|
||||
async fn import_students(
|
||||
claims: TutorClaims,
|
||||
State(pool): State<SqlitePool>,
|
||||
Path(course_id): Path<i64>,
|
||||
mut multipart: Multipart,
|
||||
) -> Result<(StatusCode, Json<Value>), AppError> {
|
||||
super::verify_tutor_course_access(&pool, claims.sub, course_id).await?;
|
||||
if !claims.is_superadmin {
|
||||
super::verify_tutor_course_access(&pool, claims.sub, course_id).await?;
|
||||
}
|
||||
|
||||
let mut count = 0i64;
|
||||
|
||||
|
||||
@@ -139,8 +139,8 @@
|
||||
{/if}
|
||||
<td style="padding:12px 14px;text-align:right">
|
||||
<div style="display:flex;gap:6px;justify-content:flex-end">
|
||||
<a href="/admin/students" class="btn ghost sm">Studierende</a>
|
||||
<a href="/admin/sessions" class="btn ghost sm">Sitzungen</a>
|
||||
<a href="/admin/students?courseId={course.id}" class="btn ghost sm">Studierende</a>
|
||||
<a href="/admin/sessions?courseId={course.id}" class="btn ghost sm">Sitzungen</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/state';
|
||||
import { api } from '$lib/api';
|
||||
import type { Course, Room, Session } from '$lib/types';
|
||||
import Icon from '$lib/components/Icon.svelte';
|
||||
@@ -23,8 +24,9 @@
|
||||
onMount(async () => {
|
||||
courses = await api.admin.courses.list();
|
||||
rooms = await api.admin.rooms.list();
|
||||
const first = courses[0];
|
||||
if (first) selectedCourseId = first.id;
|
||||
const paramId = page.url.searchParams.get('courseId');
|
||||
const match = paramId ? courses.find(c => c.id === Number(paramId)) : null;
|
||||
selectedCourseId = (match ?? courses[0])?.id ?? null;
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/state';
|
||||
import { api } from '$lib/api';
|
||||
import type { Course, Student } from '$lib/types';
|
||||
import Icon from '$lib/components/Icon.svelte';
|
||||
@@ -13,8 +14,9 @@
|
||||
|
||||
onMount(async () => {
|
||||
courses = await api.admin.courses.list();
|
||||
const first = courses[0];
|
||||
if (first) selectedCourseId = first.id;
|
||||
const paramId = page.url.searchParams.get('courseId');
|
||||
const match = paramId ? courses.find(c => c.id === Number(paramId)) : null;
|
||||
selectedCourseId = (match ?? courses[0])?.id ?? null;
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
|
||||
Reference in New Issue
Block a user