added s3 compatible, and updated stuff
This commit is contained in:
parent
2810370c02
commit
771eb60ea4
@ -215,13 +215,29 @@ func (server *Server) getLocation(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type reviewWithImages struct {
|
||||||
|
db.GetListLocationReviewsRow
|
||||||
|
Images []db.GetImagesByReviewRow `json:"images"`
|
||||||
|
}
|
||||||
|
|
||||||
|
attachImages := func(reviews []db.GetListLocationReviewsRow) []reviewWithImages {
|
||||||
|
result := make([]reviewWithImages, 0, len(reviews))
|
||||||
|
for _, r := range reviews {
|
||||||
|
images, _ := server.Store.GetImagesByReview(ctx, r.ID)
|
||||||
|
if images == nil {
|
||||||
|
images = []db.GetImagesByReviewRow{}
|
||||||
|
}
|
||||||
|
result = append(result, reviewWithImages{r, images})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
users_reviews, err := server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{
|
users_reviews, err := server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{
|
||||||
LocationID: req.ID,
|
LocationID: req.ID,
|
||||||
Limit: 5,
|
Limit: 5,
|
||||||
Offset: 0,
|
Offset: 0,
|
||||||
IsCritics: false,
|
IsCritics: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location"))
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location"))
|
||||||
return
|
return
|
||||||
@ -233,7 +249,6 @@ func (server *Server) getLocation(ctx *gin.Context) {
|
|||||||
Offset: 0,
|
Offset: 0,
|
||||||
IsCritics: true,
|
IsCritics: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location"))
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location"))
|
||||||
return
|
return
|
||||||
@ -242,8 +257,8 @@ func (server *Server) getLocation(ctx *gin.Context) {
|
|||||||
res := gin.H{
|
res := gin.H{
|
||||||
"tags": tags,
|
"tags": tags,
|
||||||
"detail": location,
|
"detail": location,
|
||||||
"users_review": users_reviews,
|
"users_review": attachImages(users_reviews),
|
||||||
"critics_review": critics_reviews,
|
"critics_review": attachImages(critics_reviews),
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, res)
|
ctx.JSON(http.StatusOK, res)
|
||||||
|
|||||||
@ -1,13 +1,19 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
db "git.nochill.in/nochill/hiling_go/db/repository"
|
db "git.nochill.in/nochill/hiling_go/db/repository"
|
||||||
|
"git.nochill.in/nochill/hiling_go/util"
|
||||||
"git.nochill.in/nochill/hiling_go/util/token"
|
"git.nochill.in/nochill/hiling_go/util/token"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type createReviewReq struct {
|
type createReviewReq struct {
|
||||||
@ -17,6 +23,7 @@ type createReviewReq struct {
|
|||||||
IsFromCritic *bool `json:"is_from_critic"`
|
IsFromCritic *bool `json:"is_from_critic"`
|
||||||
IsHided *bool `json:"is_hided"`
|
IsHided *bool `json:"is_hided"`
|
||||||
LocationId int32 `json:"location_id" binding:"required"`
|
LocationId int32 `json:"location_id" binding:"required"`
|
||||||
|
Title string `json:"title"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) createReview(ctx *gin.Context) {
|
func (server *Server) createReview(ctx *gin.Context) {
|
||||||
@ -51,6 +58,7 @@ func (server *Server) createReview(ctx *gin.Context) {
|
|||||||
IsFromCritic: *req.IsFromCritic,
|
IsFromCritic: *req.IsFromCritic,
|
||||||
IsHided: *req.IsHided,
|
IsHided: *req.IsHided,
|
||||||
LocationID: req.LocationId,
|
LocationID: req.LocationId,
|
||||||
|
Title: pgtype.Text{String: req.Title, Valid: req.Title != ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
review, err := server.Store.CreateReview(ctx, arg)
|
review, err := server.Store.CreateReview(ctx, arg)
|
||||||
@ -63,10 +71,77 @@ func (server *Server) createReview(ctx *gin.Context) {
|
|||||||
ctx.JSON(http.StatusOK, review)
|
ctx.JSON(http.StatusOK, review)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) uploadReviewImages(ctx *gin.Context) {
|
||||||
|
reviewIdStr := ctx.PostForm("review_id")
|
||||||
|
reviewId, err := strconv.Atoi(reviewIdStr)
|
||||||
|
if err != nil || reviewId <= 0 {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"message": "Invalid review_id"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||||
|
|
||||||
|
form, err := ctx.MultipartForm()
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, ErrorResponse(err, "Failed to parse multipart form"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
files := form.File["images"]
|
||||||
|
if len(files) == 0 {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"message": "No images provided"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var params []db.CreateImageParams
|
||||||
|
|
||||||
|
for _, fileHeader := range files {
|
||||||
|
fileExt := filepath.Ext(fileHeader.Filename)
|
||||||
|
key := fmt.Sprintf("reviews/%d/%s%d%s", reviewId, util.RandomString(5), time.Now().UnixNano(), fileExt)
|
||||||
|
|
||||||
|
f, err := fileHeader.Open()
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Failed to open image"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
contentType := fileHeader.Header.Get("Content-Type")
|
||||||
|
if contentType == "" {
|
||||||
|
contentType = "application/octet-stream"
|
||||||
|
}
|
||||||
|
|
||||||
|
publicURL, err := server.R2.UploadFile(ctx.Request.Context(), key, f, contentType)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Failed to upload image to storage"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
params = append(params, db.CreateImageParams{
|
||||||
|
ImageUrl: publicURL,
|
||||||
|
UploadedBy: int32(authPayload.UserID),
|
||||||
|
ImageType: "reviews",
|
||||||
|
ImageOf: int32(reviewId),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := server.Store.CreateImage(ctx, params); err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Failed to save image records"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"uploaded": len(params)})
|
||||||
|
}
|
||||||
|
|
||||||
type getUserReviewByLocationReq struct {
|
type getUserReviewByLocationReq struct {
|
||||||
LocationID int32 `uri:"location_id" binding:"required"`
|
LocationID int32 `uri:"location_id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type userReviewWithImagesResponse struct {
|
||||||
|
db.GetUserReviewByLocationRow
|
||||||
|
Images []db.GetImagesByReviewRow `json:"images"`
|
||||||
|
}
|
||||||
|
|
||||||
func (server *Server) getUserReviewByLocation(ctx *gin.Context) {
|
func (server *Server) getUserReviewByLocation(ctx *gin.Context) {
|
||||||
var req getUserReviewByLocationReq
|
var req getUserReviewByLocationReq
|
||||||
|
|
||||||
@ -95,5 +170,13 @@ func (server *Server) getUserReviewByLocation(ctx *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, review)
|
images, err := server.Store.GetImagesByReview(ctx, review.ID)
|
||||||
|
if err != nil {
|
||||||
|
images = []db.GetImagesByReviewRow{}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, userReviewWithImagesResponse{
|
||||||
|
GetUserReviewByLocationRow: review,
|
||||||
|
Images: images,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
db "git.nochill.in/nochill/hiling_go/db/repository"
|
db "git.nochill.in/nochill/hiling_go/db/repository"
|
||||||
"git.nochill.in/nochill/hiling_go/util"
|
"git.nochill.in/nochill/hiling_go/util"
|
||||||
|
"git.nochill.in/nochill/hiling_go/util/cloudfare"
|
||||||
"git.nochill.in/nochill/hiling_go/util/token"
|
"git.nochill.in/nochill/hiling_go/util/token"
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -17,6 +18,7 @@ type Server struct {
|
|||||||
TokenMaker token.Maker
|
TokenMaker token.Maker
|
||||||
Router *gin.Engine
|
Router *gin.Engine
|
||||||
MeilisearchClient *meilisearch.Client
|
MeilisearchClient *meilisearch.Client
|
||||||
|
R2 *cloudfare.R2Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(config util.Config, store db.Store) (*Server, error) {
|
func NewServer(config util.Config, store db.Store) (*Server, error) {
|
||||||
@ -30,11 +32,23 @@ func NewServer(config util.Config, store db.Store) (*Server, error) {
|
|||||||
APIKey: config.MeilisearchKey,
|
APIKey: config.MeilisearchKey,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r2Client, err := cloudfare.NewR2Client(
|
||||||
|
config.R2AccountID,
|
||||||
|
config.R2AccessKeyID,
|
||||||
|
config.R2SecretAccessKey,
|
||||||
|
config.R2BucketName,
|
||||||
|
config.R2PublicApiID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot create R2 client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
server := &Server{
|
server := &Server{
|
||||||
Config: config,
|
Config: config,
|
||||||
Store: store,
|
Store: store,
|
||||||
TokenMaker: tokenMaker,
|
TokenMaker: tokenMaker,
|
||||||
MeilisearchClient: meiliClient,
|
MeilisearchClient: meiliClient,
|
||||||
|
R2: r2Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
server.getRoutes()
|
server.getRoutes()
|
||||||
@ -45,7 +59,7 @@ func (server *Server) getRoutes() {
|
|||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
router.Use(cors.New(cors.Config{
|
router.Use(cors.New(cors.Config{
|
||||||
AllowOrigins: []string{"http://localhost:5173", "http://192.168.18.164:5173", "http://192.168.1.13:5173"},
|
AllowOrigins: []string{"http://localhost:5173", "http://192.168.18.164:5173", "http://192.168.18.150:5173", "http://192.168.1.9:5173"},
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With", "X-XSRF-TOKEN"},
|
AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With", "X-XSRF-TOKEN"},
|
||||||
AllowMethods: []string{"POST", "PUT", "GET", "DELETE", "PATCH"},
|
AllowMethods: []string{"POST", "PUT", "GET", "DELETE", "PATCH"},
|
||||||
@ -77,6 +91,7 @@ func (server *Server) getRoutes() {
|
|||||||
// REQUIRE AUTH TOKEN
|
// REQUIRE AUTH TOKEN
|
||||||
authRoutes := router.Use(authMiddleware(server.TokenMaker))
|
authRoutes := router.Use(authMiddleware(server.TokenMaker))
|
||||||
authRoutes.POST("/review/location", server.createReview)
|
authRoutes.POST("/review/location", server.createReview)
|
||||||
|
authRoutes.POST("/review/location/images", server.uploadReviewImages)
|
||||||
authRoutes.PATCH("/user", server.updateUser)
|
authRoutes.PATCH("/user", server.updateUser)
|
||||||
authRoutes.GET("/user/review/location/:location_id", server.getUserReviewByLocation)
|
authRoutes.GET("/user/review/location/:location_id", server.getUserReviewByLocation)
|
||||||
authRoutes.GET("/user/profile", server.getUserStats)
|
authRoutes.GET("/user/profile", server.getUserStats)
|
||||||
|
|||||||
@ -118,6 +118,7 @@ CREATE TABLE location_images (
|
|||||||
|
|
||||||
CREATE TABLE reviews (
|
CREATE TABLE reviews (
|
||||||
"id" serial primary key not null,
|
"id" serial primary key not null,
|
||||||
|
"title" varchar,
|
||||||
"submitted_by" integer references "users"("id") not null,
|
"submitted_by" integer references "users"("id") not null,
|
||||||
"comments" text not null,
|
"comments" text not null,
|
||||||
"score" smallint not null,
|
"score" smallint not null,
|
||||||
|
|||||||
@ -84,6 +84,21 @@ func (mr *MockStoreMockRecorder) CreateImage(arg0, arg1 any) *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateImage", reflect.TypeOf((*MockStore)(nil).CreateImage), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateImage", reflect.TypeOf((*MockStore)(nil).CreateImage), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetImagesByReview mocks base method.
|
||||||
|
func (m *MockStore) GetImagesByReview(arg0 context.Context, arg1 int32) ([]db.GetImagesByReviewRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetImagesByReview", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]db.GetImagesByReviewRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImagesByReview indicates an expected call of GetImagesByReview.
|
||||||
|
func (mr *MockStoreMockRecorder) GetImagesByReview(arg0, arg1 any) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImagesByReview", reflect.TypeOf((*MockStore)(nil).GetImagesByReview), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateLocation mocks base method.
|
// CreateLocation mocks base method.
|
||||||
func (m *MockStore) CreateLocation(arg0 context.Context, arg1 db.CreateLocationParams) (int32, error) {
|
func (m *MockStore) CreateLocation(arg0 context.Context, arg1 db.CreateLocationParams) (int32, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@ -7,6 +7,8 @@ WHERE reviews.location_id = $1 AND reviews.submitted_by = $2;
|
|||||||
-- name: GetUserReviewByLocation :one
|
-- name: GetUserReviewByLocation :one
|
||||||
SELECT
|
SELECT
|
||||||
re.id,
|
re.id,
|
||||||
|
re.title,
|
||||||
|
re.submitted_by,
|
||||||
re.location_id,
|
re.location_id,
|
||||||
re.score,
|
re.score,
|
||||||
re.comments,
|
re.comments,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: follow.sql
|
// source: follow.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -61,6 +61,39 @@ func (q *Queries) GetImagesByLocation(ctx context.Context, arg GetImagesByLocati
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetImagesByReviewRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Src string `json:"src"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const getImagesByReviewQ = `
|
||||||
|
SELECT id, image_url as src
|
||||||
|
FROM images
|
||||||
|
WHERE image_type = 'reviews' AND image_of = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetImagesByReview(ctx context.Context, reviewID int32) ([]GetImagesByReviewRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getImagesByReviewQ, reviewID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
items := []GetImagesByReviewRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetImagesByReviewRow
|
||||||
|
if err := rows.Scan(&i.ID, &i.Src); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
type CreateImageParams struct {
|
type CreateImageParams struct {
|
||||||
ImageUrl string `json:"url"`
|
ImageUrl string `json:"url"`
|
||||||
UploadedBy int32 `json:"uploaded_by"`
|
UploadedBy int32 `json:"uploaded_by"`
|
||||||
@ -87,11 +120,7 @@ func (q *Queries) CreateImage(ctx context.Context, arg []CreateImageParams) erro
|
|||||||
// Replacing ? with $n for postgres
|
// Replacing ? with $n for postgres
|
||||||
queryStr = util.ReplaceSQL(queryStr, "?")
|
queryStr = util.ReplaceSQL(queryStr, "?")
|
||||||
|
|
||||||
// prepare the statement
|
_, err := q.db.Exec(ctx, queryStr, values...)
|
||||||
_, err := q.db.Exec(ctx, queryStr)
|
|
||||||
|
|
||||||
// format all vals at once
|
|
||||||
// _, err := stmt.Ex(ctx, values...)
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: images.sql
|
// source: images.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: locations.sql
|
// source: locations.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
@ -58,10 +58,11 @@ func (ns NullCommentType) Value() (driver.Value, error) {
|
|||||||
type LocationType string
|
type LocationType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LocationTypeBeach LocationType = "beach"
|
LocationTypeTraditionalmarket LocationType = "traditional market"
|
||||||
LocationTypeAmusementpark LocationType = "amusement park"
|
LocationTypeMall LocationType = "mall"
|
||||||
LocationTypeCulinary LocationType = "culinary"
|
LocationTypeCulinary LocationType = "culinary"
|
||||||
LocationTypeHikingCamping LocationType = "hiking / camping"
|
LocationTypeRecreation LocationType = "recreation"
|
||||||
|
LocationTypeAccommodation LocationType = "accommodation"
|
||||||
LocationTypeOther LocationType = "other"
|
LocationTypeOther LocationType = "other"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -168,6 +169,12 @@ type Comment struct {
|
|||||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Emote struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
EmoteID string `json:"emote_id"`
|
||||||
|
}
|
||||||
|
|
||||||
type Image struct {
|
type Image struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
ImageUrl string `json:"image_url"`
|
ImageUrl string `json:"image_url"`
|
||||||
@ -204,6 +211,15 @@ type LocationImage struct {
|
|||||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LocationPageVisit struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
LocationID int32 `json:"location_id"`
|
||||||
|
WeekKey string `json:"week_key"`
|
||||||
|
VisitCount int64 `json:"visit_count"`
|
||||||
|
IsDeleted pgtype.Bool `json:"is_deleted"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type NewsEvent struct {
|
type NewsEvent struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
@ -243,6 +259,7 @@ type Region struct {
|
|||||||
|
|
||||||
type Review struct {
|
type Review struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
|
Title pgtype.Text `json:"title"`
|
||||||
SubmittedBy int32 `json:"submitted_by"`
|
SubmittedBy int32 `json:"submitted_by"`
|
||||||
Comments string `json:"comments"`
|
Comments string `json:"comments"`
|
||||||
Score int16 `json:"score"`
|
Score int16 `json:"score"`
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: news_events.sql
|
// source: news_events.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: provinces.sql
|
// source: provinces.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: regencies.sql
|
// source: regencies.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: regions.sql
|
// source: regions.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -14,9 +14,10 @@ INSERT INTO reviews (
|
|||||||
score,
|
score,
|
||||||
is_from_critic,
|
is_from_critic,
|
||||||
is_hided,
|
is_hided,
|
||||||
location_id
|
location_id,
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6)
|
title
|
||||||
RETURNING id, submitted_by, comments, score, is_from_critic, is_hided, location_id, created_at, updated_at
|
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
RETURNING id, submitted_by, comments, score, is_from_critic, is_hided, location_id, title, created_at, updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateReviewParams struct {
|
type CreateReviewParams struct {
|
||||||
@ -26,6 +27,7 @@ type CreateReviewParams struct {
|
|||||||
IsFromCritic bool `json:"is_from_critic"`
|
IsFromCritic bool `json:"is_from_critic"`
|
||||||
IsHided bool `json:"is_hided"`
|
IsHided bool `json:"is_hided"`
|
||||||
LocationID int32 `json:"location_id"`
|
LocationID int32 `json:"location_id"`
|
||||||
|
Title pgtype.Text `json:"title"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Review, error) {
|
func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Review, error) {
|
||||||
@ -36,6 +38,7 @@ func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Rev
|
|||||||
arg.IsFromCritic,
|
arg.IsFromCritic,
|
||||||
arg.IsHided,
|
arg.IsHided,
|
||||||
arg.LocationID,
|
arg.LocationID,
|
||||||
|
arg.Title,
|
||||||
)
|
)
|
||||||
var i Review
|
var i Review
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
@ -46,6 +49,7 @@ func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Rev
|
|||||||
&i.IsFromCritic,
|
&i.IsFromCritic,
|
||||||
&i.IsHided,
|
&i.IsHided,
|
||||||
&i.LocationID,
|
&i.LocationID,
|
||||||
|
&i.Title,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
)
|
)
|
||||||
@ -61,6 +65,7 @@ type GetListLocationReviewsParams struct {
|
|||||||
|
|
||||||
type GetListLocationReviewsRow struct {
|
type GetListLocationReviewsRow struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
|
Title pgtype.Text `json:"title"`
|
||||||
Score int8 `json:"score"`
|
Score int8 `json:"score"`
|
||||||
Comment string `json:"comments"`
|
Comment string `json:"comments"`
|
||||||
UserID int32 `json:"user_id"`
|
UserID int32 `json:"user_id"`
|
||||||
@ -73,6 +78,7 @@ type GetListLocationReviewsRow struct {
|
|||||||
const getListLocationReviews = `
|
const getListLocationReviews = `
|
||||||
SELECT
|
SELECT
|
||||||
re.id as id,
|
re.id as id,
|
||||||
|
re.title as title,
|
||||||
re.score as score,
|
re.score as score,
|
||||||
re.comments as comments,
|
re.comments as comments,
|
||||||
u.id as user_id,
|
u.id as user_id,
|
||||||
@ -101,6 +107,7 @@ func (q *Queries) GetListLocationReviews(ctx context.Context, arg GetListLocatio
|
|||||||
var i GetListLocationReviewsRow
|
var i GetListLocationReviewsRow
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
|
&i.Title,
|
||||||
&i.Score,
|
&i.Score,
|
||||||
&i.Comment,
|
&i.Comment,
|
||||||
&i.UserID,
|
&i.UserID,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: reviews.sql
|
// source: reviews.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
@ -32,6 +32,8 @@ func (q *Queries) CheckIfReviewExists(ctx context.Context, arg CheckIfReviewExis
|
|||||||
const getUserReviewByLocation = `-- name: GetUserReviewByLocation :one
|
const getUserReviewByLocation = `-- name: GetUserReviewByLocation :one
|
||||||
SELECT
|
SELECT
|
||||||
re.id,
|
re.id,
|
||||||
|
re.title,
|
||||||
|
re.submitted_by,
|
||||||
re.location_id,
|
re.location_id,
|
||||||
re.score,
|
re.score,
|
||||||
re.comments,
|
re.comments,
|
||||||
@ -48,6 +50,8 @@ type GetUserReviewByLocationParams struct {
|
|||||||
|
|
||||||
type GetUserReviewByLocationRow struct {
|
type GetUserReviewByLocationRow struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
|
Title pgtype.Text `json:"title"`
|
||||||
|
SubmittedBy int32 `json:"submitted_by"`
|
||||||
LocationID int32 `json:"location_id"`
|
LocationID int32 `json:"location_id"`
|
||||||
Score int16 `json:"score"`
|
Score int16 `json:"score"`
|
||||||
Comments string `json:"comments"`
|
Comments string `json:"comments"`
|
||||||
@ -60,6 +64,8 @@ func (q *Queries) GetUserReviewByLocation(ctx context.Context, arg GetUserReview
|
|||||||
var i GetUserReviewByLocationRow
|
var i GetUserReviewByLocationRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
|
&i.Title,
|
||||||
|
&i.SubmittedBy,
|
||||||
&i.LocationID,
|
&i.LocationID,
|
||||||
&i.Score,
|
&i.Score,
|
||||||
&i.Comments,
|
&i.Comments,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: sessions.sql
|
// source: sessions.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
@ -19,6 +19,7 @@ type Store interface {
|
|||||||
GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error)
|
GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error)
|
||||||
CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error)
|
CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error)
|
||||||
CreateImage(ctx context.Context, arg []CreateImageParams) error
|
CreateImage(ctx context.Context, arg []CreateImageParams) error
|
||||||
|
GetImagesByReview(ctx context.Context, reviewID int32) ([]GetImagesByReviewRow, error)
|
||||||
CreateLocationTx(ctx context.Context, arg CreateLocationTxParams) error
|
CreateLocationTx(ctx context.Context, arg CreateLocationTxParams) error
|
||||||
GetNewsEventsList(ctx context.Context, arg GetNewsEventsListParams) ([]NewsEventRow, error)
|
GetNewsEventsList(ctx context.Context, arg GetNewsEventsListParams) ([]NewsEventRow, error)
|
||||||
GetListRecentLocationsWithRatings(ctx context.Context, arg GetListRecentLocationsParams) ([]GetListRecentLocationsWithRatingsRow, error)
|
GetListRecentLocationsWithRatings(ctx context.Context, arg GetListRecentLocationsParams) ([]GetListRecentLocationsWithRatingsRow, error)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.30.0
|
||||||
// source: users.sql
|
// source: users.sql
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|||||||
25
go.mod
25
go.mod
@ -1,13 +1,15 @@
|
|||||||
module git.nochill.in/nochill/hiling_go
|
module git.nochill.in/nochill/hiling_go
|
||||||
|
|
||||||
go 1.20
|
go 1.24
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29
|
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29
|
||||||
github.com/gin-contrib/cors v1.4.0
|
github.com/gin-contrib/cors v1.4.0
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-playground/validator/v10 v10.17.0
|
github.com/go-playground/validator/v10 v10.17.0
|
||||||
|
github.com/henvic/pgq v0.0.2
|
||||||
github.com/jackc/pgx/v5 v5.5.3
|
github.com/jackc/pgx/v5 v5.5.3
|
||||||
|
github.com/meilisearch/meilisearch-go v0.26.2
|
||||||
github.com/o1egl/paseto v1.0.0
|
github.com/o1egl/paseto v1.0.0
|
||||||
github.com/spf13/viper v1.16.0
|
github.com/spf13/viper v1.16.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
@ -20,6 +22,25 @@ require (
|
|||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
|
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.32.14 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
||||||
|
github.com/aws/smithy-go v1.24.2 // indirect
|
||||||
github.com/bytedance/sonic v1.10.2 // indirect
|
github.com/bytedance/sonic v1.10.2 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
@ -32,7 +53,6 @@ require (
|
|||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/henvic/pgq v0.0.2 // indirect
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
@ -44,7 +64,6 @@ require (
|
|||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/meilisearch/meilisearch-go v0.26.2 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
|||||||
43
go.sum
43
go.sum
@ -47,6 +47,44 @@ github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyY
|
|||||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.32.14/go.mod h1:U4/V0uKxh0Tl5sxmCBZ3AecYny4UNlVmObYjKuuaiOo=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 h1:hlSuz394kV0vhv9drL5lhuEFbEOEP1VyQpy15qWh1Pk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
|
||||||
|
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||||
|
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||||
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
||||||
@ -77,6 +115,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||||
|
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
@ -93,6 +132,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
|
|||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
@ -146,6 +186,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
@ -200,6 +241,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@ -242,6 +284,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
|||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||||
|
|||||||
@ -1,2 +1,57 @@
|
|||||||
package cloudfare
|
package cloudfare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"mime/multipart"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type R2Client struct {
|
||||||
|
s3Client *s3.Client
|
||||||
|
bucketName string
|
||||||
|
publicBaseURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewR2Client(accountID, accessKeyID, secretAccessKey, bucketName, publicApiID string) (*R2Client, error) {
|
||||||
|
r2Endpoint := fmt.Sprintf("https://%s.r2.cloudflarestorage.com", accountID)
|
||||||
|
|
||||||
|
cfg, err := config.LoadDefaultConfig(context.Background(),
|
||||||
|
config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, "")),
|
||||||
|
config.WithRegion("auto"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load R2 config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
|
||||||
|
o.BaseEndpoint = aws.String(r2Endpoint)
|
||||||
|
})
|
||||||
|
|
||||||
|
return &R2Client{
|
||||||
|
s3Client: client,
|
||||||
|
bucketName: bucketName,
|
||||||
|
publicBaseURL: fmt.Sprintf("https://pub-%s.r2.dev", publicApiID),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadFile uploads a multipart file to R2 and returns the public URL.
|
||||||
|
// key is the object path inside the bucket, e.g. "reviews/18/image.png"
|
||||||
|
func (r *R2Client) UploadFile(ctx context.Context, key string, file multipart.File, contentType string) (string, error) {
|
||||||
|
_, err := r.s3Client.PutObject(ctx, &s3.PutObjectInput{
|
||||||
|
Bucket: aws.String(r.bucketName),
|
||||||
|
Key: aws.String(key),
|
||||||
|
Body: file,
|
||||||
|
ContentType: aws.String(contentType),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to upload to R2: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicURL := fmt.Sprintf("%s/%s", r.publicBaseURL, key)
|
||||||
|
return publicURL, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +17,11 @@ type Config struct {
|
|||||||
RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"`
|
RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"`
|
||||||
MeilisearchHost string `mapstructure:"MEILISEARCH_HOST"`
|
MeilisearchHost string `mapstructure:"MEILISEARCH_HOST"`
|
||||||
MeilisearchKey string `mapstructure:"MEILISEARCH_KEY"`
|
MeilisearchKey string `mapstructure:"MEILISEARCH_KEY"`
|
||||||
|
R2AccountID string `mapstructure:"R2_ACCOUNT_ID"`
|
||||||
|
R2AccessKeyID string `mapstructure:"R2_ACCESS_KEY_ID"`
|
||||||
|
R2SecretAccessKey string `mapstructure:"R2_SECRET_ACCESS_KEY"`
|
||||||
|
R2BucketName string `mapstructure:"R2_BUCKET_NAME"`
|
||||||
|
R2PublicApiID string `mapstructure:"R2_PUBLIC_API_ID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(path string) (config Config, err error) {
|
func LoadConfig(path string) (config Config, err error) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user