package routes import ( "bssapp-backend/auth" "bssapp-backend/internal/auditlog" "bssapp-backend/permissions" "bssapp-backend/queries" "database/sql" "encoding/json" "fmt" "log" "net/http" "strconv" "github.com/gorilla/mux" ) type IdTitleOption struct { ID string `json:"id"` Title string `json:"title"` } type Row struct { Route string `json:"route"` CanAccess bool `json:"can_access"` } type RoleDepartmentPermissionHandler struct { DB *sql.DB Repo *permissions.RoleDepartmentPermissionRepo } func NewRoleDepartmentPermissionHandler(db *sql.DB) *RoleDepartmentPermissionHandler { return &RoleDepartmentPermissionHandler{ DB: db, // ✅ EKLENDİ Repo: permissions.NewRoleDepartmentPermissionRepo(db), } } /* ====================================================== GET ====================================================== */ func (h *RoleDepartmentPermissionHandler) Get(w http.ResponseWriter, r *http.Request) { claims, ok := auth.GetClaimsFromContext(r.Context()) if !ok || claims == nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return } vars := mux.Vars(r) roleID, err := strconv.Atoi(vars["roleId"]) if err != nil || roleID <= 0 { http.Error(w, "invalid roleId", http.StatusBadRequest) return } deptCode := vars["deptCode"] if deptCode == "" { http.Error(w, "invalid deptCode", http.StatusBadRequest) return } list, err := h.Repo.Get(roleID, deptCode) if err != nil { http.Error(w, "db error", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") _ = json.NewEncoder(w).Encode(list) } /* ====================================================== POST ====================================================== */ func (h *RoleDepartmentPermissionHandler) Save(w http.ResponseWriter, r *http.Request) { claims, ok := auth.GetClaimsFromContext(r.Context()) if !ok || claims == nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return } vars := mux.Vars(r) roleID, err := strconv.Atoi(vars["roleId"]) if err != nil || roleID <= 0 { http.Error(w, "invalid roleId", http.StatusBadRequest) return } deptCode := vars["deptCode"] if deptCode == "" { http.Error(w, "invalid deptCode", http.StatusBadRequest) return } var list []permissions.RoleDepartmentPermission if err := json.NewDecoder(r.Body).Decode(&list); err != nil { http.Error(w, "bad payload", http.StatusBadRequest) return } // ================= OLD ================= oldRows, err := h.Repo.Get(roleID, deptCode) if err != nil { log.Println("OLD PERM LOAD ERROR:", err) } oldMap := map[string]bool{} for _, p := range oldRows { key := p.Module + ":" + p.Action oldMap[key] = p.Allowed } // ================= DIFF ================= var changes []map[string]any for _, p := range list { key := p.Module + ":" + p.Action oldVal := oldMap[key] if oldVal != p.Allowed { changes = append(changes, map[string]any{ "module": p.Module, "action": p.Action, "before": oldVal, "after": p.Allowed, }) } } // ================= SAVE ================= if err := h.Repo.Save(roleID, deptCode, list); err != nil { http.Error(w, "save error", http.StatusInternalServerError) return } // ================= AUDIT ================= if len(changes) > 0 { auditlog.Enqueue(r.Context(), auditlog.ActivityLog{ ActionType: "role_department_permission_change", ActionCategory: "role_permission", ActionTarget: fmt.Sprintf( "/api/roles/%d/departments/%s/permissions", roleID, deptCode, ), Description: "role+department permissions updated", Username: claims.Username, RoleCode: claims.RoleCode, DfUsrID: int64(claims.ID), ChangeBefore: map[string]any{ "permissions": oldRows, }, ChangeAfter: map[string]any{ "changes": changes, }, IsSuccess: true, }) } w.Header().Set("Content-Type", "application/json; charset=utf-8") _ = json.NewEncoder(w).Encode(map[string]bool{"success": true}) } func GetModuleLookupRoute(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { rows, err := db.Query(queries.GetModuleLookup) if err != nil { http.Error(w, "db error", 500) return } defer rows.Close() type Row struct { Value string `json:"value"` Label string `json:"label"` } var list []Row for rows.Next() { var r Row if err := rows.Scan( &r.Value, &r.Label, ); err != nil { http.Error(w, "scan error", 500) return } list = append(list, r) } json.NewEncoder(w).Encode(list) } } func GetRolesForPermissionSelectRoute(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") rows, err := db.Query(queries.GetRolesForPermissionSelect) if err != nil { http.Error(w, "roles select error", 500) return } defer rows.Close() list := make([]IdTitleOption, 0) for rows.Next() { var o IdTitleOption if err := rows.Scan(&o.ID, &o.Title); err == nil { list = append(list, o) } } _ = json.NewEncoder(w).Encode(list) } } func GetDepartmentsForPermissionSelectRoute(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") rows, err := db.Query(queries.GetDepartmentsForPermissionSelect) if err != nil { http.Error(w, "departments select error", 500) return } defer rows.Close() list := make([]IdTitleOption, 0) for rows.Next() { var o IdTitleOption if err := rows.Scan(&o.ID, &o.Title); err == nil { list = append(list, o) } } _ = json.NewEncoder(w).Encode(list) } } func GetUsersForPermissionSelectRoute(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") rows, err := db.Query(queries.GetUserLookupForPermission) if err != nil { http.Error(w, "users lookup error", http.StatusInternalServerError) return } defer rows.Close() list := make([]IdTitleOption, 0, 128) for rows.Next() { var o IdTitleOption if err := rows.Scan(&o.ID, &o.Title); err == nil { list = append(list, o) } } _ = json.NewEncoder(w).Encode(list) } } func (h *PermissionHandler) GetUserOverrides(w http.ResponseWriter, r *http.Request) { claims, ok := auth.GetClaimsFromContext(r.Context()) if !ok || claims == nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return } userID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64) if err != nil || userID <= 0 { http.Error(w, "invalid id", http.StatusBadRequest) return } list, err := h.Repo.GetUserOverridesByUserID(userID) if err != nil { log.Println("❌ USER OVERRIDE LOAD ERROR:", err) http.Error(w, "db error", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") _ = json.NewEncoder(w).Encode(list) } func GetUserRoutePermissionsHandler(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") claims, ok := auth.GetClaimsFromContext(r.Context()) if !ok || claims == nil { http.Error(w, "unauthorized", 401) return } repo := permissions.NewPermissionRepository(db) // JWT’den departmanlar depts := claims.DepartmentCodes rows, err := db.Query(` SELECT DISTINCT module_code, action, path FROM mk_sys_routes `) if err != nil { http.Error(w, err.Error(), 500) return } defer rows.Close() type Row struct { Route string `json:"route"` CanAccess bool `json:"can_access"` } list := make([]Row, 0, 64) for rows.Next() { var module, action, path string if err := rows.Scan( &module, &action, &path, ); err != nil { continue } allowed, err := repo.ResolvePermissionChain( int64(claims.ID), int64(claims.RoleID), depts, module, action, ) if err != nil { log.Println("PERM RESOLVE ERROR:", err) continue } list = append(list, Row{ Route: path, CanAccess: allowed, }) } _ = json.NewEncoder(w).Encode(list) } } func GetMyEffectivePermissions(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") claims, ok := auth.GetClaimsFromContext(r.Context()) if !ok || claims == nil { http.Error(w, "unauthorized", 401) return } repo := permissions.NewPermissionRepository(db) // ✅ JWT'DEN DEPARTMENTS depts := claims.DepartmentCodes log.Printf("🧪 EFFECTIVE PERM | user=%d role=%d depts=%v", claims.ID, claims.RoleID, depts, ) // all system perms all, err := db.Query(` SELECT DISTINCT module_code, action FROM mk_sys_routes `) if err != nil { http.Error(w, err.Error(), 500) return } defer all.Close() type Row struct { Module string `json:"module"` Action string `json:"action"` Allowed bool `json:"allowed"` } list := make([]Row, 0, 128) for all.Next() { var m, a string if err := all.Scan(&m, &a); err != nil { continue } allowed, err := repo.ResolvePermissionChain( int64(claims.ID), int64(claims.RoleID), depts, m, a, ) if err != nil { continue } list = append(list, Row{ Module: m, Action: a, Allowed: allowed, }) } _ = json.NewEncoder(w).Encode(list) } }