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 NewStandardizationRepository(db *sql.DB) *StandardizationRepository {
|
||||
}
|
||||
|
||||
func (r *StandardizationRepository) GetStandardsBodiesWithProjects(ctx context.Context) ([]models.StandardsBodyWithProjects, error) {
|
||||
// Query bodies ordered by display_order
|
||||
bodyQuery := `
|
||||
SELECT id, org, full_name, scope, period, role, display_order
|
||||
FROM standards_bodies
|
||||
@@ -30,7 +29,7 @@ func (r *StandardizationRepository) GetStandardsBodiesWithProjects(ctx context.C
|
||||
defer bodyRows.Close()
|
||||
|
||||
var bodies []models.StandardsBodyWithProjects
|
||||
bodyMap := make(map[int]int) // body_id -> index in bodies
|
||||
bodyMap := make(map[int]int)
|
||||
|
||||
for bodyRows.Next() {
|
||||
var b models.StandardsBodyWithProjects
|
||||
@@ -45,7 +44,6 @@ func (r *StandardizationRepository) GetStandardsBodiesWithProjects(ctx context.C
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Query documents ordered by body_id, and preserves curated insertion order with MIN(id) ASC
|
||||
docQuery := `
|
||||
SELECT id, body_id, wg, project_name, title, doc_ref, status, published_at
|
||||
FROM standard_documents
|
||||
@@ -57,13 +55,12 @@ func (r *StandardizationRepository) GetStandardsBodiesWithProjects(ctx context.C
|
||||
}
|
||||
defer docRows.Close()
|
||||
|
||||
// Group documents into projects under each body preserving curated order
|
||||
type projectKey struct {
|
||||
bodyID int
|
||||
wg string
|
||||
projectName string
|
||||
}
|
||||
projectMap := make(map[projectKey]int) // projectKey -> index in bodies[bIdx].Projects
|
||||
projectMap := make(map[projectKey]int)
|
||||
|
||||
for docRows.Next() {
|
||||
var d models.StandardDocument
|
||||
@@ -93,3 +90,83 @@ func (r *StandardizationRepository) GetStandardsBodiesWithProjects(ctx context.C
|
||||
|
||||
return bodies, docRows.Err()
|
||||
}
|
||||
|
||||
func (r *StandardizationRepository) CreateStandardsBody(ctx context.Context, b models.StandardsBody) (int, error) {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO standards_bodies (org, full_name, scope, period, role, display_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`, b.Org, b.FullName, b.Scope, b.Period, b.Role, b.DisplayOrder)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("insert standards body: %w", err)
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
return int(id), err
|
||||
}
|
||||
|
||||
func (r *StandardizationRepository) UpdateStandardsBody(ctx context.Context, id int, b models.StandardsBody) error {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
UPDATE standards_bodies
|
||||
SET org = ?, full_name = ?, scope = ?, period = ?, role = ?, display_order = ?
|
||||
WHERE id = ?
|
||||
`, b.Org, b.FullName, b.Scope, b.Period, b.Role, b.DisplayOrder, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update standards body: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *StandardizationRepository) DeleteStandardsBody(ctx context.Context, id int) error {
|
||||
res, err := r.db.ExecContext(ctx, `DELETE FROM standards_bodies WHERE id = ?`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete standards body: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *StandardizationRepository) CreateStandardDocument(ctx context.Context, d models.StandardDocument) (int, error) {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO standard_documents (body_id, wg, project_name, title, doc_ref, status, published_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`, d.BodyID, d.WG, d.ProjectName, d.Title, d.DocRef, d.Status, d.PublishedAt)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("insert standard document: %w", err)
|
||||
}
|
||||
id, err := res.LastInsertId()
|
||||
return int(id), err
|
||||
}
|
||||
|
||||
func (r *StandardizationRepository) UpdateStandardDocument(ctx context.Context, id int, d models.StandardDocument) error {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
UPDATE standard_documents
|
||||
SET wg = ?, project_name = ?, title = ?, doc_ref = ?, status = ?, published_at = ?
|
||||
WHERE id = ?
|
||||
`, d.WG, d.ProjectName, d.Title, d.DocRef, d.Status, d.PublishedAt, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update standard document: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *StandardizationRepository) DeleteStandardDocument(ctx context.Context, id int) error {
|
||||
res, err := r.db.ExecContext(ctx, `DELETE FROM standard_documents WHERE id = ?`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete standard document: %w", err)
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if err == nil && rows == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user