restructure repo

This commit is contained in:
2025-03-17 21:48:21 +01:00
parent 833c396b9b
commit bbf66ddc74
160 changed files with 49 additions and 1163 deletions

View File

@@ -1,23 +0,0 @@
package main
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth"
"github.com/zitadel/zitadel-go/v3/pkg/http/middleware"
)
func auth(mw *middleware.Interceptor[*oauth.IntrospectionContext]) gin.HandlerFunc {
return func(c *gin.Context) {
mw.RequireAuthorization()(http.HandlerFunc(authHTTPHandler(mw, c))).ServeHTTP(c.Writer, c.Request)
}
}
func authHTTPHandler(mw *middleware.Interceptor[*oauth.IntrospectionContext], c *gin.Context) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
authCtx := mw.Context(r.Context())
w.Header().Set("id", authCtx.UserID())
c.JSON(http.StatusOK, gin.H{"id": authCtx.UserID(), "status": "ok"})
}
}

View File

@@ -1,18 +0,0 @@
FROM --platform=$BUILDPLATFORM golang:alpine AS build
WORKDIR /app
COPY . .
RUN go get ./...
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o auth-service
RUN apk update && apk add ca-certificates && update-ca-certificates
FROM --platform=$TARGETPLATFORM busybox:1.36.1
COPY --from=build /etc/ssl/certs /etc/ssl/certs
COPY --from=build /app/auth-service /app/
CMD [ "/app/auth-service" ]

View File

@@ -1,84 +0,0 @@
package main
import (
"context"
"flag"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
utilities "github.com/vcscsvcscs/GenerationsHeritage/pkg"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/gin/healthcheck"
"github.com/zitadel/zitadel-go/v3/pkg/authorization"
"github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth"
"github.com/zitadel/zitadel-go/v3/pkg/http/middleware"
"github.com/zitadel/zitadel-go/v3/pkg/zitadel"
)
var (
cert = flag.String("cert", "/etc/gh-auth-service/ssl/tls.crt", "Specify the path of TLS cert")
key = flag.String("key", "/etc/gh-auth-service/ssl/tls.key", "Specify the path of TLS key")
zitadelAccessKey = flag.String("zitadel-access-key", "/etc/gh-auth-service/zitadel/api-key.json", "Specify the path of Zitadel access key")
httpsPort = flag.String("https", ":443", "Specify port for http secure hosting(example for format :443)")
httpPort = flag.String("http", ":80", "Specify port for http hosting(example for format :80)")
zitadelURI = flag.String("zitadel-uri", "zitadel.varghacsongor.hu", "Specify the Zitadel URI")
release = flag.Bool("release", false, "Set true to release build")
logToFile = flag.Bool("log-to-file", false, "Set true to log to file")
logToFileAndStd = flag.Bool("log-to-file-and-std", false, "Set true to log to file and std")
requestTimeout = time.Duration(*flag.Int("request-timeout", 20, "Set request timeout in seconds"))
)
func main() {
flag.Parse()
if *release {
gin.SetMode(gin.ReleaseMode)
}
utilities.SetupLogger(*logToFileAndStd, *logToFile)
hc := healthcheck.New()
router := gin.Default()
router.Use(gin.Recovery())
ctx := context.Background()
// Initiate the authorization by providing a zitadel configuration and a verifier.
// This example will use OAuth2 Introspection for this, therefore you will also need to provide the downloaded api key.json
authZ, err := authorization.New(ctx, zitadel.New(*zitadelURI), oauth.DefaultAuthorization(*zitadelAccessKey))
if err != nil {
log.Println("zitadel sdk could not initialize", "error", err)
os.Exit(1)
}
// Initialize the HTTP middleware by providing the authorization
mw := middleware.New(authZ)
router.GET("/health", hc.HealthCheckHandler())
router.GET("/auth", auth(mw))
server := utilities.SetupHttpsServer(router, *cert, *key, *httpsPort, *httpPort, requestTimeout)
// Wait for interrupt signal to gracefully shutdown the server with some time to finish requests.
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-quit
log.Println("Shutting down server...")
// The context is used to inform the server it has some seconds to finish
// the request it is currently handling
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exiting")
}

View File

@@ -1,18 +0,0 @@
FROM --platform=$BUILDPLATFORM golang:alpine AS build
WORKDIR /app
COPY . .
RUN go get ./...
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o backend
RUN apk update && apk add ca-certificates && update-ca-certificates
FROM --platform=$TARGETPLATFORM busybox:1.36.1
COPY --from=build /etc/ssl/certs /etc/ssl/certs
COPY --from=build /app/backend /app/
CMD [ "/app/backend" ]

View File

@@ -1,49 +0,0 @@
package handlers
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
)
func CreatePerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Body == nil || c.ContentType() != "application/json" {
log.Printf("ip: %s error: request body is empty or content type is not application/json", c.ClientIP())
c.JSON(http.StatusBadRequest, gin.H{"error": "content type must be application/json and request body must not be empty"})
return
}
var person memgraph.Person
err := json.NewDecoder(c.Request.Body).Decode(&person)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
if err := person.Verify(); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "contains-forbidden-characters"})
return
}
person.ID = c.GetString("id")
rec, err := person.CreatePerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "already-exists"})
return
}
c.JSON(http.StatusCreated, gin.H{"person": rec.AsMap()})
}
}

View File

@@ -1,32 +0,0 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
)
func CreateRelationship(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var relationship memgraph.Relationship
if err := c.ShouldBindJSON(&relationship); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
rec, err := relationship.CreateRelationship(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusCreated, gin.H{"relationship": rec.AsMap()})
}
}

View File

@@ -1,32 +0,0 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
)
func CreateRelationshipAndPerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var rp memgraph.RelationshipAndPerson
if err := c.ShouldBindJSON(&rp); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
rec, err := rp.CreateRelationshipAndPerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusCreated, gin.H{"relationship": rec.AsMap()})
}
}

View File

@@ -1,48 +0,0 @@
package handlers
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
)
func DeletePerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Body == nil || c.ContentType() != "application/json" {
log.Printf("ip: %s error: request body is empty or content type is not application/json", c.ClientIP())
c.JSON(http.StatusBadRequest, gin.H{"error": "content type must be application/json and request body must not be empty"})
return
}
var person memgraph.Person
err := json.NewDecoder(c.Request.Body).Decode(&person)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
if person.ID != "" {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "no person ID provided"})
return
}
err = person.DeletePerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not delete person with ID provided"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "person deleted successfully"})
}
}

View File

@@ -1,32 +0,0 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
)
func DeleteRelationship(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var relationship memgraph.Relationship
if err := c.ShouldBindJSON(&relationship); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
err := relationship.DeleteRelationship(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusAccepted, gin.H{"status": "relationship deleted successfully"})
}
}

View File

@@ -1,48 +0,0 @@
package handlers
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
)
func UpdatePerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Body == nil || c.ContentType() != "application/json" {
log.Printf("ip: %s error: request body is empty or content type is not application/json", c.ClientIP())
c.JSON(http.StatusBadRequest, gin.H{"error": "content type must be application/json and request body must not be empty"})
return
}
var person memgraph.Person
err := json.NewDecoder(c.Request.Body).Decode(&person)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
return
}
if person.ID == "" {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": "No ID provided"})
return
}
rec, err := person.UpdatePerson(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not update person with information provided"})
return
}
c.JSON(http.StatusOK, gin.H{"person": rec.AsMap()})
}
}

View File

@@ -1,32 +0,0 @@
package handlers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
)
func VerifyRelationship(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
var relationship memgraph.Relationship
if err := c.ShouldBindJSON(&relationship); err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
rec, err := relationship.VerifyRelationship(driver)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
c.JSON(http.StatusOK, gin.H{"relationship": rec.AsMap()})
}
}

View File

@@ -1,55 +0,0 @@
package handlers
import (
"context"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
func ViewFamiliyTree(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead})
defer session.Close(ctx)
id := c.GetString("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "id is required"})
return
}
query := `
MATCH (n:Person {id: $person_id})-[p:Parent*1..]->(family:Person)
OPTIONAL MATCH (family)-[c:Child]->(children:Person)
WITH family, p, children, c, n
OPTIONAL MATCH (children)<-[p2:Parent]-(OtherParents:Person)
WITH family, p, children, c, OtherParents, p2,n
OPTIONAL MATCH (family)-[s:Spouse]-(spouse:Person)
RETURN family, p, children, c, OtherParents, p2, spouse, s, n;`
result, err := session.Run(ctx, query, map[string]any{"person_id": id})
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
rec, err := result.Collect(ctx)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not find family tree for person with id: " + id})
return
}
c.JSON(200, rec)
}
}

View File

@@ -1,50 +0,0 @@
package handlers
import (
"context"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
func ViewPerson(driver neo4j.DriverWithContext) gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead})
defer session.Close(ctx)
id := c.Query("id")
if id == "" {
id = c.GetString("id")
}
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "id is required"})
return
}
result, err := session.Run(ctx, "MATCH (n:Person) WHERE n.id = $person_id RETURN n;", map[string]any{"person_id": id})
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
rec, err := result.Single(ctx)
if err != nil {
log.Printf("ip: %s error: %s", c.ClientIP(), err)
c.JSON(http.StatusNotFound, gin.H{"error": "could not find person with information provided"})
return
}
c.JSON(200, rec.AsMap()["n"])
}
}

View File

@@ -1,106 +0,0 @@
package main
import (
"context"
"flag"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/vcscsvcscs/GenerationsHeritage/cmd/backend/handlers"
utilities "github.com/vcscsvcscs/GenerationsHeritage/pkg"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/gin/healthcheck"
"github.com/vcscsvcscs/GenerationsHeritage/pkg/memgraph"
//"github.com/zitadel/zitadel-go/v3/pkg/authorization"
//"github.com/zitadel/zitadel-go/v3/pkg/authorization/oauth"
//"github.com/zitadel/zitadel-go/v3/pkg/zitadel"
)
var (
cert = flag.String("cert", "/etc/gh-backend/ssl/tls.crt", "Specify the path of TLS cert")
key = flag.String("key", "/etc/gh-backend/ssl/tls.key", "Specify the path of TLS key")
httpsPort = flag.String("https", ":443", "Specify port for http secure hosting(example for format :443)")
httpPort = flag.String("http", ":80", "Specify port for http hosting(example for format :80)")
// zitadelAccessKey = flag.String("zitadel-access-key", "/etc/gh-backend/zitadel/api-key.json", "Specify the path of Zitadel access key")
// zitadelURI = flag.String("zitadel-uri", "zitadel.varghacsongor.hu", "Specify the Zitadel URI")
memgraphURI = flag.String("memgraph", "bolt+ssc://memgraph:7687", "Specify the Memgraph database URI")
memgraphUser = flag.String("memgraph-user", "", "Specify the Memgraph database user")
memgraphPass = flag.String("memgraph-pass", "", "Specify the Memgraph database password")
release = flag.Bool("release", false, "Set true to release build")
logToFile = flag.Bool("log-to-file", false, "Set true to log to file")
logToFileAndStd = flag.Bool("log-to-file-and-std", false, "Set true to log to file and std")
requestTimeout = time.Duration(*flag.Int("request-timeout", 20, "Set request timeout in seconds"))
)
func main() {
flag.Parse()
if *release {
gin.SetMode(gin.ReleaseMode)
}
utilities.SetupLogger(*logToFileAndStd, *logToFile)
hc := healthcheck.New()
memgraphDriver := memgraph.InitDatabase(*memgraphURI, *memgraphUser, *memgraphPass)
router := gin.Default()
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:5173", "http://localhost", "https://heritagebackend.varghacsongor.hu", "https://feature-add-frontend.generationsheritage.pages.dev/", "https://csalad.varghacsongor.hu/"},
AllowCredentials: true,
AllowHeaders: []string{"Authorization", "id", "Content-Type"},
MaxAge: 12 * time.Hour,
}))
router.Use(gin.Recovery())
//ctx := context.Background()
// Initiate the authorization by providing a zitadel configuration and a verifier.
// This example will use OAuth2 Introspection for this, therefore you will also need to provide the downloaded api key.json
//authZ, err := authorization.New(ctx, zitadel.New(*zitadelURI), oauth.DefaultAuthorization(*zitadelAccessKey))
//if err != nil {
// log.Println("zitadel sdk could not initialize", "error", err)
// os.Exit(1)
//}
// Initialize the HTTP middleware by providing the authorization
//mw := middleware.New(authZ)
//router.Use(auth(mw))
router.GET("/health", hc.HealthCheckHandler())
router.GET("/person", handlers.ViewPerson(memgraphDriver))
router.POST("/person", handlers.CreatePerson(memgraphDriver))
router.DELETE("/person", handlers.DeletePerson(memgraphDriver))
router.PUT("/person", handlers.UpdatePerson(memgraphDriver))
router.POST("/relationship", handlers.CreateRelationship(memgraphDriver))
router.DELETE("/relationship", handlers.DeleteRelationship(memgraphDriver))
router.PUT("/relationship", handlers.VerifyRelationship(memgraphDriver))
router.POST("/createRelationshipAndPerson", handlers.CreateRelationshipAndPerson(memgraphDriver))
router.GET("/familyTree", handlers.ViewFamiliyTree(memgraphDriver))
server := utilities.SetupHttpsServer(router, *cert, *key, *httpsPort, *httpPort, requestTimeout)
// Wait for interrupt signal to gracefully shutdown the server with some time to finish requests.
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-quit
log.Println("Shutting down server...")
// The context is used to inform the server it has some seconds to finish
// the request it is currently handling
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
log.Println("Server exiting")
}