diff --git a/backend/go.mod b/backend/go.mod index 633f1f8..0d659af 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,5 +4,6 @@ go 1.24.2 require ( 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 ) diff --git a/backend/go.sum b/backend/go.sum index 2cbaca1..bd69801 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,4 +1,6 @@ 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= diff --git a/backend/router.go b/backend/router.go index 0f65a56..66bfe7d 100644 --- a/backend/router.go +++ b/backend/router.go @@ -14,6 +14,7 @@ import ( "time" "github.com/gorilla/mux" + "github.com/joho/godotenv" "github.com/neo4j/neo4j-go-driver/v5/neo4j" ) @@ -34,7 +35,7 @@ func serve() { var wg sync.WaitGroup driver_ctx := context.Background() - driver, err := neo4j.NewDriverWithContext("bolt://localhost:7687", neo4j.BasicAuth("neo4j", "jzLbsy2C23WHzQ-", "")) + driver, err := neo4j.NewDriverWithContext(os.Getenv("NEO4J_BOLT"), neo4j.BasicAuth("neo4j", os.Getenv("NEO4J_PASSWORD"), "")) if err != nil { panic(err) } @@ -43,18 +44,19 @@ func serve() { 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}", "/users/{user_id}/posts", "/users/{user_id}/spaces", "/spaces", @@ -74,9 +76,9 @@ func serve() { methods := []string{ http.MethodPost, - http.MethodGet, - http.MethodGet, http.MethodPost, + http.MethodGet, + http.MethodGet, http.MethodPatch, http.MethodDelete, http.MethodGet, @@ -97,10 +99,10 @@ func serve() { } 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), - auth_user(driver, driver_ctx), update_user(driver, driver_ctx), delete_user(driver, driver_ctx), retrieve_users_posts(driver, driver_ctx), @@ -168,5 +170,11 @@ func serve() { } func main() { + err := godotenv.Load("../.env") + if err != nil { + log.Println("Error loading .env.") + return + } + serve() } diff --git a/backend/users.go b/backend/users.go index f97a551..a21b452 100644 --- a/backend/users.go +++ b/backend/users.go @@ -3,24 +3,112 @@ package main import ( "context" "encoding/json" - // "fmt" + "fmt" + "io" + "os" "strconv" + //"io" - // "log" + "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) { - return + w.Header().Set("Access-Control-Allow-Origin", "http://localhost:2025") + + 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 + } } } @@ -32,38 +120,47 @@ func retrieve_all_users(driver neo4j.DriverWithContext, driver_ctx context.Conte func retrieve_user(driver neo4j.DriverWithContext, driver_ctx context.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - user_id, _ := strconv.Atoi(mux.Vars(r)["user_id"]) + 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": user_id, + "user_id": id, }, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j")) - 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), - } + 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 { + 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 } - - send_response(w, json) } } 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", "http://localhost:2025") + username := r.FormValue("username") password_hash := r.FormValue("password_hash") @@ -96,7 +193,10 @@ func auth_user(driver neo4j.DriverWithContext, driver_ctx context.Context) http. return } - send_response(w, json) + 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 } diff --git a/components/nav.php b/components/nav.php index 0e08abe..1b59cfb 100644 --- a/components/nav.php +++ b/components/nav.php @@ -54,6 +54,8 @@ if (isset($_SESSION['username'])) { $nav .= ' + + '; diff --git a/components/user/login.php b/components/user/login.php index 613e589..5681f61 100644 --- a/components/user/login.php +++ b/components/user/login.php @@ -4,11 +4,12 @@ function login_form($error_msg = '') $form = '