From 2398d3ebc035434cfaa28ea5e88417518798b773 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 4 Nov 2024 23:12:51 +0300 Subject: [PATCH] CORS has been defeated, as my sanity... --- enshi/src/App.tsx | 35 +- enshi/src/Pages/MainPage/MainPage.tsx | 11 + enshi/src/api/axios/axios.ts | 13 + enshi_back/main.go | 2 + enshi_back/utils/routesSetup.go | 1026 +------------------------ package-lock.json | 102 ++- package.json | 3 +- 7 files changed, 178 insertions(+), 1014 deletions(-) create mode 100644 enshi/src/api/axios/axios.ts diff --git a/enshi/src/App.tsx b/enshi/src/App.tsx index 8d115e6..952c44e 100644 --- a/enshi/src/App.tsx +++ b/enshi/src/App.tsx @@ -1,23 +1,36 @@ import "./App.css"; import "@radix-ui/themes/styles.css"; -import { - Theme, - ThemePanel, -} from "@radix-ui/themes"; +import { Theme, ThemePanel } from "@radix-ui/themes"; -import { - createBrowserRouter, - RouterProvider, -} from "react-router-dom"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { QueryClientProvider } from "@tanstack/react-query"; import queryClient from "./api/QueryClient/QueryClient"; import { routes } from "./routes/routes"; +import { useEffect } from "react"; +import "axios"; +import { axiosLocalhost } from "./api/axios/axios"; -const router = createBrowserRouter( - routes -); +const router = createBrowserRouter(routes); export default function App() { + useEffect(() => { + let f = async () => { + let c = await axiosLocalhost.post( + "/login", + { + nickname: "StasikChess", + password: "123456", + } + ); + + console.log(c.headers); + console.log(document.cookie); + }; + + f(); + + }, []); + return ( diff --git a/enshi/src/Pages/MainPage/MainPage.tsx b/enshi/src/Pages/MainPage/MainPage.tsx index 1a65171..12102a2 100644 --- a/enshi/src/Pages/MainPage/MainPage.tsx +++ b/enshi/src/Pages/MainPage/MainPage.tsx @@ -1,12 +1,23 @@ import React from 'react' import { Outlet } from 'react-router-dom' import NavBar from '../../Components/NavBar/NavBar' +import { axiosLocalhost } from '../../api/axios/axios' export default function MainPage() { return ( <> + ) } diff --git a/enshi/src/api/axios/axios.ts b/enshi/src/api/axios/axios.ts new file mode 100644 index 0000000..1a352da --- /dev/null +++ b/enshi/src/api/axios/axios.ts @@ -0,0 +1,13 @@ +import axios from "axios"; + +export const axiosLocalhost = axios.create( + { + baseURL: `http://localhost:9876/`, + withCredentials: true, + headers: { + + } + } +) + +axios.defaults.withCredentials = true; \ No newline at end of file diff --git a/enshi_back/main.go b/enshi_back/main.go index feab8de..cda1f59 100644 --- a/enshi_back/main.go +++ b/enshi_back/main.go @@ -45,5 +45,7 @@ func main() { return } + router.Run("localhost:9876") + fmt.Printf("Hey!, %v", "you") } diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/utils/routesSetup.go index b1c9305..9917dac 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/utils/routesSetup.go @@ -2,974 +2,18 @@ package utils import ( "context" - "fmt" - "math/rand" + db_repo "enshi/db/go_queries" "net/http" "strconv" "strings" "time" "github.com/gin-gonic/gin" - "github.com/google/uuid" ) -func testRoute1(c *gin.Context) { - testInfo := map[string]interface{}{ - "name": "Kyle", - "id": 1, - } - - newToken, err := CreateToken(testInfo) - - if err != nil { - c.IndentedJSON(401, gin.H{"error": err.Error()}) - return - } - - c.IndentedJSON(200, gin.H{"token": newToken}) -} - -func testRoute2(c *gin.Context) { - type content struct { - Email string - Name string - } - - // Check correct type of receiving data - if c.ContentType() != "application/json" { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "content type should by 'application/json'"}) - return - } - - var body content - - if err := c.BindJSON(&body); err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "invalid body of request" + err.Error()}) - return - } - - // Get data from token - temp, err := c.Get("claims") - - if !err { - c.IndentedJSON(http.StatusBadRequest, gin.H{"Error": "your token does not contain information needed"}) - return - } - - c.IndentedJSON(http.StatusOK, gin.H{"1": temp, "2": body}) -} - -func getRecipeInformation(c *gin.Context) { - - recipe_id, err := strconv.Atoi(strings.Trim(c.Param("recipe_id"), "/")) - if err != nil { - c.IndentedJSON(404, gin.H{"err": err.Error()}) - return - } - - returnData := map[string]interface{}{} - - recipe, err := Dbx.Query(context.Background(), "select * from recipes where recipe_id = $1", recipe_id) - if err != nil { - c.IndentedJSON(500, gin.H{"err": err.Error()}) - return - } - - isMore := recipe.Next() - - recipeFields := recipe.FieldDescriptions() - recipeValues, err := recipe.Values() - if err != nil { - c.IndentedJSON(500, gin.H{"err": err.Error()}) - return - } - - for i, recipeField := range recipeFields { - returnData[recipeField.Name] = recipeValues[i] - } - - if isMore { - fmt.Println("there is more than one items with this id: ", recipe_id) - } - - recipe.Close() - - // - // Adding ingredients - // - - ingredients, err := Dbx.Query(context.Background(), - "select a.ingredient_id, a.quantity, a.unit, b.name from recipe_ingredients a join "+ - "ingredients b on a.recipe_id = $1 and "+ - "a.ingredient_id = b.ingredient_id", recipe_id) - if err != nil { - c.IndentedJSON(500, gin.H{"err": err.Error()}) - return - } - - ingredientsSlice := []map[string]interface{}{} - - for ingredients.Next() { - - ingredientsFields := ingredients.FieldDescriptions() - ingredientsValues, err := ingredients.Values() - if err != nil { - c.IndentedJSON(500, gin.H{"err": err.Error()}) - return - } - - tempMap := map[string]interface{}{} - - for i, ingredient := range ingredientsFields { - tempMap[ingredient.Name] = ingredientsValues[i] - } - - ingredientsSlice = append(ingredientsSlice, tempMap) - - } - - returnData["proportions"] = ingredientsSlice - - ingredients.Close() - // - // End of adding ingredients - // - - // - // Start adding steps - // - - stepSlice := []map[string]interface{}{} - - steps, err := Dbx.Query(context.Background(), "select * from instructions where recipe_id = $1", recipe_id) - if err != nil { - c.IndentedJSON(500, gin.H{"err": err.Error()}) - return - } - - for steps.Next() { - tempStep := map[string]interface{}{} - - stepFields := steps.FieldDescriptions() - stepValues, err := steps.Values() - if err != nil { - c.IndentedJSON(500, gin.H{"err": err.Error()}) - return - } - - for i, step := range stepFields { - tempStep[step.Name] = stepValues[i] - } - - stepSlice = append(stepSlice, tempStep) - } - - returnData["steps"] = stepSlice - - steps.Close() - - // - // End adding steps - // - - c.IndentedJSON(200, returnData) -} - -// Register new user in the system and return JWT-token if -// all went well -func registerUser(c *gin.Context) { - type content struct { - Password string - Name string - Username string - } - - if c.ContentType() != "application/json" { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "content type should by 'application/json'"}) - return - } - - var body = content{} - - if err := c.BindJSON(&body); err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - newUuid := uuid.New().ID() - - hashedPassword, err := Argon2Hasher.HashGen([]byte(body.Password), []byte{}) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - rowsToClose, errr := Dbx.Query(context.Background(), "INSERT INTO users "+ - "(user_id, username, user_name, user_password) "+ - "VALUES($1, $2, $3, $4);", newUuid, body.Username, body.Name, hashedPassword.StringToStore) - - if errr != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": errr.Error()}) - return - } - - rowsToClose.Close() - - newToken, err := CreateToken(map[string]interface{}{"id": newUuid, "name": body.Name}) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - c.Header("Authorization", newToken) - c.IndentedJSON(200, gin.H{"newToken": newToken}) -} - -// Returns all available diets and ids of them -func getAllDiets(c *gin.Context) { - diets, err := Dbx.Query(context.Background(), "select * from diets") - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - sliceOfDiets := []map[string]interface{}{} - - for diets.Next() { - - temp := map[string]interface{}{} - - dietsRows := diets.FieldDescriptions() - dietsValues, err := diets.Values() - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - for i, value := range dietsRows { - temp[value.Name] = dietsValues[i] - } - - sliceOfDiets = append(sliceOfDiets, temp) - - } - - c.IndentedJSON(http.StatusOK, sliceOfDiets) -} - -// Returns slice of cuisines and they ids -func getAllCuisines(c *gin.Context) { - cuisines, err := Dbx.Query(context.Background(), "select * from cuisines") - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - cuisinesSlice := []map[string]interface{}{} - - for cuisines.Next() { - - cuisinesFields := cuisines.FieldDescriptions() - cuisinesValues, err := cuisines.Values() - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - temp := map[string]interface{}{} - - for i, cuisine := range cuisinesFields { - temp[cuisine.Name] = cuisinesValues[i] - } - - cuisinesSlice = append(cuisinesSlice, temp) - - } - - c.IndentedJSON(http.StatusOK, cuisinesSlice) - -} - -// Returns all ingredients with they ids -func getAllIngredients(c *gin.Context) { - cuisines, err := Dbx.Query(context.Background(), "select * from ingredients") - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - ingredientsSlice := []map[string]interface{}{} - - for cuisines.Next() { - - ingredientsFields := cuisines.FieldDescriptions() - ingredientsValues, err := cuisines.Values() - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - temp := map[string]interface{}{} - - for i, cuisine := range ingredientsFields { - temp[cuisine.Name] = ingredientsValues[i] - } - - ingredientsSlice = append(ingredientsSlice, temp) - - } - - c.IndentedJSON(http.StatusOK, ingredientsSlice) - -} - -func getRecipesByIngredient(c *gin.Context) { - ingredientId, err := strconv.Atoi(strings.Trim(c.Param("ingredientId"), "/")) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - recipes, err := Dbx.Query(context.Background(), - "select a.recipe_id, a.name, a.description, a.servings, a.prep_time, a.cook_time, a.image_url, a.source_url, a.author from recipes a join (select * from ingredients "+ - " b join recipe_ingredients c on b.ingredient_id = $1 where b.ingredient_id = c.ingredient_id) d "+ - " on a.recipe_id = d.recipe_id", - ingredientId) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - recipesSlice := []map[string]interface{}{} - - for recipes.Next() { - temp := map[string]interface{}{} - - recipeFields := recipes.FieldDescriptions() - recipeValues, err := recipes.Values() - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - for i, field := range recipeFields { - temp[field.Name] = recipeValues[i] - } - - recipesSlice = append(recipesSlice, temp) - - } - - c.IndentedJSON(http.StatusOK, recipesSlice) -} - -func getRecipesByCuisine(c *gin.Context) { - cuisineId, err := strconv.Atoi(strings.Trim(c.Param("cuisineId"), "/")) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - recipes, err := Dbx.Query(context.Background(), - "select a.recipe_id, a.name, a.description, a.servings, a.prep_time, a.cook_time, a.image_url, a.source_url, a.author from recipes a join (select * from cuisines "+ - " b join recipe_cuisines c on b.cuisine_id = $1 where b.cuisine_id = c.cuisine_id) d "+ - " on a.recipe_id = d.recipe_id", - cuisineId) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - cuisinesSlice := []map[string]interface{}{} - - for recipes.Next() { - temp := map[string]interface{}{} - - recipeFields := recipes.FieldDescriptions() - recipeValues, err := recipes.Values() - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - for i, field := range recipeFields { - temp[field.Name] = recipeValues[i] - } - - cuisinesSlice = append(cuisinesSlice, temp) - - } - - c.IndentedJSON(http.StatusOK, cuisinesSlice) -} - -func conditionalRollback(b *bool) { - if *b { - _, err := Dbx.Exec(context.Background(), - "ROLLBACK;") - if err != nil { - fmt.Println("Failed to rollback. Data in database could be invalid.") - return - } - fmt.Println(RedColor + strings.ToUpper("Some things went wrong. Rollback.") + ResetColor) - } -} - -func addRecipe(c *gin.Context) { - type ingredient struct { - IngredientId uint32 - Name string - Quantity float64 - Unit string - } - - type instruction struct { - InstructionId uint32 - Step_number uint16 - InstructionText string - InstructionTime string - } - - type cuisine struct { - CuisineId uint32 - Name string - } - - type diet struct { - DietId uint32 - Name string - } - - type content struct { - - // Recipe information we receive from client - Name string - Description string - Servings uint8 - Prep_time uint16 - Cook_time uint16 - Image_url string - Source_url string - - // Ingredient list - IngredientList []ingredient // Done - - // Instruction list - InstructionList []instruction // Done - - // Other information - Cuisines []cuisine // Client side - Diets []diet // Client side - } - - usrID, idExists := c.Get("id") - if !idExists { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing required data in token"}) - return - } - - // Id of user that created recipe - userId, err := strconv.Atoi(usrID.(string)) - if err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing required data in body of the request"}) - return - } - - var body content - - err = c.BindJSON(&body) - if err != nil { - fmt.Println(err.Error()) - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing required data in body of the request " + err.Error()}) - return - } - - recipeUuid := uuid.New() - - // New recipe uuid - var recipeUuidInt = recipeUuid.ID() - - // Set author ID as user ID request came from - - // Set new uuid for recipe - - // Ingredient to be added to the db - newIngredients := []ingredient{} - - needToRollback := false - _, err = Dbx.Exec(context.Background(), - "BEGIN;") - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": "failed to begin transaction"}) - return - } - defer conditionalRollback(&needToRollback) - - // Check if ingredient already in database - // if not we give it a uuid and add to slice to add it to db later - // Also we change name of ingredient to lower case - for i, ingredient := range body.IngredientList { - temp, err := Dbx.Query(context.Background(), - "select find_id_in_the_table_by_name_ingredient($1)", - strings.ToLower(ingredient.Name)) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - needToRollback = true - return - } - - temp.Next() - if t, err := temp.Values(); err == nil && t[0].(int64) < 0 { - - uid := uuid.New() - body.IngredientList[i].IngredientId = uid.ID() - body.IngredientList[i].Name = strings.ToLower(body.IngredientList[i].Name) - newIngredients = append(newIngredients, body.IngredientList[i]) - - } else if err == nil && t[0].(int64) > 0 { - - ttemp := t[0].(int64) - body.IngredientList[i].IngredientId = uint32(ttemp) - body.IngredientList[i].Name = strings.ToLower(body.IngredientList[i].Name) - - } else if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error in loop": err.Error()}) - needToRollback = true - return - } - temp.Close() - } - - // Give every instruction uuid - for i := range body.InstructionList { - body.InstructionList[i].InstructionId = uuid.New().ID() - } - - // Adding new ingredients to db - for _, newIngredient := range newIngredients { - _, err := Dbx.Exec(context.Background(), - "INSERT INTO ingredients "+ - `(ingredient_id, "name") `+ - "VALUES($1, $2);", - newIngredient.IngredientId, newIngredient.Name) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - needToRollback = true - return - } - - } - - // Adding recipe with it information - _, err = Dbx.Exec(context.Background(), - "INSERT INTO recipes "+ - `(recipe_id, "name", description, servings, prep_time, cook_time, image_url, source_url, author) `+ - "VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9);", - recipeUuidInt, body.Name, body.Description, body.Servings, body.Prep_time, body.Cook_time, body.Image_url, body.Source_url, userId) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error adding recipe": err.Error() + fmt.Sprint(userId)}) - needToRollback = true - return - } - - var counter = 0 - // Adding recipe steps to db - for _, instruction := range body.InstructionList { - counter++ - _, err := Dbx.Exec(context.Background(), - "INSERT INTO instructions "+ - "(instruction_id, recipe_id, step_number, instruction_text, instruction_time) "+ - "VALUES($1, $2, $3, $4, $5);", - instruction.InstructionId, recipeUuidInt, instruction.Step_number, instruction.InstructionText, instruction.InstructionTime) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error adding recipe step": err.Error() + fmt.Sprint(counter)}) - needToRollback = true - return - } - } - - // Link this recipe with stuff - - // Link recipe with ingredients - for _, ingredient := range body.IngredientList { - _, err := Dbx.Exec(context.Background(), - "INSERT INTO recipe_ingredients "+ - "(recipe_id, ingredient_id, quantity, unit) "+ - "VALUES($1, $2, $3, $4);", - recipeUuidInt, ingredient.IngredientId, ingredient.Quantity, ingredient.Unit) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error() + fmt.Sprint(recipeUuidInt, ingredient.IngredientId)}) - needToRollback = true - return - } - } - - // Link with cuisines - for _, cuisine := range body.Cuisines { - _, err := Dbx.Exec(context.Background(), - "INSERT INTO recipe_cuisines "+ - "(recipe_id, cuisine_id) "+ - "VALUES($1, $2);", - recipeUuidInt, cuisine.CuisineId) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - needToRollback = true - return - } - } - - // Link with diets - for _, diet := range body.Diets { - _, err := Dbx.Exec(context.Background(), - "INSERT INTO recipe_diets "+ - "(recipe_id, diet_id) "+ - "VALUES($1, $2);", - recipeUuidInt, diet.DietId) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - needToRollback = true - return - } - } - // - //Todo: Add transactions, so bad could not pass to db - - _, err = Dbx.Exec(context.Background(), - "COMMIT;") - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": "Failed to COMMIT " + err.Error()}) - needToRollback = true - return - } - - c.IndentedJSON(http.StatusOK, gin.H{"message": "all good"}) -} - -func findRecipesByIngredients(c *gin.Context) { - type content struct { - Ingredients []string - } - - if c.ContentType() != "application/json" { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "content_type is not 'application/json'"}) - return - } - - var body content - - if err := c.BindJSON(&body); err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error 1": `Invalid body of the request`}) - return - } - - recipe_ids, err := Dbx.Query(context.Background(), - `select recipe_id from - recipe_ingredients ri - join ingredients i on i.ingredient_id = ri.ingredient_id - where i.name in ('`+strings.Join(body.Ingredients[:], "','")+`') - group by recipe_id - having count(distinct name) = `+strconv.Itoa(len(body.Ingredients))) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error 2": err.Error()}) - return - } - - ids := []uint32{} - - for recipe_ids.Next() { - values, err := recipe_ids.Values() - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error 3": err.Error()}) - return - } - - ids = append(ids, uint32(values[0].(int64))) - } - - c.IndentedJSON(http.StatusOK, gin.H{"ids": ids}) -} - -func getRandomRecipes(c *gin.Context) { - recipes, err := Dbx.Query(context.Background(), - `select * from recipes`) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - recipeNumber := 15 - recipesToReturn := make([]map[string]interface{}, recipeNumber) - - recipeCounter := 0 - r := rand.New(rand.NewSource(time.Now().Unix())) - for recipes.Next() { - b := r.Intn(10) - - if b >= 9 { - temp := map[string]interface{}{} - - fields := recipes.FieldDescriptions() - val, err := recipes.Values() - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - for i, field := range fields { - temp[field.Name] = val[i] - } - recipesToReturn[recipeCounter] = temp - - recipeCounter++ - - if recipeCounter >= recipeNumber { - break - } - } - - } - - recipes.Close() - - c.IndentedJSON(http.StatusOK, recipesToReturn) -} - -func isAuth(c *gin.Context) { - _, err := ValidateToken(c.GetHeader("Authorization")) - - if err != nil { - c.IndentedJSON(http.StatusUnauthorized, gin.H{"message": "invalid token"}) - return - } - - c.IndentedJSON(http.StatusOK, gin.H{"message": "valid token"}) -} - -func addFavoriteRecipe(c *gin.Context) { - - usrID, exists := c.Get("id") - if !exists { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing required data in token"}) - return - } - - recipeId, err := strconv.Atoi(strings.Trim(c.Param("recipeId"), "/")) - if err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing recipe id"}) - return - } - - _, err = Dbx.Exec(context.Background(), `INSERT INTO user_favorite_recipe - (user_id, recipe_id) - VALUES($1, $2);`, usrID, recipeId) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error/inserting": err.Error()}) - return - } - - c.IndentedJSON(http.StatusOK, gin.H{"message": "all good"}) - -} - -func removeFavoriteRecipe(c *gin.Context) { - - usrID, exists := c.Get("id") - if !exists { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing required data in token"}) - return - } - - recipeId, err := strconv.Atoi(strings.Trim(c.Param("recipeId"), "/")) - if err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing recipe id"}) - return - } - - _, err = Dbx.Exec(context.Background(), `DELETE from user_favorite_recipe where - user_id = $1 and recipe_id = $2`, usrID, recipeId) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error/inserting": err.Error()}) - return - } - - c.IndentedJSON(http.StatusOK, gin.H{"message": "all good"}) - -} - -func getAllFavoriteRecipes(c *gin.Context) { - usrID, exists := c.Get("id") - if !exists { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing required data in token"}) - return - } - - recipeIdsRows, err := Dbx.Query(context.Background(), `select * from recipes r - join user_favorite_recipe ufr on r.recipe_id = ufr.recipe_id - where user_id = $1`, usrID) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error/selecting": err.Error()}) - return - } - - recipeIds := make([]any, 0) - for recipeIdsRows.Next() { - fieldNames := recipeIdsRows.FieldDescriptions() - temp, err := recipeIdsRows.Values() - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error/selecting": err.Error()}) - return - } - tempMap := map[string]any{} - for i, fieldName := range fieldNames { - tempMap[fieldName.Name] = temp[i] - } - recipeIds = append(recipeIds, tempMap) - } - - c.IndentedJSON(http.StatusOK, gin.H{"values": recipeIds}) -} - -func isFavorite(c *gin.Context) { - usrID, exists := c.Get("id") - if !exists { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": "missing required data in token"}) - return - } - - recipeId, err := strconv.Atoi(strings.Trim(c.Param("recipeId"), "/")) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - rows, err := Dbx.Query(context.Background(), `SELECT user_id, recipe_id - FROM user_favorite_recipe where user_id = $1 and recipe_id = $2`, usrID, recipeId) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error/selecting": err.Error()}) - return - } - - flag := false - for rows.Next() { - flag = true - } - - c.IndentedJSON(http.StatusOK, flag) -} - -func getRecipesByName(c *gin.Context) { - recipeName := strings.Trim(c.Param("recipeName"), "/") - - rows, err := Dbx.Query(context.Background(), `SELECT * - FROM recipes where "name" LIKE $1;`, fmt.Sprint(`%`, recipeName, `%`)) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error in select (getRecipesByName)": err.Error()}) - return - } - - recipesToReturn := []map[string]any{} - - for rows.Next() { - fields := rows.FieldDescriptions() - values, err := rows.Values() - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - recipeInfo := map[string]any{} - for i, field := range fields { - recipeInfo[field.Name] = values[i] - } - - recipesToReturn = append(recipesToReturn, recipeInfo) - } - - c.IndentedJSON(http.StatusOK, recipesToReturn) -} - -func deleteRecipe(c *gin.Context) { - fmt.Println(c.Param("recipe_id")) - recipe_id, err := strconv.Atoi(strings.Trim(c.Param("recipe_id"), "/")) - if err != nil { - c.IndentedJSON(404, gin.H{"err": err.Error()}) - return - } - - userID, exists := c.Get("id") - if !exists { - c.IndentedJSON(404, gin.H{"err": "no valid token provided"}) - return - } - - var count int - - Dbx.QueryRow(context.Background(), `select count(*) from recipes where author = $1 and recipe_id = $2`, userID, recipe_id).Scan(&count) - - if count > 0 { - _, err := Dbx.Exec(context.Background(), `DELETE FROM recipes - WHERE recipe_id = $1`, recipe_id) - - if err != nil { - c.IndentedJSON(404, gin.H{"err": err.Error()}) - return - } - } else { - c.IndentedJSON(http.StatusUnauthorized, gin.H{"message": "Could not delete recipe. ID: " + strconv.Itoa(recipe_id)}) - return - } - - c.IndentedJSON(http.StatusOK, gin.H{"message": "Successfully deleted recipe. ID: " + strconv.Itoa(recipe_id)}) -} - -func getAllUserRecipes(c *gin.Context) { - userID, exists := c.Get("id") - if !exists { - c.IndentedJSON(404, gin.H{"err": "no valid token provided"}) - return - } - - rows, err := Dbx.Query(context.Background(), `select * from recipes where author = $1`, userID) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - recipesToReturn := []map[string]any{} - - for rows.Next() { - fields := rows.FieldDescriptions() - values, err := rows.Values() - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - recipeInfo := map[string]any{} - for i, field := range fields { - recipeInfo[field.Name] = values[i] - } - - recipesToReturn = append(recipesToReturn, recipeInfo) - } - - c.IndentedJSON(http.StatusOK, recipesToReturn) - +func testCookie(c *gin.Context) { + cock, _ := c.Cookie("auth_cookie") + c.IndentedJSON(http.StatusOK, gin.H{"token": "SLESAR' U STASA " + strings.Split(cock, "_")[0]}) } func SetupRotes(g *gin.Engine) error { @@ -978,32 +22,14 @@ func SetupRotes(g *gin.Engine) error { freeGroup := g.Group("/") // Free group routes - freeGroup.GET("recipe_info/:recipe_id", getRecipeInformation) - freeGroup.GET("get_all_diets", getAllDiets) - freeGroup.GET("get_all_cuisines", getAllCuisines) - freeGroup.GET("get_all_ingredients", getAllIngredients) - freeGroup.GET("get_recipes_by_ingredient/:ingredientId", getRecipesByIngredient) - freeGroup.GET("get_recipes_by_cuisine/:cuisineId", getRecipesByCuisine) - freeGroup.GET("get_recipes_by_name/:recipeName", getRecipesByName) - freeGroup.GET("getRandomRecipes", getRandomRecipes) - freeGroup.GET("isAuth", isAuth) - freeGroup.POST("register_user", registerUser) + freeGroup.POST("login", login) - freeGroup.POST("get_recipes_by_ingredients", findRecipesByIngredients) + freeGroup.GET("getCookie", testCookie) authGroup := g.Group("/") authGroup.Use(AuthMiddleware()) // Auth group routes - authGroup.GET("test1", testRoute1) - authGroup.GET("addToFavorite/:recipeId", addFavoriteRecipe) - authGroup.GET("removeFromFavorite/:recipeId", removeFavoriteRecipe) - authGroup.GET("getAllFavorite", getAllFavoriteRecipes) - authGroup.GET("isFavorite/:recipeId", isFavorite) - authGroup.GET("getAllUserRecipes", getAllUserRecipes) - authGroup.POST("test2", testRoute2) - authGroup.POST("add_recipe", addRecipe) - authGroup.DELETE("deleteRecipe/:recipe_id", deleteRecipe) return nil } @@ -1022,41 +48,28 @@ func login(c *gin.Context) { return } - hash, err := Dbx.Query(context.Background(), - "select * from users where username = $1;", - body.Nickname) - - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error 2nd": err.Error()}) - return - } - - hash.Next() - values, err := hash.Values() + repo := db_repo.New(Dbx) + user, err := repo.GetUserByUsername(context.Background(), body.Nickname) if err != nil { c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } - user_id := strconv.FormatInt(values[0].(int64), 10) - user_name := values[2].(string) - user_hash := values[3].(string) - hash.Close() - user_hash_, salt, err := DecodeArgon2String(user_hash) + password_hash, salt, err := DecodeArgon2String(user.Password) if err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - err = Argon2Hasher.Compare(user_hash_, salt, []byte(body.Password)) + err = Argon2Hasher.Compare(password_hash, salt, []byte(body.Password)) if err != nil { c.IndentedJSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) return } user_info := map[string]interface{}{ - "id": user_id, - "name": user_name, + "id": user.UserID, + "name": user.Username, } token, err := CreateToken(user_info) @@ -1065,16 +78,27 @@ func login(c *gin.Context) { return } + cookieName := "auth_cookie" + cookieValue := "id=" + strconv.FormatInt(user_info["id"].(int64), 10) + + "_nickname=" + user_info["name"].(string) + maxAge := int(2 * time.Hour.Seconds()) // Cookie expiry time in seconds (1 hour) + path := "/" // Cookie path + domain := "localhost" // Set domain (localhost for testing) + secure := false // Secure cookie (set to true in production with HTTPS) + httpOnly := false // HTTP only, so it can't be accessed by JavaScript + // LookupEnv(&domain, "DOMAIN") + c.Header("Authorization", token) + c.SetCookie(cookieName, cookieValue, maxAge, path, domain, secure, httpOnly) c.IndentedJSON(http.StatusOK, gin.H{"token": token}) } func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "*") + c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173") c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, authorization, Authorization, accept, origin, Cache-Control, X-Requested-With") + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, authorization, Authorization, accept, origin, Cache-Control, X-Requested-With, Cookie") c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT") c.Writer.Header().Set("Access-Control-Expose-Headers", "Access-Token, Uid, Authorization") diff --git a/package-lock.json b/package-lock.json index caaa064..793629c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,8 @@ "packages": { "": { "dependencies": { - "@tanstack/react-query": "^5.59.0" + "@tanstack/react-query": "^5.59.0", + "axios": "^1.7.7" } }, "node_modules/@tanstack/query-core": { @@ -34,6 +35,78 @@ "react": "^18 || ^19" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -54,6 +127,33 @@ "loose-envify": "cli.js" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", diff --git a/package.json b/package.json index 29cce3b..82a5d97 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "dependencies": { - "@tanstack/react-query": "^5.59.0" + "@tanstack/react-query": "^5.59.0", + "axios": "^1.7.7" } }