package main import ( "fmt" "log" "math/big" "net/http" "os" "4vif5i.gitea.cloud/numenor-labs/discourse/backend/blockchain" "4vif5i.gitea.cloud/numenor-labs/discourse/backend/feedback" "4vif5i.gitea.cloud/numenor-labs/discourse/backend/identity" "4vif5i.gitea.cloud/numenor-labs/discourse/backend/ipfs" "4vif5i.gitea.cloud/numenor-labs/discourse/backend/moderation" "4vif5i.gitea.cloud/numenor-labs/discourse/backend/perspective" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/gin-gonic/gin" "github.com/go-redis/redis/v8" "gorm.io/driver/postgres" "gorm.io/gorm" ) var ( rpcURL = os.Getenv("RPC_URL") contractAddress = os.Getenv("CONTRACT_ADDRESS") privateKey = os.Getenv("PRIVATE_KEY") redisAddr = getEnv("REDIS_ADDR", "localhost:6379") redisPassword = getEnv("REDIS_PASSWORD", "") redisDB = 0 dbDSN = getEnv("DATABASE_URL", "postgres://postgres:postgres@localhost:5432/voxpop?sslmode=disable") ) // getEnv gets an environment variable or returns a default value func getEnv(key, defaultValue string) string { value := os.Getenv(key) if value == "" { return defaultValue } return value } func main() { // Connect to Sepolia client, err := ethclient.Dial(rpcURL) if err != nil { log.Fatalf("Failed to connect to Ethereum client: %v", err) } // Connect to Redis redisClient := redis.NewClient(&redis.Options{ Addr: redisAddr, Password: redisPassword, DB: redisDB, }) // Connect to PostgreSQL db, err := gorm.Open(postgres.Open(dbDSN), &gorm.Config{}) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } log.Println("Connected to PostgreSQL database") // Set up HTTP server router := gin.Default() // Initialize the verification handler verificationHandler := identity.NewVerificationHandler() // Register the verification routes verificationHandler.RegisterRoutes(router) // Initialize the IPFS service (or use mock for development) var ipfsService perspective.IPFSServiceInterface pinataProd := os.Getenv("PINATA_PRODUCTION") if pinataProd == "true" { ipfsServiceReal, err := ipfs.NewIPFSService() if err != nil { log.Printf("Warning: Failed to initialize Pinata IPFS service: %v", err) log.Println("Falling back to mock IPFS service") ipfsService = ipfs.NewMockIPFSService() } else { ipfsService = ipfsServiceReal log.Println("Initialized Pinata IPFS service") } } else { ipfsService = ipfs.NewMockIPFSService() log.Println("Using mock IPFS service") } // Initialize the perspective service and handler perspectiveService := perspective.NewPerspectiveService(redisClient, db, ipfsService) perspectiveHandler := perspective.NewPerspectiveHandler(perspectiveService) // Register the perspective routes perspectiveHandler.RegisterRoutes(router) // Initialize the moderation service and handler moderationService := moderation.NewModerationService(db) moderationHandler := moderation.NewModerationHandler(moderationService) // Register the moderation routes moderationHandler.RegisterRoutes(router) // Initialize the blockchain service and handler blockchainService, err := blockchain.NewBlockchainService() if err != nil { log.Fatalf("Failed to initialize blockchain service: %v", err) } defer blockchainService.Close() blockchainHandler := blockchain.NewHandler(blockchainService) // Register the blockchain routes blockchainHandler.RegisterRoutes(router) // Existing feedback submission endpoint router.OPTIONS( "/submit-feedback", func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "POST, OPTIONS") c.Header("Access-Control-Allow-Headers", "Content-Type") c.Status(http.StatusOK) }, ) router.POST( "/submit-feedback", func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "POST, OPTIONS") c.Header("Access-Control-Allow-Headers", "Content-Type") var request struct { Proof string `json:"proof"` Feedback string `json:"feedback"` } if err := c.BindJSON(&request); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) return } // Mocked ZKP verification if request.Proof != "valid_proof" { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid proof"}) return } privateKeyECDSA, err := crypto.HexToECDSA(privateKey) if err != nil { fmt.Println("Failed to parse private key:", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse private key"}) return } // Create the transactor (chain ID 11155111 is for Sepolia testnet, adjust as needed) auth, err := bind.NewKeyedTransactorWithChainID(privateKeyECDSA, big.NewInt(11155111)) if err != nil { log.Fatalf("Failed to create transactor: %v", err) } // Instantiate the contract contract, err := feedback.NewFeedback(common.HexToAddress(contractAddress), client) if err != nil { fmt.Println("Failed to instantiate contract:", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to instantiate contract"}) return } // Submit feedback to blockchain tx, err := contract.SubmitFeedback(auth, request.Feedback) if err != nil { fmt.Println("Failed to submit feedback:", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to submit feedback"}) return } fmt.Println("Submitted feedback:", tx.Hash().Hex()) c.JSON(http.StatusOK, gin.H{"transaction": tx.Hash().Hex()}) }, ) log.Println("Server running on :3000") router.Run(":3000") }