ilk
This commit is contained in:
587
svc/main.go
Normal file
587
svc/main.go
Normal file
@@ -0,0 +1,587 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/internal/auditlog"
|
||||
"bssapp-backend/internal/mailer"
|
||||
"bssapp-backend/middlewares"
|
||||
"bssapp-backend/permissions"
|
||||
"bssapp-backend/repository"
|
||||
"bssapp-backend/routes"
|
||||
"database/sql"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
/*
|
||||
===========================================================
|
||||
✅ CORS
|
||||
===========================================================
|
||||
*/
|
||||
func enableCORS(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:9000")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
===========================================================
|
||||
✅ V3 — Method-aware Route Auto Register
|
||||
- mk_sys_routes: (path, method, module_code, action)
|
||||
- unique: (path, method)
|
||||
- admin auto-allow: mk_sys_role_permissions (role_id=3)
|
||||
===========================================================
|
||||
*/
|
||||
func autoRegisterRouteV3(
|
||||
pg *sql.DB,
|
||||
path string,
|
||||
method string,
|
||||
module string,
|
||||
action string,
|
||||
) {
|
||||
tx, err := pg.Begin()
|
||||
if err != nil {
|
||||
log.Println("❌ TX begin error:", err)
|
||||
return
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// 1) ROUTE REGISTER (path+method)
|
||||
_, err = tx.Exec(`
|
||||
INSERT INTO mk_sys_routes
|
||||
(path, method, module_code, action)
|
||||
VALUES
|
||||
($1, $2, $3, $4)
|
||||
ON CONFLICT (path, method) DO UPDATE
|
||||
SET
|
||||
module_code = EXCLUDED.module_code,
|
||||
action = EXCLUDED.action
|
||||
`,
|
||||
path,
|
||||
method,
|
||||
module,
|
||||
action,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("❌ Route register error (%s %s): %v", method, path, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 2) ADMIN AUTO PERMISSION (module+action bazlı)
|
||||
_, err = tx.Exec(`
|
||||
INSERT INTO mk_sys_role_permissions
|
||||
(role_id, module_code, action, allowed)
|
||||
SELECT
|
||||
id,
|
||||
$1,
|
||||
$2,
|
||||
true
|
||||
FROM dfrole
|
||||
WHERE id = 3 -- ADMIN
|
||||
ON CONFLICT DO NOTHING
|
||||
`,
|
||||
module,
|
||||
action,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("❌ Admin perm seed error (%s %s): %v", method, path, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
log.Println("❌ TX commit error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("✅ Route+Perm registered → %s %s [%s:%s]",
|
||||
method, path, module, action,
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
===========================================================
|
||||
✅ V3 Route Bind Helper
|
||||
- tek satırda: autoRegister + wrap + Handle
|
||||
===========================================================
|
||||
*/
|
||||
func bindV3(
|
||||
r *mux.Router,
|
||||
pg *sql.DB,
|
||||
path string,
|
||||
method string,
|
||||
module string,
|
||||
action string,
|
||||
h http.Handler,
|
||||
) {
|
||||
// main
|
||||
autoRegisterRouteV3(pg, path, method, module, action)
|
||||
|
||||
r.Handle(path, h).Methods(method, "OPTIONS")
|
||||
}
|
||||
|
||||
/*
|
||||
===========================================================
|
||||
InitRoutes — FULL V3 (Method-aware) PERMISSION EDITION
|
||||
===========================================================
|
||||
*/
|
||||
func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router {
|
||||
|
||||
r := mux.NewRouter()
|
||||
|
||||
/*
|
||||
===========================================================
|
||||
✅ wrapV3 (method-aware):
|
||||
- AuthMiddleware (JWT)
|
||||
- panic recover
|
||||
- AuthzGuardByRoute(pgDB) => route(path+method) lookup
|
||||
===========================================================
|
||||
*/
|
||||
wrapV3 := func(h http.Handler) http.Handler {
|
||||
return middlewares.AuthMiddleware(
|
||||
pgDB,
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
log.Printf("🔥 PANIC %s %s\n%v", r.Method, r.URL.Path, rec)
|
||||
debug.PrintStack()
|
||||
http.Error(w, "internal server error", 500)
|
||||
}
|
||||
}()
|
||||
|
||||
// ✅ method-aware route guard
|
||||
middlewares.AuthzGuardByRoute(pgDB)(h).ServeHTTP(w, r)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PUBLIC (NO AUTHZ)
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/auth/login", "POST",
|
||||
"auth", "login",
|
||||
http.HandlerFunc(routes.LoginHandler(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/auth/refresh", "POST",
|
||||
"auth", "refresh",
|
||||
routes.AuthRefreshHandler(pgDB),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// SYSTEM
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/password/change", "POST",
|
||||
"system", "update",
|
||||
wrapV3(http.HandlerFunc(routes.FirstPasswordChangeHandler(pgDB))),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/activity-logs", "GET",
|
||||
"user", "view",
|
||||
wrapV3(routes.AdminActivityLogsHandler(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/test-mail", "POST",
|
||||
"user", "insert",
|
||||
wrapV3(routes.TestMailHandler(ml)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// PERMISSIONS
|
||||
// ============================================================
|
||||
rolePerm := "/api/roles/{id}/permissions"
|
||||
|
||||
bindV3(r, pgDB,
|
||||
rolePerm, "GET",
|
||||
"user", "update",
|
||||
wrapV3(routes.GetRolePermissionMatrix(pgDB)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
rolePerm, "POST",
|
||||
"user", "update",
|
||||
wrapV3(routes.SaveRolePermissionMatrix(pgDB)),
|
||||
)
|
||||
|
||||
userPerm := "/api/users/{id}/permissions"
|
||||
|
||||
bindV3(r, pgDB,
|
||||
userPerm, "GET",
|
||||
"user", "update",
|
||||
wrapV3(routes.GetUserPermissionsHandler(pgDB)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
userPerm, "POST",
|
||||
"user", "update",
|
||||
wrapV3(routes.SaveUserPermissionsHandler(pgDB)),
|
||||
)
|
||||
|
||||
// ✅ permissions/routes (system:view)
|
||||
bindV3(r, pgDB,
|
||||
"/api/permissions/routes", "GET",
|
||||
"system", "view",
|
||||
wrapV3(routes.GetUserRoutePermissionsHandler(pgDB)),
|
||||
)
|
||||
|
||||
// ✅ permissions/effective (system:view)
|
||||
bindV3(r, pgDB,
|
||||
"/api/permissions/effective", "GET",
|
||||
"system", "view",
|
||||
wrapV3(routes.GetMyEffectivePermissions(pgDB)),
|
||||
)
|
||||
|
||||
// ✅ my permission matrix (system:view) (Senin tabloda user:view görünüyor; düzeltiyoruz)
|
||||
bindV3(r, pgDB,
|
||||
"/api/permissions/matrix", "GET",
|
||||
"system", "view",
|
||||
wrapV3(routes.GetMyPermissionMatrix(pgDB)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// ROLE + DEPARTMENT PERMISSIONS
|
||||
// ============================================================
|
||||
rdPerm := "/api/roles/{roleId}/departments/{deptCode}/permissions"
|
||||
rdHandler := routes.NewRoleDepartmentPermissionHandler(pgDB)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
rdPerm, "GET",
|
||||
"user", "update",
|
||||
wrapV3(http.HandlerFunc(rdHandler.Get)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
rdPerm, "POST",
|
||||
"user", "update",
|
||||
wrapV3(http.HandlerFunc(rdHandler.Save)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// USERS
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/users/list", "GET",
|
||||
"user", "view",
|
||||
wrapV3(routes.UserListRoute(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/users", "POST",
|
||||
"user", "insert",
|
||||
wrapV3(routes.UserCreateRoute(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/users/{id}", "GET",
|
||||
"user", "update",
|
||||
wrapV3(routes.UserDetailRoute(pgDB)),
|
||||
)
|
||||
bindV3(r, pgDB,
|
||||
"/api/users/{id}", "PUT",
|
||||
"user", "update",
|
||||
wrapV3(routes.UserDetailRoute(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/users/{id}/admin-reset-password", "POST",
|
||||
"user", "update",
|
||||
wrapV3(routes.AdminResetPasswordHandler(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/users/{id}/send-password-mail", "POST",
|
||||
"user", "update",
|
||||
wrapV3(routes.SendPasswordResetMailHandler(pgDB, ml)),
|
||||
)
|
||||
|
||||
// ✅ eski kısayol create endpoint (senin eski main.go’da vardı)
|
||||
bindV3(r, pgDB,
|
||||
"/api/users/create", "POST",
|
||||
"user", "insert",
|
||||
wrapV3(routes.UserCreateRoute(pgDB)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// LOOKUPS
|
||||
// ============================================================
|
||||
lookups := map[string]http.Handler{
|
||||
"/api/lookups/roles": routes.GetRoleLookupRoute(pgDB),
|
||||
"/api/lookups/departments": routes.GetDepartmentLookupRoute(pgDB),
|
||||
"/api/lookups/nebim-users": routes.GetNebimUserLookupRoute(pgDB),
|
||||
"/api/lookups/piyasalar": routes.GetPiyasaLookupRoute(pgDB),
|
||||
"/api/lookups/users-perm": routes.GetUsersForPermissionSelectRoute(pgDB),
|
||||
"/api/lookups/roles-perm": routes.GetRolesForPermissionSelectRoute(pgDB),
|
||||
"/api/lookups/departments-perm": routes.GetDepartmentsForPermissionSelectRoute(pgDB),
|
||||
"/api/lookups/modules": routes.GetModuleLookupRoute(pgDB),
|
||||
}
|
||||
|
||||
for path, handler := range lookups {
|
||||
bindV3(r, pgDB,
|
||||
path, "GET",
|
||||
"user", "view",
|
||||
wrapV3(handler),
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CUSTOMER
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/accounts", "GET",
|
||||
"customer", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetAccountsHandler)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/customer-list", "GET",
|
||||
"customer", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetCustomerListHandler)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// FINANCE
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/today-currency", "GET",
|
||||
"finance", "view",
|
||||
wrapV3(routes.GetTodayCurrencyV3Handler(mssql)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/export-pdf", "GET",
|
||||
"finance", "export",
|
||||
wrapV3(routes.ExportPDFHandler(mssql)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/exportstamentheaderreport-pdf", "GET",
|
||||
"finance", "export",
|
||||
wrapV3(routes.ExportStatementHeaderReportPDFHandler(mssql)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// REPORT (STATEMENTS)
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/statements", "GET",
|
||||
"finance", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetStatementHeadersHandler)),
|
||||
)
|
||||
|
||||
// ⚠️ Senin handler: GetStatementDetailsHandler vars["accountCode"] bekliyor,
|
||||
// route’da {id} var. Burada, DB route’unu ve path’i bozmayalım diye {id}’yi koruyorum,
|
||||
// ama handler içinde accountCode := mux.Vars(r)["id"] yapman daha doğru.
|
||||
bindV3(r, pgDB,
|
||||
"/api/statements/{id}/details", "GET",
|
||||
"finance", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetStatementDetailsHandler)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// ORDER
|
||||
// ============================================================
|
||||
orderRoutes := []struct {
|
||||
Path string
|
||||
Method string
|
||||
Action string
|
||||
Handle http.Handler
|
||||
}{
|
||||
{"/api/order/create", "POST", "insert", routes.CreateOrderHandler(pgDB, mssql)},
|
||||
{"/api/order/update", "POST", "update", http.HandlerFunc(routes.UpdateOrderHandler)},
|
||||
{"/api/order/get/{id}", "GET", "view", routes.GetOrderByIDHandler(mssql)},
|
||||
{"/api/orders/list", "GET", "view", routes.OrderListRoute(mssql)},
|
||||
{"/api/orders/export", "GET", "export", routes.OrderListExcelRoute(mssql)},
|
||||
{"/api/order/check/{id}", "GET", "view", routes.OrderExistsHandler(mssql)},
|
||||
{"/api/order/validate", "POST", "insert", routes.ValidateOrderHandler(mssql)},
|
||||
{"/api/order/pdf/{id}", "GET", "export", routes.OrderPDFHandler(mssql)},
|
||||
{"/api/order-inventory", "GET", "view", http.HandlerFunc(routes.GetOrderInventoryHandler)},
|
||||
{"/api/orderpricelistb2b", "GET", "view", routes.GetOrderPriceListB2BHandler(pgDB, mssql)},
|
||||
{"/api/min-price", "GET", "view", routes.GetOrderPriceListB2BHandler(pgDB, mssql)},
|
||||
}
|
||||
|
||||
for _, rt := range orderRoutes {
|
||||
bindV3(r, pgDB,
|
||||
rt.Path, rt.Method,
|
||||
"order", rt.Action,
|
||||
wrapV3(rt.Handle),
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PRODUCTS (✅ handler mapping fix)
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/products", "GET",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetProductListHandler)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/product-detail", "GET",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetProductDetailHandler)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/product-colors", "GET",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetProductColorsHandler)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/product-colorsize", "GET",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetProductColorSizesHandler)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/product-secondcolor", "GET",
|
||||
"order", "view",
|
||||
wrapV3(http.HandlerFunc(routes.GetProductSecondColorsHandler)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// ROLE MANAGEMENT
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/roles", "GET",
|
||||
"user", "view",
|
||||
wrapV3(routes.GetRolesHandler(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/departments", "GET",
|
||||
"user", "view",
|
||||
wrapV3(routes.GetDepartmentsHandler(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/piyasalar", "GET",
|
||||
"user", "view",
|
||||
wrapV3(routes.GetPiyasalarHandler(pgDB)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// ROLE RELATIONS
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/roles/{id}/departments", "POST",
|
||||
"user", "update",
|
||||
wrapV3(routes.UpdateRoleDepartmentsHandler(pgDB)),
|
||||
)
|
||||
|
||||
bindV3(r, pgDB,
|
||||
"/api/roles/{id}/piyasalar", "POST",
|
||||
"user", "update",
|
||||
wrapV3(routes.UpdateRolePiyasalarHandler(pgDB)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// USER ↔ ROLE
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/users/{id}/roles", "POST",
|
||||
"user", "update",
|
||||
wrapV3(routes.UpdateUserRolesHandler(pgDB)),
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// ADMIN EXTRA (eski main.go’da vardı, yeni sisteme alındı)
|
||||
// ============================================================
|
||||
bindV3(r, pgDB,
|
||||
"/api/admin/users/{id}/piyasa-sync", "POST",
|
||||
"admin", "user.update",
|
||||
wrapV3(http.HandlerFunc(routes.AdminSyncUserPiyasaHandler)),
|
||||
)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Println("🔥🔥🔥 BSSAPP BACKEND STARTED — LOGIN ROUTE SHOULD EXIST 🔥🔥🔥")
|
||||
|
||||
// -------------------------------------------------------
|
||||
// 🔑 ENV
|
||||
// -------------------------------------------------------
|
||||
if err := godotenv.Load(".env", "mail.env"); err != nil {
|
||||
log.Println("⚠️ .env / mail.env bulunamadı")
|
||||
}
|
||||
|
||||
jwtSecret := os.Getenv("JWT_SECRET")
|
||||
if len(jwtSecret) < 10 {
|
||||
log.Fatal("❌ JWT_SECRET tanımlı değil veya çok kısa (min 10 karakter)")
|
||||
}
|
||||
log.Println("🔐 JWT_SECRET yüklendi")
|
||||
|
||||
// -------------------------------------------------------
|
||||
// 🔗 DATABASE
|
||||
// -------------------------------------------------------
|
||||
db.ConnectMSSQL()
|
||||
|
||||
pgDB, err := db.ConnectPostgres()
|
||||
if err != nil {
|
||||
log.Fatalf("❌ PostgreSQL bağlantı hatası: %v", err)
|
||||
}
|
||||
defer pgDB.Close()
|
||||
|
||||
// -------------------------------------------------------
|
||||
// 🔐 ADMIN ROLE + DEPARTMENT AUTO SEED
|
||||
// -------------------------------------------------------
|
||||
if err := permissions.SeedAdminRoleDepartments(pgDB); err != nil {
|
||||
log.Println("❌ Admin dept seed failed:", err)
|
||||
} else {
|
||||
log.Println("✅ Admin dept permissions seeded")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
// 🕵️ AUDIT LOG INIT
|
||||
// -------------------------------------------------------
|
||||
auditlog.Init(pgDB, 1000)
|
||||
log.Println("🕵️ AuditLog sistemi başlatıldı (buffer=1000)")
|
||||
|
||||
// -------------------------------------------------------
|
||||
// ✉️ MAILER INIT
|
||||
// -------------------------------------------------------
|
||||
graphMailer, err := mailer.NewGraphMailer()
|
||||
if err != nil {
|
||||
log.Fatalf("❌ Graph Mailer init error: %v", err)
|
||||
}
|
||||
log.Println("✉️ Graph Mailer hazır")
|
||||
|
||||
// -------------------------------------------------------
|
||||
// 👤 DEBUG
|
||||
// -------------------------------------------------------
|
||||
repository.NewUserRepository(pgDB).DebugListUsers()
|
||||
|
||||
// -------------------------------------------------------
|
||||
// 🌍 SERVER
|
||||
// -------------------------------------------------------
|
||||
router := InitRoutes(pgDB, db.MssqlDB, graphMailer)
|
||||
|
||||
handler := enableCORS(
|
||||
middlewares.GlobalAuthMiddleware(
|
||||
pgDB,
|
||||
middlewares.RequestLogger(router),
|
||||
),
|
||||
)
|
||||
|
||||
log.Println("✅ Server çalışıyor: http://localhost:8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", handler))
|
||||
}
|
||||
Reference in New Issue
Block a user