package main import ( "encoding/json" "fmt" "log" "net/http" "os" "verofy-backend/models" "verofy-backend/qc" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/joho/godotenv" "gorm.io/driver/postgres" "gorm.io/gorm" ) var db *gorm.DB func getEnv(key, fallback string) string { if value := os.Getenv(key); value != "" { return value } return fallback } func initDB() { if err := godotenv.Load(); err != nil { log.Println("No .env file found") } dsn := fmt.Sprintf( "host=%s user=%s password=%s dbname=%s port=%s sslmode=require", getEnv("DB_HOST", "localhost"), getEnv("DB_USER", "postgres"), getEnv("DB_PASS", ""), getEnv("DB_NAME", "verofy"), getEnv("DB_PORT", "5432"), ) var err error db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("Failed to connect to database:", err) } } // Helper function to parse geometry from json.RawMessage func parseGeometry(rawGeometry json.RawMessage) interface{} { if len(rawGeometry) == 0 { return nil } var geometry interface{} if err := json.Unmarshal(rawGeometry, &geometry); err != nil { log.Printf("Failed to parse geometry: %v", err) return nil } return geometry } func main() { initDB() // Define configuration variables schema := getEnv("SCHEMA_NAME", "eli_test") segmentTable := getEnv("SEGMENT_TABLE", "segment2") zoneCol := getEnv("ZONE_COLUMN", "group_1") mapIDCol := getEnv("MAPID_COLUMN", "mapid") idCol := getEnv("ID_COLUMN", "id") qcFlagCol := getEnv("QCFLAG_COLUMN", "qc_flag") serverPort := getEnv("SERVER_PORT", "8080") router := gin.Default() router.Use(cors.Default()) router.Static("/static", "../Frontend") router.GET("/", func(c *gin.Context) { c.File("../Frontend/index.html") }) // Register QC routes qc.GraphConnectivityRoute(router, db, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol) qc.SingleSpanRoute(router, db, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol) qc.SiteConnectivityRoute(router, db, schema) qc.UndergroundEndpointsRoute(router, db, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol) qc.AerialEndpointsRoute(router, db, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol) qc.ZoneContainmentRoute(router, db, schema, segmentTable, mapIDCol, zoneCol, idCol, qcFlagCol) router.GET("/api/markets", func(c *gin.Context) { var markets []models.MarketOption table := fmt.Sprintf("%s.map_projects", schema) db.Table(table).Select("mapid, TRIM(project) as project").Where("mapid IS NOT NULL").Order("project").Scan(&markets) c.JSON(http.StatusOK, markets) }) router.GET("/api/zones", func(c *gin.Context) { mapID := c.Query("map_id") if mapID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Missing map_id query parameter"}) return } var zones []string table := fmt.Sprintf("%s.%s", schema, segmentTable) db.Table(table).Where(fmt.Sprintf("%s = ? AND %s IS NOT NULL", mapIDCol, zoneCol), mapID).Distinct(zoneCol).Pluck(zoneCol, &zones) c.JSON(http.StatusOK, zones) }) router.GET("/api/segments", func(c *gin.Context) { mapID := c.Query("map_id") zone := c.Query("zone") var segments []struct { ID0 int `gorm:"column:id_0"` MapID int `gorm:"column:mapid"` SegmentType string `gorm:"column:segment_type"` SegmentStatus string `gorm:"column:segment_status"` ID int `gorm:"column:id"` ProtectionStatus string `gorm:"column:protection_status"` QCFlag string `gorm:"column:qc_flag"` Group1 *string `gorm:"column:group_1"` Geometry json.RawMessage `gorm:"column:geometry"` } table := fmt.Sprintf("%s.%s", schema, segmentTable) query := db.Table(table).Select(fmt.Sprintf("id_0, %s, segment_type, segment_status, %s, protection_status, %s, \"%s\" as group_1, ST_AsGeoJSON(ST_Transform(geom, 4326))::json AS geometry", mapIDCol, idCol, qcFlagCol, zoneCol)) if mapID != "" { query = query.Where(fmt.Sprintf("%s = ?", mapIDCol), mapID) } if zone != "" { query = query.Where(fmt.Sprintf("\"%s\" = ?", zoneCol), zone) } query.Find(&segments) features := []map[string]interface{}{} for _, s := range segments { features = append(features, map[string]interface{}{ "type": "Feature", "geometry": parseGeometry(s.Geometry), "properties": map[string]interface{}{ "id_0": s.ID0, "mapid": s.MapID, "segment_type": s.SegmentType, "segment_status": s.SegmentStatus, "id": s.ID, "protection_status": s.ProtectionStatus, "qc_flag": s.QCFlag, "group_1": s.Group1, }, }) } c.JSON(http.StatusOK, map[string]interface{}{ "type": "FeatureCollection", "features": features, }) }) // SITES router.GET("/api/sites", func(c *gin.Context) { mapID := c.Query("mapprojectid") var sites []struct { GID int `gorm:"column:gid"` ID *int `gorm:"column:id"` MapProjectID *int `gorm:"column:mapprojectid"` Name *string `gorm:"column:name"` Address1 *string `gorm:"column:address"` City *string `gorm:"column:city"` State *string `gorm:"column:state"` Zip *string `gorm:"column:zip"` Group1 *string `gorm:"column:group1"` Geometry json.RawMessage `gorm:"column:geometry"` } table := fmt.Sprintf("%s.sites", schema) query := db.Table(table).Select("gid, id, \"MapProjectID\" as mapprojectid, \"Name\" as name, \"Address1\" as address, \"City\" as city, \"State\" as state, \"Zip\" as zip, \"Group 1\" as group1, ST_AsGeoJSON(geometry)::json AS geometry") if mapID != "" { query = query.Where("\"MapProjectID\" = ?", mapID) } query.Find(&sites) features := []map[string]interface{}{} for _, s := range sites { features = append(features, map[string]interface{}{ "type": "Feature", "geometry": parseGeometry(s.Geometry), "properties": map[string]interface{}{ "gid": s.GID, "id": s.ID, "mapprojectid": s.MapProjectID, "name": s.Name, "address": s.Address1, "city": s.City, "state": s.State, "zip": s.Zip, "group1": s.Group1, }, }) } c.JSON(http.StatusOK, map[string]interface{}{ "type": "FeatureCollection", "features": features, }) }) // POLES router.GET("/api/poles", func(c *gin.Context) { mapID := c.Query("map_id") var poles []models.PolesGeoJSON table := fmt.Sprintf("%s.poles", schema) query := db.Table(table).Select("gid, id, mapprojectid, name, tags, group1, group2, owner, poleheight, attachmentheight, ST_AsGeoJSON(ST_Transform(geom, 4326))::json AS geometry") if mapID != "" { query = query.Where("mapprojectid = ?", mapID) } query.Find(&poles) features := []map[string]interface{}{} for _, p := range poles { features = append(features, map[string]interface{}{ "type": "Feature", "geometry": parseGeometry(p.Geometry), "properties": map[string]interface{}{ "gid": p.GID, "id": p.ID, "mapprojectid": p.MapProjectID, "name": p.Name, "tags": p.Tags, "group1": p.Group1, "group2": p.Group2, "owner": p.Owner, "poleheight": p.PoleHeight, "attachmentheight": p.AttachmentHeight, }, }) } c.JSON(http.StatusOK, map[string]interface{}{ "type": "FeatureCollection", "features": features, }) }) // Access_Points router.GET("/api/access_points", func(c *gin.Context) { mapID := c.Query("map_id") var accessPoints []models.AccessPointGeoJSON table := fmt.Sprintf("%s.access_points", schema) query := db.Table(table).Select(` gid, id, name, mapprojectid, latitude, longitude, manufacturer, size, locked, description, aka, createdby, createddate, modifiedby, modifieddate, historyid, group1, group2, typeid, statusid, crmvendorid, billdate, ST_AsGeoJSON(ST_Transform(geom, 4326))::json AS geometry `) if mapID != "" { query = query.Where("mapprojectid = ?", mapID) } query.Find(&accessPoints) features := []map[string]interface{}{} for _, ap := range accessPoints { features = append(features, map[string]interface{}{ "type": "Feature", "geometry": parseGeometry(ap.Geometry), "properties": map[string]interface{}{ "gid": ap.GID, "id": ap.ID, "name": ap.Name, "mapprojectid": ap.MapProjectID, "latitude": ap.Latitude, "longitude": ap.Longitude, "manufacturer": ap.Manufacturer, "size": ap.Size, "locked": ap.Locked, "description": ap.Description, "aka": ap.AKA, "createdby": ap.CreatedBy, "createddate": ap.CreatedDate, "modifiedby": ap.ModifiedBy, "modifieddate": ap.ModifiedDate, "historyid": ap.HistoryID, "group1": ap.Group1, "group2": ap.Group2, "typeid": ap.TypeID, "statusid": ap.StatusID, "crmvendorid": ap.CRMVendorID, "billdate": ap.BillDate, }, }) } c.JSON(http.StatusOK, map[string]interface{}{ "type": "FeatureCollection", "features": features, }) }) // Info Objects - FIXED router.GET("/api/info", func(c *gin.Context) { mapID := c.Query("map_id") var infos []models.InfoGeoJSON table := fmt.Sprintf("%s.info", schema) query := db.Table(table).Select(` id, name, tags, description, group_1, group_2, ST_AsGeoJSON(geom)::json AS geometry `) if mapID != "" { query = query.Where("mapprojectid = ?", mapID) } if err := query.Find(&infos).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } features := []map[string]interface{}{} for _, info := range infos { features = append(features, map[string]interface{}{ "type": "Feature", "geometry": parseGeometry(info.Geometry), "properties": map[string]interface{}{ "id": info.ID, "name": info.Name, "tags": info.Tags, "description": info.Description, "group_1": info.Group1, "group_2": info.Group2, }, }) } c.JSON(http.StatusOK, map[string]interface{}{ "type": "FeatureCollection", "features": features, }) }) // Server Start log.Printf("Server is running on http://localhost:%s", serverPort) if err := router.Run(":" + serverPort); err != nil { log.Fatal("Server failed:", err) } }