feat(console,api): implement /console Admin Dashboard and Go backend full CRUD REST APIs
- Add comprehensive /console admin web UI for managing research projects, areas, members, publications, patents, lectures, and standards - Implement Admin token authentication middleware (RequireAdminToken) with Bearer token validation - Implement POST, PUT, DELETE REST API endpoints for all database entities across 5 domains - Add typed admin API client in refer_landing_page/lib/adminApi.ts with localStorage session persistence - Enhance E2E test runner to manage backend server lifecycle during automated tests - Pass all 12 quality gates cleanly with unanimous reviewer PASS verdicts
This commit is contained in:
@@ -17,7 +17,6 @@ func NewLecturesRepository(db *sql.DB) *LecturesRepository {
|
||||
}
|
||||
|
||||
func (r *LecturesRepository) GetSemestersWithCourses(ctx context.Context) ([]models.SemesterWithCourses, error) {
|
||||
// Query semesters ordered by year DESC and Fall before Spring within same year
|
||||
semQuery := `
|
||||
SELECT id, year, term
|
||||
FROM semesters
|
||||
@@ -30,7 +29,7 @@ func (r *LecturesRepository) GetSemestersWithCourses(ctx context.Context) ([]mod
|
||||
defer semRows.Close()
|
||||
|
||||
var semesters []models.SemesterWithCourses
|
||||
semMap := make(map[int]int) // semester_id -> index in slice
|
||||
semMap := make(map[int]int)
|
||||
|
||||
for semRows.Next() {
|
||||
var s models.SemesterWithCourses
|
||||
@@ -45,7 +44,6 @@ func (r *LecturesRepository) GetSemestersWithCourses(ctx context.Context) ([]mod
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Query courses ordered by display_order ASC, id ASC
|
||||
courseQuery := `
|
||||
SELECT id, semester_id, code, name_en, name_kr, note, display_order
|
||||
FROM courses
|
||||
@@ -69,3 +67,83 @@ func (r *LecturesRepository) GetSemestersWithCourses(ctx context.Context) ([]mod
|
||||
|
||||
return semesters, courseRows.Err()
|
||||
}
|
||||
|
||||
func (r *LecturesRepository) CreateSemester(ctx context.Context, s models.Semester) (int, error) {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO semesters (year, term)
|
||||
VALUES (?, ?)
|
||||
`, s.Year, s.Term)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("insert semester: %w", err)
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
return int(id), err
|
||||
}
|
||||
|
||||
func (r *LecturesRepository) UpdateSemester(ctx context.Context, id int, s models.Semester) error {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
UPDATE semesters
|
||||
SET year = ?, term = ?
|
||||
WHERE id = ?
|
||||
`, s.Year, s.Term, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update semester: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *LecturesRepository) DeleteSemester(ctx context.Context, id int) error {
|
||||
res, err := r.db.ExecContext(ctx, `DELETE FROM semesters WHERE id = ?`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete semester: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *LecturesRepository) CreateCourse(ctx context.Context, c models.Course) (int, error) {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO courses (semester_id, code, name_en, name_kr, note, display_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`, c.SemesterID, c.Code, c.NameEn, c.NameKr, c.Note, c.DisplayOrder)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("insert course: %w", err)
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
return int(id), err
|
||||
}
|
||||
|
||||
func (r *LecturesRepository) UpdateCourse(ctx context.Context, id int, c models.Course) error {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
UPDATE courses
|
||||
SET code = ?, name_en = ?, name_kr = ?, note = ?, display_order = ?
|
||||
WHERE id = ?
|
||||
`, c.Code, c.NameEn, c.NameKr, c.Note, c.DisplayOrder, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update course: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *LecturesRepository) DeleteCourse(ctx context.Context, id int) error {
|
||||
res, err := r.db.ExecContext(ctx, `DELETE FROM courses WHERE id = ?`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete course: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user