Fix: Remove broken getKlausurApiUrl and clean up empty lines
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
sed replacement left orphaned hostname references in story page and empty lines in getApiBase functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,6 @@ package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/breakpilot/edu-search-service/internal/policy"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -349,289 +348,6 @@ func (h *PolicyHandler) UpdateOperationPermission(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, op)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// PII RULES
|
||||
// =============================================================================
|
||||
|
||||
// ListPIIRules returns all PII detection rules.
|
||||
func (h *PolicyHandler) ListPIIRules(c *gin.Context) {
|
||||
activeOnly := c.Query("active_only") == "true"
|
||||
|
||||
rules, err := h.store.ListPIIRules(c.Request.Context(), activeOnly)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list PII rules", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"rules": rules,
|
||||
"total": len(rules),
|
||||
})
|
||||
}
|
||||
|
||||
// GetPIIRule returns a single PII rule by ID.
|
||||
func (h *PolicyHandler) GetPIIRule(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid PII rule ID"})
|
||||
return
|
||||
}
|
||||
|
||||
rule, err := h.store.GetPIIRule(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get PII rule", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
if rule == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "PII rule not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, rule)
|
||||
}
|
||||
|
||||
// CreatePIIRule creates a new PII detection rule.
|
||||
func (h *PolicyHandler) CreatePIIRule(c *gin.Context) {
|
||||
var req policy.CreatePIIRuleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
rule, err := h.store.CreatePIIRule(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create PII rule", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Log audit
|
||||
userEmail := getUserEmail(c)
|
||||
h.enforcer.LogChange(c.Request.Context(), policy.AuditActionCreate, policy.AuditEntityPIIRule, &rule.ID, nil, rule, userEmail)
|
||||
|
||||
c.JSON(http.StatusCreated, rule)
|
||||
}
|
||||
|
||||
// UpdatePIIRule updates an existing PII rule.
|
||||
func (h *PolicyHandler) UpdatePIIRule(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid PII rule ID"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get old value for audit
|
||||
oldRule, err := h.store.GetPIIRule(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get PII rule", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
if oldRule == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "PII rule not found"})
|
||||
return
|
||||
}
|
||||
|
||||
var req policy.UpdatePIIRuleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
rule, err := h.store.UpdatePIIRule(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update PII rule", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Log audit
|
||||
userEmail := getUserEmail(c)
|
||||
h.enforcer.LogChange(c.Request.Context(), policy.AuditActionUpdate, policy.AuditEntityPIIRule, &rule.ID, oldRule, rule, userEmail)
|
||||
|
||||
c.JSON(http.StatusOK, rule)
|
||||
}
|
||||
|
||||
// DeletePIIRule deletes a PII rule.
|
||||
func (h *PolicyHandler) DeletePIIRule(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid PII rule ID"})
|
||||
return
|
||||
}
|
||||
|
||||
// Get rule for audit before deletion
|
||||
rule, err := h.store.GetPIIRule(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get PII rule", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
if rule == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "PII rule not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.store.DeletePIIRule(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete PII rule", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Log audit
|
||||
userEmail := getUserEmail(c)
|
||||
h.enforcer.LogChange(c.Request.Context(), policy.AuditActionDelete, policy.AuditEntityPIIRule, &id, rule, nil, userEmail)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"deleted": true, "id": id})
|
||||
}
|
||||
|
||||
// TestPIIRules tests PII detection against sample text.
|
||||
func (h *PolicyHandler) TestPIIRules(c *gin.Context) {
|
||||
var req policy.PIITestRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.enforcer.DetectPII(c.Request.Context(), req.Text)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to test PII detection", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// AUDIT & COMPLIANCE
|
||||
// =============================================================================
|
||||
|
||||
// ListAuditLogs returns audit log entries.
|
||||
func (h *PolicyHandler) ListAuditLogs(c *gin.Context) {
|
||||
var filter policy.AuditLogFilter
|
||||
if err := c.ShouldBindQuery(&filter); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if filter.Limit <= 0 || filter.Limit > 500 {
|
||||
filter.Limit = 100
|
||||
}
|
||||
|
||||
logs, total, err := h.store.ListAuditLogs(c.Request.Context(), &filter)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list audit logs", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"logs": logs,
|
||||
"total": total,
|
||||
"limit": filter.Limit,
|
||||
"offset": filter.Offset,
|
||||
})
|
||||
}
|
||||
|
||||
// ListBlockedContent returns blocked content log entries.
|
||||
func (h *PolicyHandler) ListBlockedContent(c *gin.Context) {
|
||||
var filter policy.BlockedContentFilter
|
||||
if err := c.ShouldBindQuery(&filter); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if filter.Limit <= 0 || filter.Limit > 500 {
|
||||
filter.Limit = 100
|
||||
}
|
||||
|
||||
logs, total, err := h.store.ListBlockedContent(c.Request.Context(), &filter)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list blocked content", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"blocked": logs,
|
||||
"total": total,
|
||||
"limit": filter.Limit,
|
||||
"offset": filter.Offset,
|
||||
})
|
||||
}
|
||||
|
||||
// CheckCompliance performs a compliance check for a URL.
|
||||
func (h *PolicyHandler) CheckCompliance(c *gin.Context) {
|
||||
var req policy.CheckComplianceRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.enforcer.CheckCompliance(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check compliance", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// GetPolicyStats returns aggregated statistics.
|
||||
func (h *PolicyHandler) GetPolicyStats(c *gin.Context) {
|
||||
stats, err := h.store.GetStats(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get stats", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
|
||||
// GenerateComplianceReport generates an audit report.
|
||||
func (h *PolicyHandler) GenerateComplianceReport(c *gin.Context) {
|
||||
var auditFilter policy.AuditLogFilter
|
||||
var blockedFilter policy.BlockedContentFilter
|
||||
|
||||
// Parse date filters
|
||||
fromStr := c.Query("from")
|
||||
toStr := c.Query("to")
|
||||
|
||||
if fromStr != "" {
|
||||
from, err := time.Parse("2006-01-02", fromStr)
|
||||
if err == nil {
|
||||
auditFilter.FromDate = &from
|
||||
blockedFilter.FromDate = &from
|
||||
}
|
||||
}
|
||||
|
||||
if toStr != "" {
|
||||
to, err := time.Parse("2006-01-02", toStr)
|
||||
if err == nil {
|
||||
// Add 1 day to include the end date
|
||||
to = to.Add(24 * time.Hour)
|
||||
auditFilter.ToDate = &to
|
||||
blockedFilter.ToDate = &to
|
||||
}
|
||||
}
|
||||
|
||||
// No limit for report
|
||||
auditFilter.Limit = 10000
|
||||
blockedFilter.Limit = 10000
|
||||
|
||||
auditor := policy.NewAuditor(h.store)
|
||||
report, err := auditor.GenerateAuditReport(c.Request.Context(), &auditFilter, &blockedFilter)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate report", "details": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Set filename for download
|
||||
format := c.Query("format")
|
||||
if format == "download" {
|
||||
filename := "compliance-report-" + time.Now().Format("2006-01-02") + ".json"
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Header("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, report)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HELPERS
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user