This commit is contained in:
2026-02-11 17:46:22 +03:00
commit eacfacb13b
266 changed files with 51337 additions and 0 deletions

View File

@@ -0,0 +1,440 @@
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)
// JWTden 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)
}
}