Initial commit

This commit is contained in:
2025-03-07 11:12:53 +07:00
commit 5a7e718666
22 changed files with 4527 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
package database
import (
"context"
"fmt"
"log"
"os"
"time"
_ "github.com/joho/godotenv/autoload"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Service interface {
Health() map[string]string
}
type service struct {
db *mongo.Client
}
var (
host = os.Getenv("BLUEPRINT_DB_HOST")
port = os.Getenv("BLUEPRINT_DB_PORT")
//database = os.Getenv("BLUEPRINT_DB_DATABASE")
)
func New() Service {
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%s", host, port)))
if err != nil {
log.Fatal(err)
}
return &service{
db: client,
}
}
func (s *service) Health() map[string]string {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
err := s.db.Ping(ctx, nil)
if err != nil {
log.Fatalf(fmt.Sprintf("db down: %v", err))
}
return map[string]string{
"message": "It's healthy",
}
}

View File

@@ -0,0 +1,61 @@
package database
import (
"context"
"log"
"testing"
"github.com/testcontainers/testcontainers-go/modules/mongodb"
)
func mustStartMongoContainer() (func(context.Context) error, error) {
dbContainer, err := mongodb.Run(context.Background(), "mongo:latest")
if err != nil {
return nil, err
}
dbHost, err := dbContainer.Host(context.Background())
if err != nil {
return dbContainer.Terminate, err
}
dbPort, err := dbContainer.MappedPort(context.Background(), "27017/tcp")
if err != nil {
return dbContainer.Terminate, err
}
host = dbHost
port = dbPort.Port()
return dbContainer.Terminate, err
}
func TestMain(m *testing.M) {
teardown, err := mustStartMongoContainer()
if err != nil {
log.Fatalf("could not start mongodb container: %v", err)
}
m.Run()
if teardown != nil && teardown(context.Background()) != nil {
log.Fatalf("could not teardown mongodb container: %v", err)
}
}
func TestNew(t *testing.T) {
srv := New()
if srv == nil {
t.Fatal("New() returned nil")
}
}
func TestHealth(t *testing.T) {
srv := New()
stats := srv.Health()
if stats["message"] != "It's healthy" {
t.Fatalf("expected message to be 'It's healthy', got %s", stats["message"])
}
}

78
internal/server/routes.go Normal file
View File

@@ -0,0 +1,78 @@
package server
import (
"net/http"
"fmt"
"log"
"time"
"github.com/gin-gonic/gin"
"blueprint_fix/cmd/web"
"github.com/a-h/templ"
"io/fs"
"github.com/coder/websocket"
)
func (s *Server) RegisterRoutes() http.Handler {
r := gin.Default()
r.GET("/", s.HelloWorldHandler)
r.GET("/health", s.healthHandler)
r.GET("/websocket", s.websocketHandler)
staticFiles, _ := fs.Sub(web.Files, "assets")
r.StaticFS("/assets", http.FS(staticFiles))
r.GET("/web", func(c *gin.Context) {
templ.Handler(web.HelloForm()).ServeHTTP(c.Writer, c.Request)
})
r.POST("/hello", func(c *gin.Context) {
web.HelloWebHandler(c.Writer, c.Request)
})
return r
}
func (s *Server) HelloWorldHandler(c *gin.Context) {
resp := make(map[string]string)
resp["message"] = "Hello World"
c.JSON(http.StatusOK, resp)
}
func (s *Server) healthHandler(c *gin.Context) {
c.JSON(http.StatusOK, s.db.Health())
}
func (s *Server) websocketHandler(c *gin.Context) {
w := c.Writer
r := c.Request
socket, err := websocket.Accept(w, r, nil)
if err != nil {
log.Printf("could not open websocket: %v", err)
_, _ = w.Write([]byte("could not open websocket"))
w.WriteHeader(http.StatusInternalServerError)
return
}
defer socket.Close(websocket.StatusGoingAway, "server closing websocket")
ctx := r.Context()
socketCtx := socket.CloseRead(ctx)
for {
payload := fmt.Sprintf("server timestamp: %d", time.Now().UnixNano())
err := socket.Write(socketCtx, websocket.MessageText, []byte(payload))
if err != nil {
break
}
time.Sleep(time.Second * 2)
}
}

View File

@@ -0,0 +1,32 @@
package server
import (
"github.com/gin-gonic/gin"
"net/http"
"net/http/httptest"
"testing"
)
func TestHelloWorldHandler(t *testing.T) {
s := &Server{}
r := gin.New()
r.GET("/", s.HelloWorldHandler)
// Create a test HTTP request
req, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}
// Create a ResponseRecorder to record the response
rr := httptest.NewRecorder()
// Serve the HTTP request
r.ServeHTTP(rr, req)
// Check the status code
if status := rr.Code; status != http.StatusOK {
t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusOK)
}
// Check the response body
expected := "{\"message\":\"Hello World\"}"
if rr.Body.String() != expected {
t.Errorf("Handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
}
}

39
internal/server/server.go Normal file
View File

@@ -0,0 +1,39 @@
package server
import (
"fmt"
"net/http"
"os"
"strconv"
"time"
_ "github.com/joho/godotenv/autoload"
"blueprint_fix/internal/database"
)
type Server struct {
port int
db database.Service
}
func NewServer() *http.Server {
port, _ := strconv.Atoi(os.Getenv("PORT"))
NewServer := &Server{
port: port,
db: database.New(),
}
// Declare Server config
server := &http.Server{
Addr: fmt.Sprintf(":%d", NewServer.port),
Handler: NewServer.RegisterRoutes(),
IdleTimeout: time.Minute,
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
}
return server
}