init commit from when still exploring using Neo4J for DB
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
// "log"
|
||||
"net/http"
|
||||
"os"
|
||||
// "strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
// Define a custom context key for storing user information
|
||||
var userCtxKey = &contextKey{"user"}
|
||||
|
||||
type contextKey struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// TODO: Provide user with JWT for login/guest
|
||||
|
||||
// AuthMiddleware is our JWT verification middleware
|
||||
func auth_middleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 1. Extract the JWT from the request (e.g., Authorization header)
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := ""
|
||||
parts := strings.Split(authHeader, " ")
|
||||
if len(parts) == 2 && strings.ToLower(parts[0]) == "bearer" {
|
||||
tokenString = parts[1]
|
||||
} else {
|
||||
http.Error(w, "Invalid Authorization header format", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Verify the JWT
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// Validate the signing method
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("invalid signing method: %v", token.Header["alg"])
|
||||
}
|
||||
// Replace "your-secret-key" with your actual secret key
|
||||
return []byte(os.Getenv("API_SECRET_KEY")), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
// 3. Optionally, extract user information from the claims and add it to the request context
|
||||
user_id_raw, ok := claims["user_id"].(float64)
|
||||
if !ok {
|
||||
http.Error(w, "Invalid user ID in token", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
user_id := int(user_id_raw)
|
||||
|
||||
// Create a new context with the user information
|
||||
ctx := context.WithValue(r.Context(), userCtxKey, user_id)
|
||||
// Call the next handler in the chain, passing the new context
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
} else {
|
||||
http.Error(w, "Invalid token claims", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Helper function to extract user information from the context in your handlers
|
||||
func user_id_from_context(ctx context.Context) (int, bool) {
|
||||
userID, ok := ctx.Value(userCtxKey).(int)
|
||||
return userID, ok
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
module the.hub/m
|
||||
|
||||
go 1.24.2
|
||||
|
||||
require (
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.28.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.28.0 h1:chDT68PHNa8JZRmjSkGzAbk1weLWo4rMtDvccvpobg0=
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.28.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
|
||||
@@ -0,0 +1,527 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
// "os"
|
||||
// "os/signal"
|
||||
"strconv"
|
||||
//"strings"
|
||||
// "sync"
|
||||
// "time"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
func create_new_post(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
err := r.ParseMultipartForm(10 << 20) // Limit to 10MB
|
||||
if err != nil {
|
||||
log.Println("Unable to parse form:", err)
|
||||
http.Error(w, "Unable to parse form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
space_id, err := strconv.Atoi(r.FormValue("space_id"))
|
||||
if err != nil {
|
||||
log.Println("Invalid space id:", err)
|
||||
http.Error(w, "Invalid space id.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Required fields
|
||||
user_id, ok := user_id_from_context(r.Context())
|
||||
if !ok {
|
||||
log.Println("Invalid user ID")
|
||||
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !auth_user_in_space(driver, driver_ctx, user_id, space_id) {
|
||||
log.Println("User", user_id, "attempted access to space", space_id, ", which is not allowed.")
|
||||
http.Error(w, "You are not a member of that space.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
post_type := r.FormValue("post_type")
|
||||
post_status := r.FormValue("post_status")
|
||||
content := r.FormValue("content")
|
||||
//create_space := r.FormValue("create_space")
|
||||
|
||||
if post_type == "" || content == "" || post_status == "" {
|
||||
http.Error(w, "Missing required fields", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Optional fields
|
||||
title := r.FormValue("title")
|
||||
excerpt := r.FormValue("excerpt")
|
||||
location := r.FormValue("location")
|
||||
raw_topics := r.FormValue("topics")
|
||||
// files := r.FormFile("files") // Consider using r.FormFile for actual uploads
|
||||
// images := r.Form("images") // Consider using r.Form for multiple URLs
|
||||
// videos := r.Form("videos") // Consider using r.Form for multiple URLs
|
||||
|
||||
// TODO: Implement space retrieval or creation logic based on space_name and create_space
|
||||
|
||||
topics := []string{}
|
||||
if raw_topics != "" {
|
||||
topicList := strings.SplitSeq(raw_topics, ",")
|
||||
for topic := range topicList {
|
||||
trimmedTopic := strings.TrimSpace(topic)
|
||||
if trimmedTopic != "" {
|
||||
topics = append(topics, trimmedTopic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topics_rel_stmt := ""
|
||||
if len(topics) > 0 {
|
||||
topics_rel_stmt = `
|
||||
WITH p
|
||||
FOREACH (topicName IN $topicsList |
|
||||
MERGE (t:Topic {name: topicName})
|
||||
CREATE (p)-[:TAGGED_WITH]->(t)
|
||||
)
|
||||
`
|
||||
}
|
||||
|
||||
stmt := `
|
||||
MATCH (u:User) WHERE id(u) = $user_id
|
||||
MATCH (t:PostType) WHERE t.name = $post_type
|
||||
MATCH (s:Space) WHERE id(s) = $space_id
|
||||
MATCH (st:PostStatus) WHERE st.name = $post_status
|
||||
|
||||
CREATE
|
||||
(u)-[:CREATED {time: datetime()}]->(p:Post {
|
||||
content: $content,
|
||||
title: $title,
|
||||
excerpt: $excerpt,
|
||||
location: $location
|
||||
})
|
||||
|
||||
CREATE (p)-[:POST_TYPE]->(t)
|
||||
CREATE (p)-[:IN_SPACE]->(s)
|
||||
CREATE (p)-[:STATUS]->(st)
|
||||
|
||||
` + topics_rel_stmt
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"user_id": user_id,
|
||||
"post_type": post_type,
|
||||
"space_id": space_id,
|
||||
"post_status": post_status,
|
||||
"content": content,
|
||||
"title": title,
|
||||
"excerpt": excerpt,
|
||||
"location": location,
|
||||
"topicsList": topics,
|
||||
}
|
||||
|
||||
_, err = neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if err != nil {
|
||||
log.Println("Error creating post:", err)
|
||||
http.Error(w, "Failed to create post", http.StatusInternalServerError)
|
||||
return
|
||||
} else {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte("Post created successfully!"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Need to fix bug where only one topic/topic name are being returned in JSON response.
|
||||
|
||||
func retrieve_all_posts(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// TODO: Handle anonymous users.
|
||||
user_id, ok := user_id_from_context(r.Context())
|
||||
if !ok {
|
||||
http.Error(w, "Invalid user ID.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
stmt := `
|
||||
MATCH (p:Post)-[:IN_SPACE]->(s:Space)
|
||||
MATCH (u:User)-[:SPACE_MEMBER]->(s)
|
||||
MATCH (s)-[:SPACE_TYPE]->(st:SpaceType)
|
||||
MATCH (p)-[:POST_TYPE]->(pt:PostType)
|
||||
MATCH (p)-[:STATUS]->(ps:PostStatus)
|
||||
MATCH (p)-[:TAGGED_WITH]->(t:Topic)
|
||||
MATCH (a:User)-[:CREATED]->(p)
|
||||
|
||||
WHERE
|
||||
id(u) = $user_id AND
|
||||
ps.name = "Published"
|
||||
|
||||
RETURN
|
||||
id(a) AS author_id,
|
||||
a.username AS author_username,
|
||||
a.profile_picture AS author_picture,
|
||||
|
||||
p.title AS title,
|
||||
p.excerpt AS excerpt,
|
||||
p.location AS location,
|
||||
p.content AS content,
|
||||
|
||||
pt.name AS post_type,
|
||||
id(t) AS topic_id,
|
||||
t.name AS topic_name,
|
||||
|
||||
id(s) AS space_id,
|
||||
s.name AS space_name,
|
||||
st.name AS space_type
|
||||
`
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"user_id": user_id,
|
||||
}
|
||||
|
||||
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if err != nil {
|
||||
log.Println("Error retrieving all posts:", err)
|
||||
http.Error(w, "Failed to retrieve all posts", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
posts_obj := map[int]any{}
|
||||
|
||||
for index, record := range result.Records {
|
||||
space_id, _ := record.Get("space_id")
|
||||
space_id = int(space_id.(int64))
|
||||
if !auth_user_in_space(driver, driver_ctx, user_id, space_id.(int)) {
|
||||
http.Error(w, "You are not allowed in this space.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
author_id, _ := record.Get("author_id")
|
||||
author_username, _ := record.Get("author_username")
|
||||
author_picture, _ := record.Get("author_picture")
|
||||
|
||||
title, _ := record.Get("title")
|
||||
excerpt, _ := record.Get("excerpt")
|
||||
location, _ := record.Get("location")
|
||||
content, _ := record.Get("content")
|
||||
|
||||
post_type, _ := record.Get("post_type")
|
||||
topic_id, _ := record.Get("topic_id")
|
||||
topic_name, _ := record.Get("topic_name")
|
||||
space_name, _ := record.Get("space_name")
|
||||
|
||||
record_obj := map[string]any{
|
||||
"author_id": author_id,
|
||||
"author_username": author_username,
|
||||
"author_picture": author_picture,
|
||||
"title": title,
|
||||
"excerpt": excerpt,
|
||||
"location": location,
|
||||
"content": content,
|
||||
"post_type": post_type,
|
||||
"topic_id": topic_id,
|
||||
"topic_name": topic_name,
|
||||
"space_id": space_id,
|
||||
"space_name": space_name,
|
||||
}
|
||||
|
||||
posts_obj[index] = record_obj
|
||||
}
|
||||
|
||||
obj, err := json.Marshal(posts_obj)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to parse into JSON", http.StatusInternalServerError)
|
||||
log.Println("error marshalling json")
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(obj)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Need to fix bug where only one topic/topic name are being returned in JSON response.
|
||||
func retrieve_post(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// TODO: Handle anonymous users.
|
||||
user_id, ok := user_id_from_context(r.Context())
|
||||
if !ok {
|
||||
http.Error(w, "Invalid user ID.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
post_id, err := strconv.Atoi(mux.Vars(r)["post_id"])
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid Post ID.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
stmt := `
|
||||
MATCH (p:Post)-[:IN_SPACE]->(s:Space)
|
||||
MATCH (s)-[:SPACE_TYPE]->(st:SpaceType)
|
||||
MATCH (p)-[:POST_TYPE]->(pt:PostType)
|
||||
MATCH (p)-[:STATUS]->(ps:PostStatus)
|
||||
MATCH (p)-[:TAGGED_WITH]->(t:Topic)
|
||||
MATCH (a:User)-[:CREATED]->(p)
|
||||
|
||||
WHERE
|
||||
id(p) = $post_id
|
||||
|
||||
RETURN
|
||||
id(a) AS author_id,
|
||||
a.username AS author_username,
|
||||
a.profile_picture AS author_picture,
|
||||
|
||||
p.title AS title,
|
||||
p.excerpt AS excerpt,
|
||||
p.location AS location,
|
||||
p.content AS content,
|
||||
|
||||
pt.name AS post_type,
|
||||
ps.name AS post_status,
|
||||
id(t) AS topic_id,
|
||||
t.name AS topic_name,
|
||||
|
||||
id(s) AS space_id,
|
||||
st.name AS space_type
|
||||
`
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"post_id": post_id,
|
||||
}
|
||||
|
||||
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to retrieve Post.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if len(result.Records) != 2 {
|
||||
http.Error(w, "Post does not exist.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
record := result.Records[0]
|
||||
space_id, _ := record.Get("space_id")
|
||||
space_id = int(space_id.(int64))
|
||||
if !auth_user_in_space(driver, driver_ctx, user_id, space_id.(int)) {
|
||||
http.Error(w, "You are not allowed in this space.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
author_id, _ := record.Get("author_id")
|
||||
author_username, _ := record.Get("author_username")
|
||||
author_picture, _ := record.Get("author_picture")
|
||||
title, _ := record.Get("title")
|
||||
excerpt, _ := record.Get("excerpt")
|
||||
location, _ := record.Get("location")
|
||||
content, _ := record.Get("content")
|
||||
post_type, _ := record.Get("post_type")
|
||||
post_status, _ := record.Get("post_status")
|
||||
topic_id, _ := record.Get("topic_id")
|
||||
topic_name, _ := record.Get("topic_name")
|
||||
space_type, _ := record.Get("space_type")
|
||||
|
||||
record_obj := map[string]any{
|
||||
"author_id": author_id,
|
||||
"author_username": author_username,
|
||||
"author_picture": author_picture,
|
||||
"title": title,
|
||||
"excerpt": excerpt,
|
||||
"location": location,
|
||||
"content": content,
|
||||
"post_type": post_type,
|
||||
"post_status": post_status,
|
||||
"topic_id": topic_id,
|
||||
"topic_name": topic_name,
|
||||
"space_id": space_id,
|
||||
"space_type": space_type,
|
||||
}
|
||||
|
||||
obj, err := json.Marshal(record_obj)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to parse into JSON", http.StatusInternalServerError)
|
||||
log.Println("error marshalling json")
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(obj)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Need to test.
|
||||
func update_post(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
err := r.ParseMultipartForm(10 << 20) // Limit to 10MB
|
||||
if err != nil {
|
||||
log.Println("Unable to parse form:", err)
|
||||
http.Error(w, "Unable to parse form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
space_id, err := strconv.Atoi(r.FormValue("space_id"))
|
||||
if err != nil {
|
||||
log.Println("Invalid space id:", err)
|
||||
http.Error(w, "Invalid space id.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user_id, ok := user_id_from_context(r.Context())
|
||||
if !ok {
|
||||
http.Error(w, "Invalid user ID.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if !auth_user_in_space(driver, driver_ctx, user_id, space_id) {
|
||||
log.Println("User", user_id, "attempted access to space", space_id, ", which is not allowed.")
|
||||
http.Error(w, "You are not a member of that space.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
post_id, err := strconv.Atoi(mux.Vars(r)["post_id"])
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to get Post ID.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
post_type := r.FormValue("post_type")
|
||||
post_status := r.FormValue("post_status")
|
||||
content := r.FormValue("content")
|
||||
//create_space := r.FormValue("create_space")
|
||||
|
||||
if post_type == "" || content == "" || post_status == "" {
|
||||
http.Error(w, "Missing required fields.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Optional fields
|
||||
title := r.FormValue("title")
|
||||
excerpt := r.FormValue("excerpt")
|
||||
location := r.FormValue("location")
|
||||
raw_topics := r.FormValue("topics")
|
||||
// files := r.FormFile("files") // Consider using r.FormFile for actual uploads
|
||||
// images := r.Form("images") // Consider using r.Form for multiple URLs
|
||||
// videos := r.Form("videos") // Consider using r.Form for multiple URLs
|
||||
|
||||
// TODO: Implement space retrieval or creation logic based on space_name and create_space
|
||||
|
||||
topics := []string{}
|
||||
if raw_topics != "" {
|
||||
topicList := strings.SplitSeq(raw_topics, ",")
|
||||
for topic := range topicList {
|
||||
trimmedTopic := strings.TrimSpace(topic)
|
||||
if trimmedTopic != "" {
|
||||
topics = append(topics, trimmedTopic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topics_rel_stmt := ""
|
||||
if len(topics) > 0 {
|
||||
topics_rel_stmt = `
|
||||
WITH p
|
||||
FOREACH (topicName IN $topicsList |
|
||||
MERGE (t:Topic {name: topicName})
|
||||
MERGE (p)-[:TAGGED_WITH]->(t)
|
||||
)
|
||||
`
|
||||
}
|
||||
|
||||
stmt := `
|
||||
MATCH (u:User) WHERE id(u) = $user_id
|
||||
MATCH (t:PostType) WHERE t.name = $post_type
|
||||
MATCH (s:Space) WHERE id(s) = $space_id
|
||||
MATCH (st:PostStatus) WHERE st.name = $post_status
|
||||
|
||||
SET
|
||||
p.content = $content,
|
||||
p.title = $title,
|
||||
p.excerpt = $excerpt,
|
||||
p.location = $location,
|
||||
p.updated_at = datetime()
|
||||
|
||||
|
||||
MERGE (p)-[:POST_TYPE]->(t)
|
||||
MERGE (p)-[:IN_SPACE]->(s)
|
||||
MERGE (p)-[:STATUS]->(st)
|
||||
|
||||
` + topics_rel_stmt
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"user_id": user_id,
|
||||
"post_type": post_type,
|
||||
"space_id": space_id,
|
||||
"post_status": post_status,
|
||||
"content": content,
|
||||
"title": title,
|
||||
"excerpt": excerpt,
|
||||
"location": location,
|
||||
"topicsList": topics,
|
||||
}
|
||||
|
||||
_, err = neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
if err != nil {
|
||||
log.Println("Attempted to update Post", post_id, "which failed.")
|
||||
http.Error(w, "Unable to update Post.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func delete_post(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
user_id, ok := user_id_from_context(r.Context())
|
||||
if !ok {
|
||||
http.Error(w, "Invalid user ID.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
post_id, err := strconv.Atoi(mux.Vars(r)["post_id"])
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid Post ID.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
stmt := "MATCH (u:User)-[:CREATED]->(p:Post) WHERE id(u) = $user_id AND id(p) = $post_id DETACH DELETE p"
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"user_id": user_id,
|
||||
"post_id": post_id,
|
||||
}
|
||||
|
||||
_, err = neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to delete Post.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
// "encoding/json"
|
||||
"fmt"
|
||||
//"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
//"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
func send_error(w http.ResponseWriter, json []byte) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, string(json))
|
||||
}
|
||||
|
||||
func send_response(w http.ResponseWriter, json []byte) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, string(json))
|
||||
}
|
||||
|
||||
func serve() {
|
||||
// TODO: Authenticate user credentials to verify they are allowed to even access
|
||||
var wg sync.WaitGroup
|
||||
|
||||
driver_ctx := context.Background()
|
||||
driver, err := neo4j.NewDriverWithContext(os.Getenv("NEO4J_BOLT"), neo4j.BasicAuth("neo4j", os.Getenv("NEO4J_PASSWORD"), ""))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer driver.Close(driver_ctx)
|
||||
|
||||
err = driver.VerifyConnectivity(driver_ctx)
|
||||
if err != nil {
|
||||
log.Println("Error connecting to neo4j.")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("neo4j connection established.")
|
||||
|
||||
rsrc_endpoints := []string{
|
||||
"/auth",
|
||||
"/users",
|
||||
"/users",
|
||||
"/users/{user_id}",
|
||||
"/users/{user_id}",
|
||||
"/users/{user_id}",
|
||||
"/users/{user_id}/posts",
|
||||
"/users/{user_id}/spaces",
|
||||
|
||||
"/spaces",
|
||||
"/spaces",
|
||||
"/spaces/{space_id}",
|
||||
//"/spaces/{space_id}",
|
||||
"/spaces/{space_id}",
|
||||
"/spaces/{space_id}",
|
||||
"/spaces/{space_id}/users",
|
||||
"/spaces/{space_id}/posts",
|
||||
|
||||
"/posts",
|
||||
"/posts",
|
||||
"/posts/{post_id}",
|
||||
"/posts/{post_id}",
|
||||
"/posts/{post_id}",
|
||||
}
|
||||
|
||||
methods := []string{
|
||||
http.MethodPost,
|
||||
http.MethodPost,
|
||||
http.MethodGet,
|
||||
http.MethodGet,
|
||||
http.MethodPatch,
|
||||
http.MethodDelete,
|
||||
http.MethodGet,
|
||||
http.MethodGet,
|
||||
|
||||
http.MethodPost,
|
||||
http.MethodGet,
|
||||
http.MethodGet,
|
||||
//http.MethodPost,
|
||||
http.MethodPatch,
|
||||
http.MethodDelete,
|
||||
http.MethodGet,
|
||||
http.MethodGet,
|
||||
|
||||
http.MethodPost,
|
||||
http.MethodGet,
|
||||
http.MethodGet,
|
||||
http.MethodPatch,
|
||||
http.MethodDelete,
|
||||
}
|
||||
|
||||
functions := []func(http.ResponseWriter, *http.Request){
|
||||
auth_user(driver, driver_ctx),
|
||||
create_new_user(driver, driver_ctx),
|
||||
retrieve_all_users(driver, driver_ctx),
|
||||
retrieve_user(driver, driver_ctx),
|
||||
update_user(driver, driver_ctx),
|
||||
delete_user(driver, driver_ctx),
|
||||
retrieve_users_posts(driver, driver_ctx),
|
||||
retrieve_users_spaces(driver, driver_ctx),
|
||||
|
||||
create_new_space(driver, driver_ctx),
|
||||
retrieve_all_spaces(driver, driver_ctx),
|
||||
retrieve_space(driver, driver_ctx),
|
||||
//auth_user_in_space(driver, driver_ctx),
|
||||
update_space(driver, driver_ctx),
|
||||
delete_space(driver, driver_ctx),
|
||||
retrieve_spaces_users(driver, driver_ctx),
|
||||
retrieve_spaces_posts(driver, driver_ctx),
|
||||
|
||||
create_new_post(driver, driver_ctx),
|
||||
retrieve_all_posts(driver, driver_ctx),
|
||||
retrieve_post(driver, driver_ctx),
|
||||
update_post(driver, driver_ctx),
|
||||
delete_post(driver, driver_ctx),
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
|
||||
ServeApi(r, "api", rsrc_endpoints, methods, functions, "7477")
|
||||
log.Println("API routes configured with prefix", "/api")
|
||||
|
||||
addr := "0.0.0.0:7476"
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
IdleTimeout: time.Second * 60,
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
// Run our server in a goroutine so that it doesn't block.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("Router is running on port 7476")
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+Shift+C)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
||||
// Block until we receive our signal.
|
||||
<-c
|
||||
|
||||
// Create a deadline to wait for.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(30))
|
||||
defer cancel()
|
||||
// Doesn't block if no connections, but will otherwise wait
|
||||
// until the timeout deadline.
|
||||
srv.Shutdown(ctx)
|
||||
// Optionally, you could run srv.Shutdown in a goroutine and block on
|
||||
// <-ctx.Done() if your application should wait for other services
|
||||
// to finalize based on context cancellation.
|
||||
log.Println("shutting down")
|
||||
wg.Wait()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load("../.env")
|
||||
if err != nil {
|
||||
log.Println("Error loading .env.")
|
||||
return
|
||||
}
|
||||
|
||||
serve()
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Serve an API using mux.Router().Host({domain}).Subrouter().
|
||||
// Provide the router, domain, an array of endpoints and functions, and
|
||||
// the port you would like the API accessible to.
|
||||
func ServeApi(
|
||||
router *mux.Router,
|
||||
prefix string,
|
||||
endpoints []string,
|
||||
methods []string,
|
||||
functions []func(http.ResponseWriter, *http.Request),
|
||||
port string,
|
||||
) {
|
||||
|
||||
apiR := router.PathPrefix(prefix).Subrouter()
|
||||
|
||||
for i := range endpoints {
|
||||
apiR.HandleFunc(endpoints[i], functions[i]).Methods(methods[i])
|
||||
}
|
||||
apiR.Use(auth_middleware)
|
||||
|
||||
apiSrv := &http.Server{
|
||||
Addr: "0.0.0.0:" + port,
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
IdleTimeout: time.Second * 60,
|
||||
Handler: apiR,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := apiSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("API server is running on port " + port)
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
// "fmt"
|
||||
//"io"
|
||||
"log"
|
||||
"net/http"
|
||||
// "os"
|
||||
// "os/signal"
|
||||
"strconv"
|
||||
//"strings"
|
||||
// "sync"
|
||||
// "time"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
// TODO: Need to test.
|
||||
func create_new_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// Must be a user to create a space.
|
||||
user_id, ok := user_id_from_context(r.Context())
|
||||
if !ok {
|
||||
log.Println("Invalid user ID")
|
||||
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := r.ParseMultipartForm(10 << 20) // Limit to 10MB
|
||||
if err != nil {
|
||||
log.Println("Unable to parse form:", err)
|
||||
http.Error(w, "Unable to parse form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var space map[string]any
|
||||
err = json.NewDecoder(r.Body).Decode(&space)
|
||||
|
||||
if space["id"] != -1 {
|
||||
log.Println("Attempt to create a space with a defined ID")
|
||||
http.Error(w, "Space ID cannot be provided.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
stmt := `
|
||||
MATCH (owner:User)
|
||||
WHERE id(owner) = $user_id
|
||||
|
||||
CREATE (s:Space {
|
||||
created_at: datetime(),
|
||||
name: $name,
|
||||
description: $description
|
||||
})
|
||||
|
||||
CREATE (owner)-[:SPACE_MEMBER {role: "Owner"}]->(s)
|
||||
|
||||
UNWIND $members as member
|
||||
MATCH (m:User)
|
||||
WHERE id(m) = member.id
|
||||
CREATE (m)-[:SPACE_MEMBER {role: member.role}]->(s)
|
||||
|
||||
RETURN id(s) AS id,
|
||||
s.name AS name
|
||||
`
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"user_id": user_id,
|
||||
"name": space["name"],
|
||||
"description": space["description"],
|
||||
"members": space["members"],
|
||||
}
|
||||
|
||||
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if err != nil || len(result.Records) != 2 {
|
||||
log.Println("Error creating Space:", err)
|
||||
http.Error(w, "Failed to create Space", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
record := result.Records[0]
|
||||
space_id, _ := record.Get("id")
|
||||
space_name, _ := record.Get("name")
|
||||
|
||||
record_obj := map[string]any{
|
||||
"space_id": space_id,
|
||||
"space_name": space_name,
|
||||
}
|
||||
|
||||
obj, err := json.Marshal(record_obj)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to marshal JSON.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write(obj)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Need to implement for ADMIN ONLY
|
||||
func retrieve_all_spaces(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func retrieve_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement access for Public and Open Spaces
|
||||
func auth_user_in_space(driver neo4j.DriverWithContext, driver_ctx context.Context, user_id int, space_id int) bool {
|
||||
stmt := `
|
||||
MATCH (u:User), (s:Space)
|
||||
WHERE id(u) = $user_id AND id(s) = $space_id
|
||||
RETURN EXISTS((u)-[:SPACE_MEMBER]->(s))
|
||||
`
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"user_id": user_id,
|
||||
"space_id": space_id,
|
||||
}
|
||||
|
||||
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
if err != nil {
|
||||
log.Println("Something went wrong authorizing the user to a space.")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(result.Records) > 0 {
|
||||
return result.Records[0].Values[0].(bool)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
func update_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
func delete_space(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Need to handle multiple topics.
|
||||
func retrieve_spaces_posts(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// Must be a user to create a space.
|
||||
user_id, ok := user_id_from_context(r.Context())
|
||||
if !ok {
|
||||
log.Println("Invalid user ID")
|
||||
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
space_id, _ := strconv.Atoi(mux.Vars(r)["space_id"])
|
||||
|
||||
if !auth_user_in_space(driver, driver_ctx, user_id, space_id) {
|
||||
log.Println("User", user_id, "attempted access to space", space_id, ", which is not allowed.")
|
||||
http.Error(w, "You are not a member of that space.", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
stmt := `
|
||||
MATCH (p:Post)-[:IN_SPACE]->(s:Space)
|
||||
MATCH (s)-[:SPACE_TYPE]->(st:SpaceType)
|
||||
MATCH (p)-[:POST_TYPE]->(pt:PostType)
|
||||
MATCH (p)-[:STATUS]->(ps:PostStatus)
|
||||
MATCH (p)-[:TAGGED_WITH]->(t:Topic)
|
||||
MATCH (a:User)-[:CREATED]->(p)
|
||||
|
||||
WHERE
|
||||
id(s) = $space_id AND
|
||||
ps.name = "Published"
|
||||
|
||||
RETURN
|
||||
id(a) AS author_id,
|
||||
a.username AS author_username,
|
||||
a.profile_picture AS author_picture,
|
||||
|
||||
p.title AS title,
|
||||
p.excerpt AS excerpt,
|
||||
p.location AS location,
|
||||
p.content AS content,
|
||||
|
||||
pt.name AS post_type,
|
||||
id(t) AS topic_id,
|
||||
t.name AS topic_name,
|
||||
|
||||
s.name AS space_name
|
||||
`
|
||||
|
||||
stmt_args := map[string]any{
|
||||
"space_id": space_id,
|
||||
}
|
||||
|
||||
result, err := neo4j.ExecuteQuery(driver_ctx, driver, stmt, stmt_args, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if err != nil {
|
||||
log.Println("Something went wrong retrieving Posts for a Space. Error:", err)
|
||||
http.Error(w, "Error retrieving Posts from Space.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
//
|
||||
// if !(len(result.Records) >= 2) {
|
||||
// log.Println("Failed to retrieve Posts for Space", space_id, "\n Error:", err)
|
||||
// http.Error(w, "Error retrieving Posts from Space.", http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
posts_obj := map[int]any{}
|
||||
for index, record := range result.Records {
|
||||
|
||||
author_id, _ := record.Get("author_id")
|
||||
author_username, _ := record.Get("author_username")
|
||||
author_picture, _ := record.Get("author_picture")
|
||||
|
||||
title, _ := record.Get("title")
|
||||
excerpt, _ := record.Get("excerpt")
|
||||
location, _ := record.Get("location")
|
||||
content, _ := record.Get("content")
|
||||
|
||||
post_type, _ := record.Get("post_type")
|
||||
topic_id, _ := record.Get("topic_id")
|
||||
topic_name, _ := record.Get("topic_name")
|
||||
space_name, _ := record.Get("space_name")
|
||||
|
||||
record_obj := map[string]any{
|
||||
"author_id": author_id,
|
||||
"author_username": author_username,
|
||||
"author_picture": author_picture,
|
||||
"title": title,
|
||||
"excerpt": excerpt,
|
||||
"location": location,
|
||||
"content": content,
|
||||
"post_type": post_type,
|
||||
"topic_id": topic_id,
|
||||
"topic_name": topic_name,
|
||||
"space_name": space_name,
|
||||
}
|
||||
|
||||
posts_obj[index] = record_obj
|
||||
}
|
||||
|
||||
obj, err := json.Marshal(posts_obj)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to parse into JSON", http.StatusInternalServerError)
|
||||
log.Println("error marshalling json")
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(obj)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement for Space Member role == Administrator OR role == Owner
|
||||
func retrieve_spaces_users(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
//"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
// "os"
|
||||
// "os/signal"
|
||||
// "reflect"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
// "sync"
|
||||
// "time"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
func id_exists(id int, driver neo4j.DriverWithContext, driver_ctx context.Context) bool {
|
||||
result, _ := neo4j.ExecuteQuery(driver_ctx, driver,
|
||||
"MATCH (u:User) WHERE id(u) = $id RETURN count(u)",
|
||||
map[string]any{
|
||||
"id": id,
|
||||
}, neo4j.EagerResultTransformer,
|
||||
neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if len(result.Records) > 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func create_new_user(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
err := r.ParseMultipartForm(10 << 20) // Limit to 10MB
|
||||
if err != nil {
|
||||
log.Println("Unable to parse form.")
|
||||
}
|
||||
|
||||
username := r.FormValue("username")
|
||||
email := r.FormValue("email")
|
||||
password_hash := r.FormValue("password_hash")
|
||||
|
||||
id := rand.Intn(999999999999) + 1
|
||||
for id_exists(id, driver, driver_ctx) {
|
||||
id = rand.Intn(999999999999) + 1
|
||||
}
|
||||
|
||||
profile_picture_file, header, err := r.FormFile("profile_picture")
|
||||
if err != nil {
|
||||
log.Println("Error retrieving file from form.")
|
||||
}
|
||||
|
||||
defer profile_picture_file.Close()
|
||||
filename_parts := strings.Split(header.Filename, ".")
|
||||
ext := filename_parts[len(filename_parts)-1]
|
||||
|
||||
dst, err := os.Create(os.Getenv("PROFILE_IMAGES_FQ_PATH") + strconv.Itoa(id) + ext)
|
||||
if err != nil {
|
||||
log.Println("Error saving profile picture.")
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
_, err = io.Copy(dst, profile_picture_file)
|
||||
if err != nil {
|
||||
log.Println("Error writing file to filesystem.")
|
||||
}
|
||||
|
||||
result, _ := neo4j.ExecuteQuery(driver_ctx, driver,
|
||||
"CREATE (u:User {id: $id, username: $username, email: $email, password_hash: $password_hash, profile_picture: $profile_picture, created_at: datetime()})-[:USER_TYPE]->(:Role {name: 'User'}) RETURN elementId(u) AS id",
|
||||
map[string]any{
|
||||
"id": id,
|
||||
"username": username,
|
||||
"email": email,
|
||||
"password_hash": password_hash,
|
||||
"profile_picture": strconv.Itoa(id) + ext,
|
||||
}, neo4j.EagerResultTransformer,
|
||||
neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if len(result.Records) > 0 {
|
||||
ret := struct {
|
||||
Auth bool `json:"auth"`
|
||||
Id string `json:"id"`
|
||||
}{
|
||||
Auth: true,
|
||||
Id: strconv.Itoa(id),
|
||||
}
|
||||
|
||||
json, err := json.Marshal(ret)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, string(json))
|
||||
log.Println("User ID:", id, "Action: Signup")
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func retrieve_all_users(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func retrieve_user(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id, _ := strconv.Atoi(mux.Vars(r)["user_id"])
|
||||
|
||||
result, _ := neo4j.ExecuteQuery(driver_ctx, driver,
|
||||
"MATCH (u:User) WHERE id(u) = $user_id RETURN u.username AS username, u.email AS email, u.profile_picture AS profile_picture",
|
||||
map[string]any{
|
||||
"user_id": id,
|
||||
}, neo4j.EagerResultTransformer,
|
||||
neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if len(result.Records) > 0 {
|
||||
record := result.Records[0]
|
||||
vals := record.AsMap()
|
||||
user := struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
ProfilePicture string `json:"profile_picture"`
|
||||
}{
|
||||
Username: vals["username"].(string),
|
||||
Email: vals["email"].(string),
|
||||
ProfilePicture: vals["profile_picture"].(string),
|
||||
}
|
||||
|
||||
json, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, string(json))
|
||||
log.Println("User ID:", id, "Action: Retrieval")
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func auth_user(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
username := r.FormValue("username")
|
||||
password_hash := r.FormValue("password_hash")
|
||||
|
||||
result, _ := neo4j.ExecuteQuery(driver_ctx, driver,
|
||||
"MATCH (u:User) WHERE u.username = $username RETURN u.password_hash AS password_hash, elementId(u) AS id",
|
||||
map[string]any{
|
||||
"username": username,
|
||||
}, neo4j.EagerResultTransformer,
|
||||
neo4j.ExecuteQueryWithDatabase("neo4j"))
|
||||
|
||||
if len(result.Records) > 0 {
|
||||
record := result.Records[0]
|
||||
vals := record.AsMap()
|
||||
if vals["password_hash"] != password_hash {
|
||||
return
|
||||
}
|
||||
|
||||
id := strings.Split(vals["id"].(string), ":")[2]
|
||||
|
||||
ret := struct {
|
||||
Auth bool `json:"auth"`
|
||||
Id string `json:"id"`
|
||||
}{
|
||||
Auth: true,
|
||||
Id: id,
|
||||
}
|
||||
|
||||
json, err := json.Marshal(ret)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, string(json))
|
||||
log.Println("User ID:", id, "Action: Login")
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func update_user(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func delete_user(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func retrieve_users_posts(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func retrieve_users_spaces(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user