[split-required] [guardrail-change] Enforce 500 LOC budget across all services

Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook)
and split all 44 files exceeding 500 LOC into domain-focused modules:

- consent-service (Go): models, handlers, services, database splits
- backend-core (Python): security_api, rbac_api, pdf_service, auth splits
- admin-core (TypeScript): 5 page.tsx + sidebar extractions
- pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits
- voice-service (Python): enhanced_task_orchestrator split

Result: 0 violations, 36 exempted (pipeline, tests, pure-data files).
Go build verified clean. No behavior changes — pure structural splits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-27 00:09:30 +02:00
parent 5ef039a6bc
commit 92c86ec6ba
162 changed files with 23853 additions and 23034 deletions

View File

@@ -2,8 +2,6 @@ package services
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"time"
@@ -35,21 +33,21 @@ func NewSchoolService(db *database.DB, matrixService *matrix.MatrixService) *Sch
// CreateSchool creates a new school
func (s *SchoolService) CreateSchool(ctx context.Context, req models.CreateSchoolRequest) (*models.School, error) {
school := &models.School{
ID: uuid.New(),
Name: req.Name,
ShortName: req.ShortName,
Type: req.Type,
Address: req.Address,
City: req.City,
ID: uuid.New(),
Name: req.Name,
ShortName: req.ShortName,
Type: req.Type,
Address: req.Address,
City: req.City,
PostalCode: req.PostalCode,
State: req.State,
Country: "DE",
Phone: req.Phone,
Email: req.Email,
Website: req.Website,
IsActive: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
State: req.State,
Country: "DE",
Phone: req.Phone,
Email: req.Email,
Website: req.Website,
IsActive: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
query := `
@@ -298,350 +296,6 @@ func (s *SchoolService) ListClasses(ctx context.Context, schoolID, schoolYearID
return classes, nil
}
// ========================================
// Student Management
// ========================================
// CreateStudent creates a new student
func (s *SchoolService) CreateStudent(ctx context.Context, schoolID uuid.UUID, req models.CreateStudentRequest) (*models.Student, error) {
classID, err := uuid.Parse(req.ClassID)
if err != nil {
return nil, fmt.Errorf("invalid class ID: %w", err)
}
student := &models.Student{
ID: uuid.New(),
SchoolID: schoolID,
ClassID: classID,
StudentNumber: req.StudentNumber,
FirstName: req.FirstName,
LastName: req.LastName,
Gender: req.Gender,
IsActive: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if req.DateOfBirth != nil {
dob, err := time.Parse("2006-01-02", *req.DateOfBirth)
if err == nil {
student.DateOfBirth = &dob
}
}
query := `
INSERT INTO students (id, school_id, class_id, student_number, first_name, last_name, date_of_birth, gender, is_active, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING id`
err = s.db.Pool.QueryRow(ctx, query,
student.ID, student.SchoolID, student.ClassID, student.StudentNumber,
student.FirstName, student.LastName, student.DateOfBirth, student.Gender,
student.IsActive, student.CreatedAt, student.UpdatedAt,
).Scan(&student.ID)
if err != nil {
return nil, fmt.Errorf("failed to create student: %w", err)
}
return student, nil
}
// GetStudent retrieves a student by ID
func (s *SchoolService) GetStudent(ctx context.Context, studentID uuid.UUID) (*models.Student, error) {
query := `
SELECT id, school_id, class_id, user_id, student_number, first_name, last_name, date_of_birth, gender, matrix_user_id, matrix_dm_room, is_active, created_at, updated_at
FROM students
WHERE id = $1`
student := &models.Student{}
err := s.db.Pool.QueryRow(ctx, query, studentID).Scan(
&student.ID, &student.SchoolID, &student.ClassID, &student.UserID,
&student.StudentNumber, &student.FirstName, &student.LastName,
&student.DateOfBirth, &student.Gender, &student.MatrixUserID,
&student.MatrixDMRoom, &student.IsActive, &student.CreatedAt, &student.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to get student: %w", err)
}
return student, nil
}
// ListStudentsByClass lists all students in a class
func (s *SchoolService) ListStudentsByClass(ctx context.Context, classID uuid.UUID) ([]models.Student, error) {
query := `
SELECT id, school_id, class_id, user_id, student_number, first_name, last_name, date_of_birth, gender, matrix_user_id, matrix_dm_room, is_active, created_at, updated_at
FROM students
WHERE class_id = $1 AND is_active = true
ORDER BY last_name, first_name`
rows, err := s.db.Pool.Query(ctx, query, classID)
if err != nil {
return nil, fmt.Errorf("failed to list students: %w", err)
}
defer rows.Close()
var students []models.Student
for rows.Next() {
var student models.Student
err := rows.Scan(
&student.ID, &student.SchoolID, &student.ClassID, &student.UserID,
&student.StudentNumber, &student.FirstName, &student.LastName,
&student.DateOfBirth, &student.Gender, &student.MatrixUserID,
&student.MatrixDMRoom, &student.IsActive, &student.CreatedAt, &student.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to scan student: %w", err)
}
students = append(students, student)
}
return students, nil
}
// ========================================
// Teacher Management
// ========================================
// CreateTeacher creates a new teacher linked to a user account
func (s *SchoolService) CreateTeacher(ctx context.Context, schoolID, userID uuid.UUID, firstName, lastName string, teacherCode, title *string) (*models.Teacher, error) {
teacher := &models.Teacher{
ID: uuid.New(),
SchoolID: schoolID,
UserID: userID,
TeacherCode: teacherCode,
Title: title,
FirstName: firstName,
LastName: lastName,
IsActive: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
query := `
INSERT INTO teachers (id, school_id, user_id, teacher_code, title, first_name, last_name, is_active, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id`
err := s.db.Pool.QueryRow(ctx, query,
teacher.ID, teacher.SchoolID, teacher.UserID, teacher.TeacherCode,
teacher.Title, teacher.FirstName, teacher.LastName,
teacher.IsActive, teacher.CreatedAt, teacher.UpdatedAt,
).Scan(&teacher.ID)
if err != nil {
return nil, fmt.Errorf("failed to create teacher: %w", err)
}
return teacher, nil
}
// GetTeacher retrieves a teacher by ID
func (s *SchoolService) GetTeacher(ctx context.Context, teacherID uuid.UUID) (*models.Teacher, error) {
query := `
SELECT id, school_id, user_id, teacher_code, title, first_name, last_name, matrix_user_id, is_active, created_at, updated_at
FROM teachers
WHERE id = $1`
teacher := &models.Teacher{}
err := s.db.Pool.QueryRow(ctx, query, teacherID).Scan(
&teacher.ID, &teacher.SchoolID, &teacher.UserID, &teacher.TeacherCode,
&teacher.Title, &teacher.FirstName, &teacher.LastName, &teacher.MatrixUserID,
&teacher.IsActive, &teacher.CreatedAt, &teacher.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to get teacher: %w", err)
}
return teacher, nil
}
// GetTeacherByUserID retrieves a teacher by their user ID
func (s *SchoolService) GetTeacherByUserID(ctx context.Context, userID uuid.UUID) (*models.Teacher, error) {
query := `
SELECT id, school_id, user_id, teacher_code, title, first_name, last_name, matrix_user_id, is_active, created_at, updated_at
FROM teachers
WHERE user_id = $1 AND is_active = true`
teacher := &models.Teacher{}
err := s.db.Pool.QueryRow(ctx, query, userID).Scan(
&teacher.ID, &teacher.SchoolID, &teacher.UserID, &teacher.TeacherCode,
&teacher.Title, &teacher.FirstName, &teacher.LastName, &teacher.MatrixUserID,
&teacher.IsActive, &teacher.CreatedAt, &teacher.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to get teacher by user ID: %w", err)
}
return teacher, nil
}
// AssignClassTeacher assigns a teacher to a class
func (s *SchoolService) AssignClassTeacher(ctx context.Context, classID, teacherID uuid.UUID, isPrimary bool) error {
query := `
INSERT INTO class_teachers (id, class_id, teacher_id, is_primary, created_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (class_id, teacher_id) DO UPDATE SET is_primary = EXCLUDED.is_primary`
_, err := s.db.Pool.Exec(ctx, query, uuid.New(), classID, teacherID, isPrimary, time.Now())
if err != nil {
return fmt.Errorf("failed to assign class teacher: %w", err)
}
return nil
}
// ========================================
// Subject Management
// ========================================
// CreateSubject creates a new subject
func (s *SchoolService) CreateSubject(ctx context.Context, schoolID uuid.UUID, name, shortName string, color *string) (*models.Subject, error) {
subject := &models.Subject{
ID: uuid.New(),
SchoolID: schoolID,
Name: name,
ShortName: shortName,
Color: color,
IsActive: true,
CreatedAt: time.Now(),
}
query := `
INSERT INTO subjects (id, school_id, name, short_name, color, is_active, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id`
err := s.db.Pool.QueryRow(ctx, query,
subject.ID, subject.SchoolID, subject.Name, subject.ShortName,
subject.Color, subject.IsActive, subject.CreatedAt,
).Scan(&subject.ID)
if err != nil {
return nil, fmt.Errorf("failed to create subject: %w", err)
}
return subject, nil
}
// ListSubjects lists all subjects for a school
func (s *SchoolService) ListSubjects(ctx context.Context, schoolID uuid.UUID) ([]models.Subject, error) {
query := `
SELECT id, school_id, name, short_name, color, is_active, created_at
FROM subjects
WHERE school_id = $1 AND is_active = true
ORDER BY name`
rows, err := s.db.Pool.Query(ctx, query, schoolID)
if err != nil {
return nil, fmt.Errorf("failed to list subjects: %w", err)
}
defer rows.Close()
var subjects []models.Subject
for rows.Next() {
var subject models.Subject
err := rows.Scan(
&subject.ID, &subject.SchoolID, &subject.Name, &subject.ShortName,
&subject.Color, &subject.IsActive, &subject.CreatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to scan subject: %w", err)
}
subjects = append(subjects, subject)
}
return subjects, nil
}
// ========================================
// Parent Onboarding
// ========================================
// GenerateParentOnboardingToken generates a QR code token for parent onboarding
func (s *SchoolService) GenerateParentOnboardingToken(ctx context.Context, schoolID, classID, studentID, createdByUserID uuid.UUID, role string) (*models.ParentOnboardingToken, error) {
// Generate secure random token
tokenBytes := make([]byte, 32)
if _, err := rand.Read(tokenBytes); err != nil {
return nil, fmt.Errorf("failed to generate token: %w", err)
}
token := hex.EncodeToString(tokenBytes)
onboardingToken := &models.ParentOnboardingToken{
ID: uuid.New(),
SchoolID: schoolID,
ClassID: classID,
StudentID: studentID,
Token: token,
Role: role,
ExpiresAt: time.Now().Add(72 * time.Hour), // Valid for 72 hours
CreatedAt: time.Now(),
CreatedBy: createdByUserID,
}
query := `
INSERT INTO parent_onboarding_tokens (id, school_id, class_id, student_id, token, role, expires_at, created_at, created_by)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id`
err := s.db.Pool.QueryRow(ctx, query,
onboardingToken.ID, onboardingToken.SchoolID, onboardingToken.ClassID,
onboardingToken.StudentID, onboardingToken.Token, onboardingToken.Role,
onboardingToken.ExpiresAt, onboardingToken.CreatedAt, onboardingToken.CreatedBy,
).Scan(&onboardingToken.ID)
if err != nil {
return nil, fmt.Errorf("failed to create onboarding token: %w", err)
}
return onboardingToken, nil
}
// ValidateOnboardingToken validates and retrieves info for an onboarding token
func (s *SchoolService) ValidateOnboardingToken(ctx context.Context, token string) (*models.ParentOnboardingToken, error) {
query := `
SELECT id, school_id, class_id, student_id, token, role, expires_at, used_at, used_by_user_id, created_at, created_by
FROM parent_onboarding_tokens
WHERE token = $1 AND used_at IS NULL AND expires_at > NOW()`
onboardingToken := &models.ParentOnboardingToken{}
err := s.db.Pool.QueryRow(ctx, query, token).Scan(
&onboardingToken.ID, &onboardingToken.SchoolID, &onboardingToken.ClassID,
&onboardingToken.StudentID, &onboardingToken.Token, &onboardingToken.Role,
&onboardingToken.ExpiresAt, &onboardingToken.UsedAt, &onboardingToken.UsedByUserID,
&onboardingToken.CreatedAt, &onboardingToken.CreatedBy,
)
if err != nil {
return nil, fmt.Errorf("invalid or expired token: %w", err)
}
return onboardingToken, nil
}
// RedeemOnboardingToken marks a token as used and creates the parent account
func (s *SchoolService) RedeemOnboardingToken(ctx context.Context, token string, userID uuid.UUID) error {
query := `
UPDATE parent_onboarding_tokens
SET used_at = NOW(), used_by_user_id = $1
WHERE token = $2 AND used_at IS NULL AND expires_at > NOW()`
result, err := s.db.Pool.Exec(ctx, query, userID, token)
if err != nil {
return fmt.Errorf("failed to redeem token: %w", err)
}
if result.RowsAffected() == 0 {
return fmt.Errorf("token not found or already used")
}
return nil
}
// ========================================
// Helper Functions
// ========================================