Compare commits
14 Commits
675abcdb18
...
2f05a2f8e7
Author | SHA1 | Date | |
---|---|---|---|
2f05a2f8e7 | |||
f8ecd20220 | |||
eb9774cce5 | |||
b31bc76a24 | |||
a2d4dfb282 | |||
8dbebd9f17 | |||
641831a3ae | |||
5a5d157882 | |||
110cbfeec0 | |||
47e692bf30 | |||
6cd35d7ffa | |||
ddca119e45 | |||
198e311407 | |||
1f055e92d4 |
10
TODO
10
TODO
@ -1,3 +1,9 @@
|
|||||||
- COMMENTS FLOW
|
|
||||||
- USER PROFILE FLOW
|
- USER PROFILE FLOW
|
||||||
- BEST LOCATIONS FILTER ( MIN REVIEWS AND TAGS)
|
- ADMIN FLOW (SUBMISSION, USER REPORTS)
|
||||||
|
- BEST LOCATIONS FILTER ( MIN REVIEWS AND TAGS)
|
||||||
|
- PAGINATION
|
||||||
|
- OPEN GRAPH PARSER
|
||||||
|
- USERS FEEED
|
||||||
|
|
||||||
|
## IF THERES TIME LEFT
|
||||||
|
- STORIES FLOW
|
6
api/api.go
Normal file
6
api/api.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
type BaseGetListRequest struct {
|
||||||
|
Page int32 `form:"page" binding:"required,min=1"`
|
||||||
|
PageSize int32 `form:"page_size" binding:"required,min=5"`
|
||||||
|
}
|
@ -11,7 +11,6 @@ import (
|
|||||||
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
||||||
"git.nochill.in/nochill/hiling_go/util"
|
"git.nochill.in/nochill/hiling_go/util"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/lib/pq"
|
|
||||||
ysqlc "github.com/yiplee/sqlc"
|
ysqlc "github.com/yiplee/sqlc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,64 +19,60 @@ type createLocationReq struct {
|
|||||||
Name string `form:"name" binding:"required"`
|
Name string `form:"name" binding:"required"`
|
||||||
SubmittedBy int32 `form:"submitted_by" binding:"required,number"`
|
SubmittedBy int32 `form:"submitted_by" binding:"required,number"`
|
||||||
RegencyID int16 `form:"regency_id" binding:"required,number"`
|
RegencyID int16 `form:"regency_id" binding:"required,number"`
|
||||||
|
LocationType string `form:"location_type" binding:"required"`
|
||||||
GoogleMapsLink string `form:"google_maps_link"`
|
GoogleMapsLink string `form:"google_maps_link"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) createLocation(ctx *gin.Context) {
|
func (server *Server) createLocation(ctx *gin.Context) {
|
||||||
var req createLocationReq
|
var req createLocationReq
|
||||||
var imgPath string
|
var imgPath string
|
||||||
|
var tempImg []db.CreateImageParams
|
||||||
var thumbnail, _ = ctx.FormFile("thumbnail")
|
|
||||||
|
|
||||||
if err := ctx.Bind(&req); err != nil {
|
if err := ctx.Bind(&req); err != nil {
|
||||||
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if thumbnail != nil {
|
form, _ := ctx.MultipartForm()
|
||||||
img := thumbnail
|
thumbnails := form.File["thumbnail"]
|
||||||
fileExt := filepath.Ext(img.Filename)
|
|
||||||
now := time.Now()
|
|
||||||
dir := fmt.Sprintf("public/upload/images/locations/%s/thumbnail", req.Name)
|
|
||||||
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
|
|
||||||
imgPath = fmt.Sprintf("%s%s", dir, osFilename)
|
|
||||||
|
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
if len(thumbnails) > 0 {
|
||||||
os.Mkdir(dir, 0775)
|
for _, img := range thumbnails {
|
||||||
}
|
fileExt := filepath.Ext(img.Filename)
|
||||||
|
now := time.Now()
|
||||||
|
dir := fmt.Sprintf("public/upload/images/locations/%s/thumbnail", req.Name)
|
||||||
|
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
|
||||||
|
imgPath = fmt.Sprintf("%s/%s", dir, osFilename)
|
||||||
|
|
||||||
if err := ctx.SaveUploadedFile(img, imgPath); err != nil {
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
|
os.Mkdir(dir, 0775)
|
||||||
return
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arg := db.CreateLocationParams{
|
if err := ctx.SaveUploadedFile(img, imgPath); err != nil {
|
||||||
Address: req.Address,
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
|
||||||
Name: req.Name,
|
return
|
||||||
SubmittedBy: req.SubmittedBy,
|
|
||||||
RegencyID: req.RegencyID,
|
|
||||||
GoogleMapsLink: sql.NullString{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := server.Store.CreateLocation(ctx, arg)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if pqErr, ok := err.(*pq.Error); ok {
|
|
||||||
switch pqErr.Code.Name() {
|
|
||||||
case "foreign_key_violation", "unique_violation":
|
|
||||||
if pqErr.Constraint == "locations_regency_id_fkey" {
|
|
||||||
ctx.JSON(http.StatusConflict, ErrorResponse(err, fmt.Sprintf("Failed to submit location, there's no regency with id: %d", arg.RegencyID)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if pqErr.Constraint == "submitted_by_fkey" {
|
|
||||||
ctx.JSON(http.StatusConflict, ErrorResponse(err, fmt.Sprintf("Failed to submit location, there's no user with id: %d", arg.SubmittedBy)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
|
|
||||||
return
|
arg := db.CreateLocationTxParams{
|
||||||
|
Address: req.Address,
|
||||||
|
Name: req.Name,
|
||||||
|
LocationType: db.LocationType(req.LocationType),
|
||||||
|
SubmittedBy: req.SubmittedBy,
|
||||||
|
RegencyID: req.RegencyID,
|
||||||
|
IsDeleted: false,
|
||||||
|
ApprovedBy: sql.NullInt32{Int32: 0, Valid: false},
|
||||||
|
GoogleMapsLink: sql.NullString{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink},
|
||||||
|
Thumbnail: tempImg,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := server.Store.CreateLocationTx(ctx, arg)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save Location"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Writer.WriteHeader(http.StatusOK)
|
ctx.Writer.WriteHeader(http.StatusOK)
|
||||||
|
71
api/news_event.go
Normal file
71
api/news_event.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateNewsEventsReq struct {
|
||||||
|
Url string `json:"url" binding:"required,url"`
|
||||||
|
Title string `json:"title" binding:"required"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
SubmittedBy int32 `json:"submitted_by" binding:"required,numeric"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) createNews(ctx *gin.Context) {
|
||||||
|
var req CreateNewsEventsReq
|
||||||
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := server.Store.CreateNewsEvents(ctx, db.CreateNewsEventsParams{
|
||||||
|
Title: req.Title,
|
||||||
|
Url: req.Url,
|
||||||
|
Source: req.Source,
|
||||||
|
Description: sql.NullString{Valid: len(req.Description) > 0, String: req.Description},
|
||||||
|
SubmittedBy: req.SubmittedBy,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save news/evnts"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Writer.WriteHeader(http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetNewsEventsListReq struct {
|
||||||
|
BaseGetListRequest
|
||||||
|
Approved int8 `form:"is_with_approval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) GetNewsEventsList(ctx *gin.Context) {
|
||||||
|
var req GetNewsEventsListReq
|
||||||
|
isWithApproval := ""
|
||||||
|
|
||||||
|
if err := ctx.ShouldBindQuery(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.Approved > 0 {
|
||||||
|
isWithApproval = "WHERE approved_by IS NOT NULL"
|
||||||
|
}
|
||||||
|
|
||||||
|
news, err := server.Store.GetNewsEventsList(ctx, db.GetNewsEventsListParams{
|
||||||
|
Limit: req.PageSize,
|
||||||
|
Offset: (req.Page - 1) * req.PageSize,
|
||||||
|
IsWithApproved: isWithApproval,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get news / events"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, news)
|
||||||
|
}
|
37
api/region.go
Normal file
37
api/region.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (server *Server) getListRegions(ctx *gin.Context) {
|
||||||
|
regions, err := server.Store.GetListRegions(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get regions"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, regions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) getListProvinces(ctx *gin.Context) {
|
||||||
|
provinces, err := server.Store.GetListProvinces(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get regions"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, provinces)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) getListRegencies(ctx *gin.Context) {
|
||||||
|
regencies, err := server.Store.GetListRegencies(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get regions"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, regencies)
|
||||||
|
}
|
@ -39,15 +39,16 @@ func (server *Server) getRoutes() {
|
|||||||
router.Use(cors.New(cors.Config{
|
router.Use(cors.New(cors.Config{
|
||||||
AllowOrigins: []string{"http://localhost:5173"},
|
AllowOrigins: []string{"http://localhost:5173"},
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With"},
|
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"},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// router.Use(CORSMiddleware())
|
|
||||||
|
|
||||||
router.POST("/user/signup", server.createUser)
|
router.POST("/user/signup", server.createUser)
|
||||||
router.POST("/user/login", server.login)
|
router.POST("/user/login", server.login)
|
||||||
router.POST("/user/logout", server.logout)
|
router.POST("/user/logout", server.logout)
|
||||||
|
router.GET("/regions", server.getListRegions)
|
||||||
|
router.GET("/region/provinces", server.getListProvinces)
|
||||||
|
router.GET("/region/regencies", server.getListRegencies)
|
||||||
|
|
||||||
// LOCATION
|
// LOCATION
|
||||||
router.POST("/locations", server.createLocation)
|
router.POST("/locations", server.createLocation)
|
||||||
@ -61,10 +62,18 @@ func (server *Server) getRoutes() {
|
|||||||
//IMAGES
|
//IMAGES
|
||||||
router.GET("/images/location", server.getAllImagesByLocation)
|
router.GET("/images/location", server.getAllImagesByLocation)
|
||||||
|
|
||||||
|
// NEWS / EVENTS
|
||||||
|
router.GET("/news-events", server.GetNewsEventsList)
|
||||||
|
|
||||||
// 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.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.PATCH("/user/avatar", server.updateUserAvatar)
|
||||||
|
authRoutes.DELETE("/user/avatar", server.removeAvatar)
|
||||||
|
authRoutes.POST("/news-events", server.createNews)
|
||||||
|
|
||||||
server.Router = router
|
server.Router = router
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ func randomLocation() db.Location {
|
|||||||
TotalVisited: sql.NullInt32{Valid: true, Int32: int32(util.RandomInt(0, 32))},
|
TotalVisited: sql.NullInt32{Valid: true, Int32: int32(util.RandomInt(0, 32))},
|
||||||
Thumbnail: sql.NullString{Valid: false, String: ""},
|
Thumbnail: sql.NullString{Valid: false, String: ""},
|
||||||
RegencyID: 1305,
|
RegencyID: 1305,
|
||||||
IsDeleted: sql.NullBool{Valid: true, Bool: false},
|
IsDeleted: false,
|
||||||
CreatedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
CreatedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||||
UpdatedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
UpdatedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||||
ApprovedBy: sql.NullInt32{Valid: true, Int32: 1},
|
ApprovedBy: sql.NullInt32{Valid: true, Int32: 1},
|
||||||
|
136
api/user.go
136
api/user.go
@ -2,13 +2,19 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
||||||
"git.nochill.in/nochill/hiling_go/util"
|
"git.nochill.in/nochill/hiling_go/util"
|
||||||
|
"git.nochill.in/nochill/hiling_go/util/token"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
"github.com/sqlc-dev/pqtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type createUserRequest struct {
|
type createUserRequest struct {
|
||||||
@ -124,6 +130,134 @@ func (server *Server) createUser(ctx *gin.Context) {
|
|||||||
ctx.JSON(http.StatusOK, res)
|
ctx.JSON(http.StatusOK, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) getUserStats(ctx *gin.Context) {
|
||||||
|
var scoreDistribution []map[string]int8
|
||||||
|
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||||
|
|
||||||
|
userStats, err := server.Store.GetUserStats(ctx, int32(authPayload.UserID))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get user stats"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(userStats.ScoresDistribution, &scoreDistribution)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whilet try to parse score distribution"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var userReviews []map[string]any
|
||||||
|
if userStats.Reviews != nil {
|
||||||
|
err = json.Unmarshal(userStats.Reviews, &userReviews)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something wrong while try to parse user reviwes stats"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userReviews = make([]map[string]any, 0)
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{
|
||||||
|
"reviews": userReviews,
|
||||||
|
"scores_distribution": scoreDistribution,
|
||||||
|
"user_stats": userStats,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateUserRequest struct {
|
||||||
|
About string `json:"about"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
SocialMedia []map[string]string `json:"social_media"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) updateUser(ctx *gin.Context) {
|
||||||
|
var req UpdateUserRequest
|
||||||
|
|
||||||
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||||
|
|
||||||
|
fmt.Println(req.About)
|
||||||
|
|
||||||
|
social, _ := json.Marshal(req.SocialMedia)
|
||||||
|
socialArr := pqtype.NullRawMessage{
|
||||||
|
RawMessage: social,
|
||||||
|
Valid: len(req.SocialMedia) > 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := server.Store.UpdateUser(ctx, db.UpdateUserParams{
|
||||||
|
About: sql.NullString{String: req.About, Valid: true},
|
||||||
|
SocialMedia: socialArr,
|
||||||
|
Website: sql.NullString{String: req.Website, Valid: len(req.Website) > 0},
|
||||||
|
ID: int32(authPayload.UserID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to update user"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) updateUserAvatar(ctx *gin.Context) {
|
||||||
|
file, err := ctx.FormFile("file")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to parse file"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||||
|
|
||||||
|
fileExt := filepath.Ext(file.Filename)
|
||||||
|
now := time.Now()
|
||||||
|
dir := fmt.Sprintf("public/upload/images/user/%d/avatar", authPayload.UserID)
|
||||||
|
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
|
||||||
|
imgPath := fmt.Sprintf("%s/%s", dir, osFilename)
|
||||||
|
|
||||||
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
|
os.Mkdir(dir, 0775)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.SaveUploadedFile(file, imgPath); err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url, err := server.Store.UpdateAvatar(ctx, db.UpdateAvatarParams{
|
||||||
|
ID: int32(authPayload.UserID),
|
||||||
|
AvatarPicture: sql.NullString{Valid: true, String: imgPath},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save image to database"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, gin.H{"image_url": url.String})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) removeAvatar(ctx *gin.Context) {
|
||||||
|
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||||
|
|
||||||
|
_, err := server.Store.UpdateAvatar(ctx, db.UpdateAvatarParams{
|
||||||
|
AvatarPicture: sql.NullString{String: "", Valid: false},
|
||||||
|
ID: int32(authPayload.UserID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to update user avatar"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Writer.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
func (server *Server) login(ctx *gin.Context) {
|
func (server *Server) login(ctx *gin.Context) {
|
||||||
var req createUserRequest
|
var req createUserRequest
|
||||||
|
|
||||||
@ -135,7 +269,7 @@ func (server *Server) login(ctx *gin.Context) {
|
|||||||
user, err := server.Store.GetUser(ctx, req.Username)
|
user, err := server.Store.GetUser(ctx, req.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
ctx.JSON(http.StatusNotFound, ErrorResponse(err, ""))
|
ctx.JSON(http.StatusNotFound, ErrorResponse(err, "User not found"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whlie try to get user"))
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whlie try to get user"))
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by
|
id,location_type,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by,is_deleted
|
||||||
1#Jalan Raya Beside the bridge Ubud#Murni’s Warung#1#https://cdn.discordapp.com/attachments/743422487882104837/1150972798320267304/image.png#5104#https://www.google.com/maps/place/Murni's+Warung/@-8.5048696,115.2553417,19z/data=!4m6!3m5!1s0x2dd23d3e0ffaa071:0xf2fa69b4cb211e41!8m2!3d-8.5051184!4d115.2547196!16s%2Fg%2F1tdsmcq7?entry=ttu#1
|
1#culinary#Jalan Raya Beside the bridge Ubud#Murni’s Warung#1#https://cdn.discordapp.com/attachments/743422487882104837/1150972798320267304/image.png#5104#https://www.google.com/maps/place/Murni's+Warung/@-8.5048696,115.2553417,19z/data=!4m6!3m5!1s0x2dd23d3e0ffaa071:0xf2fa69b4cb211e41!8m2!3d-8.5051184!4d115.2547196!16s%2Fg%2F1tdsmcq7?entry=ttu#1#false
|
||||||
2#Jl.Taman Wijaya Kusuma Ps. Baru Kecamatan Sawah Besar#Masjid Istiqlal#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/04/11/31/33/istiqlal-mosque-mesjid.jpg?w=500&h=-1&s=1#3173#https://www.google.com/maps/place/Masjid+Istiqlal/@-6.1703155,106.8308434,19z/data=!4m15!1m7!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!2sMasjid+Istiqlal!8m2!3d-6.17017!4d106.83139!16zL20vMDRzam1q!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!8m2!3d-6.17017!4d106.83139!15sCg9tYXNqaWQgaXN0aXFsYWxaESIPbWFzamlkIGlzdGlxbGFskgEGbW9zcXVl4AEA!16zL20vMDRzam1q?entry=ttu#1
|
2#other#Jl.Taman Wijaya Kusuma Ps. Baru Kecamatan Sawah Besar#Masjid Istiqlal#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/04/11/31/33/istiqlal-mosque-mesjid.jpg?w=500&h=-1&s=1#3173#https://www.google.com/maps/place/Masjid+Istiqlal/@-6.1703155,106.8308434,19z/data=!4m15!1m7!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!2sMasjid+Istiqlal!8m2!3d-6.17017!4d106.83139!16zL20vMDRzam1q!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!8m2!3d-6.17017!4d106.83139!15sCg9tYXNqaWQgaXN0aXFsYWxaESIPbWFzamlkIGlzdGlxbGFskgEGbW9zcXVl4AEA!16zL20vMDRzam1q?entry=ttu#1#false
|
||||||
3#Jl. Mayjend Sungkono no. 89#Hotel Ciputra World Surabaya#1#https://lh5.googleusercontent.com/p/AF1QipOvHDO-M6riRoqBrWU3MskhwL_bue8JmN9faq7Q=w500-h500-k-no#3578#https://www.google.com/maps/place/Ciputra+World+Hotel+Surabaya/@-7.2923061,112.7191552,15z/data=!4m2!3m1!1s0x0:0x736a9c49dcc2ac42?sa=X&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgVEAA&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgjEAc#1
|
3#other#Jl. Mayjend Sungkono no. 89#Hotel Ciputra World Surabaya#1#https://lh5.googleusercontent.com/p/AF1QipOvHDO-M6riRoqBrWU3MskhwL_bue8JmN9faq7Q=w500-h500-k-no#3578#https://www.google.com/maps/place/Ciputra+World+Hotel+Surabaya/@-7.2923061,112.7191552,15z/data=!4m2!3m1!1s0x0:0x736a9c49dcc2ac42?sa=X&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgVEAA&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgjEAc#1#false
|
||||||
4#Jl. Taman Safari No.101 . B Cibeureum Kec. Cisarua#Club Huis#1#https://media-cdn.tripadvisor.com/media/photo-o/0d/6a/5d/63/our-peaceful-backyard.jpg#3201#https://www.google.com/maps/place/Club+Huis/@-6.7027857,106.9453741,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69b679d7a09e01:0xf9fc2df396f09977!8m2!3d-6.7027857!4d106.947949!16s%2Fg%2F11c57lh8ky?entry=ttu#1
|
4#amusement park#Jl. Taman Safari No.101 . B Cibeureum Kec. Cisarua#Club Huis#1#https://media-cdn.tripadvisor.com/media/photo-o/0d/6a/5d/63/our-peaceful-backyard.jpg#3201#https://www.google.com/maps/place/Club+Huis/@-6.7027857,106.9453741,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69b679d7a09e01:0xf9fc2df396f09977!8m2!3d-6.7027857!4d106.947949!16s%2Fg%2F11c57lh8ky?entry=ttu#1#false
|
||||||
5#Desa Tambakrejo Kecamatan Sumbermanjing Wetan#Pulau Sempu#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/11/3b/06/a5/pulau-sempu.jpg?w=500&h=-1&s=1#3507#https://www.google.com/maps/place/Pulau+Sempu/@-8.446621,112.6746143,14z/data=!3m1!4b1!4m6!3m5!1s0x2dd60120edbc901f:0x8efd89687a308993!8m2!3d-8.4428564!4d112.6973355!16s%2Fm%2F0r8k540?entry=ttu#1
|
5#other#Desa Tambakrejo Kecamatan Sumbermanjing Wetan#Pulau Sempu#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/11/3b/06/a5/pulau-sempu.jpg?w=500&h=-1&s=1#3507#https://www.google.com/maps/place/Pulau+Sempu/@-8.446621,112.6746143,14z/data=!3m1!4b1!4m6!3m5!1s0x2dd60120edbc901f:0x8efd89687a308993!8m2!3d-8.4428564!4d112.6973355!16s%2Fm%2F0r8k540?entry=ttu#1#false
|
||||||
6#Jl. Bukit Golf I BSD Sektor VI Lengkong Karya Kec. Serpong Utara#Damai Indah Golf#1#https://lh3.googleusercontent.com/p/AF1QipN5Z-0J6vIfIO6gqPO0z5HDWlNKqp0t816XIJPS=s680-w500-h500#3674#https://www.google.com/maps/place/Damai+Indah+Golf+-+BSD+Course/@-6.2815644,106.6496566,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69fb152983d973:0x89e58e219f8b93ef!8m2!3d-6.2815644!4d106.6522315!16s%2Fg%2F11c54c9r94?entry=ttu#1
|
6#other#Jl. Bukit Golf I BSD Sektor VI Lengkong Karya Kec. Serpong Utara#Damai Indah Golf#1#https://lh3.googleusercontent.com/p/AF1QipN5Z-0J6vIfIO6gqPO0z5HDWlNKqp0t816XIJPS=s680-w500-h500#3674#https://www.google.com/maps/place/Damai+Indah+Golf+-+BSD+Course/@-6.2815644,106.6496566,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69fb152983d973:0x89e58e219f8b93ef!8m2!3d-6.2815644!4d106.6522315!16s%2Fg%2F11c54c9r94?entry=ttu#1#false
|
||||||
7#Jl. P. Mangkubumi No.72A Cokrodiningratan Kec. Jetis#Hotel Tentrem Yogyakarta#1#https://cdn.discordapp.com/attachments/743422487882104837/1150987888553623653/image.png#3471#https://www.google.com/maps?q=Hotel+Tentrem+Yogyakarta&source=lmns&entry=mc&bih=1115&biw=2124&hl=en-US&sa=X&ved=2ahUKEwjjl-HHiKSBAxUu5jgGHTU3BiwQ0pQJKAJ6BAgBEAY#1
|
7#other#Jl. P. Mangkubumi No.72A Cokrodiningratan Kec. Jetis#Hotel Tentrem Yogyakarta#1#https://cdn.discordapp.com/attachments/743422487882104837/1150987888553623653/image.png#3471#https://www.google.com/maps?q=Hotel+Tentrem+Yogyakarta&source=lmns&entry=mc&bih=1115&biw=2124&hl=en-US&sa=X&ved=2ahUKEwjjl-HHiKSBAxUu5jgGHTU3BiwQ0pQJKAJ6BAgBEAY#1#false
|
||||||
8#Moluo Kec.Kwandang#Pulau Saronde Gorontalo#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/0d/ec/58/21/saronde-island-a-place.jpg?w=700&h=-1&s=1#7505#https://www.google.com/maps/place/Pulau+Saronde+Gorontalo/@0.9263376,122.8613201,17z/data=!3m1!4b1!4m6!3m5!1s0x32795bf34dff4467:0xa8beb2a832ae8176!8m2!3d0.9263376!4d122.863895!16s%2Fg%2F11l241cc1d?hl=id&entry=ttu#1
|
8#other#Moluo Kec.Kwandang#Pulau Saronde Gorontalo#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/0d/ec/58/21/saronde-island-a-place.jpg?w=700&h=-1&s=1#7505#https://www.google.com/maps/place/Pulau+Saronde+Gorontalo/@0.9263376,122.8613201,17z/data=!3m1!4b1!4m6!3m5!1s0x32795bf34dff4467:0xa8beb2a832ae8176!8m2!3d0.9263376!4d122.863895!16s%2Fg%2F11l241cc1d?hl=id&entry=ttu#1#false
|
||||||
9#Dusun Katiet Desa Bosua Kecamatan Sipora#Pantai Katiet#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/d7/fe/f4/mentawai-islands.jpg?w=500&h=-1&s=1#1301#https://www.google.com/maps/place/Katiet,+Bosua,+Sipora+Selatan,+Mentawai+Islands+Regency,+West+Sumatra/@-2.375793,99.848187,15z/data=!3m1!4b1!4m6!3m5!1s0x2fd27efa8363912f:0x8c9c19bd76cba179!8m2!3d-2.375793!4d99.848187!16s%2Fg%2F1tcwz0mt?hl=en-US&entry=ttu#1
|
9#beach#Dusun Katiet Desa Bosua Kecamatan Sipora#Pantai Katiet#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/d7/fe/f4/mentawai-islands.jpg?w=500&h=-1&s=1#1301#https://www.google.com/maps/place/Katiet,+Bosua,+Sipora+Selatan,+Mentawai+Islands+Regency,+West+Sumatra/@-2.375793,99.848187,15z/data=!3m1!4b1!4m6!3m5!1s0x2fd27efa8363912f:0x8c9c19bd76cba179!8m2!3d-2.375793!4d99.848187!16s%2Fg%2F1tcwz0mt?hl=en-US&entry=ttu#1#false
|
||||||
10#Pulau Padar#Pulau Padar#2#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/18/4e/11/f3/padar-island.jpg?w=800&h=-1&s=1#5315#https://www.google.com/maps/place/Pulau+Padar/@-8.6554183,119.570686,14.83z/data=!4m6!3m5!1s0x2db4f84ff6cd01ab:0xf7e6fd33b692a898!8m2!3d-8.6489909!4d119.5832593!16s%2Fg%2F121d16wd?entry=ttu#1
|
10#other#Pulau Padar#Pulau Padar#2#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/18/4e/11/f3/padar-island.jpg?w=800&h=-1&s=1#5315#https://www.google.com/maps/place/Pulau+Padar/@-8.6554183,119.570686,14.83z/data=!4m6!3m5!1s0x2db4f84ff6cd01ab:0xf7e6fd33b692a898!8m2!3d-8.6489909!4d119.5832593!16s%2Fg%2F121d16wd?entry=ttu#1#false
|
||||||
11#Jl. Ahmad Yani 18, Kel. Kelimutu, Kec. Ende Tengah#Sari rasa#3#https://cdn.discordapp.com/attachments/743422487882104837/1151903310295601172/image.png#5315#https://www.google.com/maps/place/Rumah+Makan+Cha+Cha/@-8.6153697,120.4652533,21z/data=!4m6!3m5!1s0x2db37469e54c0dd3:0x36ac988c726ed544!8m2!3d-8.6153631!4d120.465275!16s%2Fg%2F11bwpclp91?entry=ttu#1
|
11#culinary#Jl. Ahmad Yani 18, Kel. Kelimutu, Kec. Ende Tengah#Sari rasa#3#https://cdn.discordapp.com/attachments/743422487882104837/1151903310295601172/image.png#5315#https://www.google.com/maps/place/Rumah+Makan+Cha+Cha/@-8.6153697,120.4652533,21z/data=!4m6!3m5!1s0x2db37469e54c0dd3:0x36ac988c726ed544!8m2!3d-8.6153631!4d120.465275!16s%2Fg%2F11bwpclp91?entry=ttu#1#false
|
||||||
12#Danau Sentani#Danau Sentani#3#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/c7/e8/17/20190630-143008-largejpg.jpg?w=800&h=-1&s=1#9403#https://www.google.com/maps/place/Danau+Sentani/@-2.639976,140.3889748,12.46z/data=!4m7!3m6!1s0x686cf33c72660fbf:0x7e5c1e7d20d930d7!4b1!8m2!3d-2.6133004!4d140.518734!16s%2Fm%2F0tkjf6v?entry=ttu#1
|
12#other#Danau Sentani#Danau Sentani#3#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/c7/e8/17/20190630-143008-largejpg.jpg?w=800&h=-1&s=1#9403#https://www.google.com/maps/place/Danau+Sentani/@-2.639976,140.3889748,12.46z/data=!4m7!3m6!1s0x686cf33c72660fbf:0x7e5c1e7d20d930d7!4b1!8m2!3d-2.6133004!4d140.518734!16s%2Fm%2F0tkjf6v?entry=ttu#1#false
|
|
@ -1,4 +1,4 @@
|
|||||||
id,username,password
|
id,username,password
|
||||||
1,user123,password
|
1,user123,$2a$10$5jbejbMXCs7noqPBt1v9K.TCjnqDp.GtTkMiRRRwU8/t78GiGjY9a
|
||||||
2,critics,password
|
2,critics,$2a$10$5jbejbMXCs7noqPBt1v9K.TCjnqDp.GtTkMiRRRwU8/t78GiGjY9a
|
||||||
3,plebs,password
|
3,plebs,$2a$10$5jbejbMXCs7noqPBt1v9K.TCjnqDp.GtTkMiRRRwU8/t78GiGjY9a
|
|
@ -12,4 +12,5 @@ DROP TABLE IF EXISTS users;
|
|||||||
|
|
||||||
|
|
||||||
DROP TYPE IF EXISTS user_reports_type;
|
DROP TYPE IF EXISTS user_reports_type;
|
||||||
DROP TYPE IF EXISTS comment_type;
|
DROP TYPE IF EXISTS comment_type;
|
||||||
|
DROP TYPE IF EXISTS location_type;
|
@ -53,14 +53,14 @@ CREATE TABLE user_reports(
|
|||||||
|
|
||||||
CREATE TABLE regions(
|
CREATE TABLE regions(
|
||||||
"id" serial primary key not null,
|
"id" serial primary key not null,
|
||||||
"region_name" varchar,
|
"region_name" varchar not null,
|
||||||
"created_at" timestamp default(now()),
|
"created_at" timestamp default(now()),
|
||||||
"updated_at" timestamp default(now())
|
"updated_at" timestamp default(now())
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE provinces(
|
CREATE TABLE provinces(
|
||||||
"id" serial primary key not null,
|
"id" serial primary key not null,
|
||||||
"province_name" varchar,
|
"province_name" varchar not null,
|
||||||
"region_id" smallint references "regions"("id") not null,
|
"region_id" smallint references "regions"("id") not null,
|
||||||
"created_at" timestamp default(now()),
|
"created_at" timestamp default(now()),
|
||||||
"updated_at" timestamp default(now())
|
"updated_at" timestamp default(now())
|
||||||
@ -68,22 +68,31 @@ CREATE TABLE provinces(
|
|||||||
|
|
||||||
CREATE TABLE regencies(
|
CREATE TABLE regencies(
|
||||||
"id" serial primary key not null,
|
"id" serial primary key not null,
|
||||||
"regency_name" varchar,
|
"regency_name" varchar not null,
|
||||||
"province_id" smallint references "provinces"("id") not null,
|
"province_id" smallint references "provinces"("id") not null,
|
||||||
"created_at" timestamp default(now()),
|
"created_at" timestamp default(now()),
|
||||||
"updated_at" timestamp default(now())
|
"updated_at" timestamp default(now())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TYPE location_type AS ENUM(
|
||||||
|
'beach',
|
||||||
|
'amusement park',
|
||||||
|
'culinary',
|
||||||
|
'hiking / camping',
|
||||||
|
'other'
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE locations(
|
CREATE TABLE locations(
|
||||||
"id" serial primary key not null,
|
"id" serial primary key not null,
|
||||||
"address" varchar not null,
|
"address" varchar not null,
|
||||||
"name" varchar not null,
|
"name" varchar not null,
|
||||||
"google_maps_link" varchar,
|
"google_maps_link" varchar,
|
||||||
|
"location_type" location_type not null,
|
||||||
"submitted_by" integer references "users"("id") not null,
|
"submitted_by" integer references "users"("id") not null,
|
||||||
"total_visited" integer,
|
"total_visited" integer,
|
||||||
"thumbnail" varchar,
|
"thumbnail" varchar,
|
||||||
"regency_id" smallint references "regencies"("id") not null,
|
"regency_id" smallint references "regencies"("id") not null,
|
||||||
"is_deleted" boolean,
|
"is_deleted" boolean not null,
|
||||||
"created_at" timestamp default(now()),
|
"created_at" timestamp default(now()),
|
||||||
"updated_at" timestamp default(now())
|
"updated_at" timestamp default(now())
|
||||||
);
|
);
|
||||||
@ -112,6 +121,7 @@ CREATE TABLE reviews (
|
|||||||
"comments" text not null,
|
"comments" text not null,
|
||||||
"score" smallint not null,
|
"score" smallint not null,
|
||||||
"is_from_critic" boolean not null,
|
"is_from_critic" boolean not null,
|
||||||
|
"cost_approx" integer,
|
||||||
"is_hided" boolean not null default(false), -- if comments violate TOS just hide the reviews
|
"is_hided" boolean not null default(false), -- if comments violate TOS just hide the reviews
|
||||||
"location_id" integer references "locations"("id") not null,
|
"location_id" integer references "locations"("id") not null,
|
||||||
"created_at" timestamp default(now()),
|
"created_at" timestamp default(now()),
|
||||||
|
1
db/migrations/000007_create_user_follow_table.down.sql
Normal file
1
db/migrations/000007_create_user_follow_table.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS user_follow;
|
7
db/migrations/000007_create_user_follow_table.up.sql
Normal file
7
db/migrations/000007_create_user_follow_table.up.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE user_follow(
|
||||||
|
id serial primary key not null,
|
||||||
|
follower_id integer references "users"("id") not null,
|
||||||
|
followee_id integer references "users"("id") not null,
|
||||||
|
created_at timestamp default (now()) not null,
|
||||||
|
updated_at timestamp default (now()) not null
|
||||||
|
)
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE users DROP COLUMN about;
|
1
db/migrations/000008_alter_users_add_about_column.up.sql
Normal file
1
db/migrations/000008_alter_users_add_about_column.up.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE users ADD COLUMN about text;
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE users DROP COLUMN IF EXISTS website;
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE users ADD COLUMN website varchar;
|
1
db/migrations/000010_create_news_event_table.down.sql
Normal file
1
db/migrations/000010_create_news_event_table.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE IF EXISTS news_events;
|
13
db/migrations/000010_create_news_event_table.up.sql
Normal file
13
db/migrations/000010_create_news_event_table.up.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE news_events (
|
||||||
|
"id" serial primary key not null,
|
||||||
|
"title" varchar not null,
|
||||||
|
"url" varchar not null,
|
||||||
|
"source" varchar not null,
|
||||||
|
"thumbnail" varchar,
|
||||||
|
"description" text,
|
||||||
|
"is_deleted" boolean not null default(false),
|
||||||
|
"submitted_by" int references "users"("id") not null,
|
||||||
|
"approved_by" int references "users"("id"),
|
||||||
|
"created_at" timestamp default(now()),
|
||||||
|
"updated_at" timestamp default(now())
|
||||||
|
)
|
186
db/mock/store.go
186
db/mock/store.go
@ -6,6 +6,7 @@ package mockdb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
context "context"
|
||||||
|
sql "database/sql"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
||||||
@ -35,6 +36,20 @@ func (m *MockStore) EXPECT() *MockStoreMockRecorder {
|
|||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddFollowUser mocks base method.
|
||||||
|
func (m *MockStore) AddFollowUser(arg0 context.Context, arg1 db.AddFollowUserParams) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AddFollowUser", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFollowUser indicates an expected call of AddFollowUser.
|
||||||
|
func (mr *MockStoreMockRecorder) AddFollowUser(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFollowUser", reflect.TypeOf((*MockStore)(nil).AddFollowUser), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// CheckIfReviewExists mocks base method.
|
// CheckIfReviewExists mocks base method.
|
||||||
func (m *MockStore) CheckIfReviewExists(arg0 context.Context, arg1 db.CheckIfReviewExistsParams) (int64, error) {
|
func (m *MockStore) CheckIfReviewExists(arg0 context.Context, arg1 db.CheckIfReviewExistsParams) (int64, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -50,20 +65,63 @@ func (mr *MockStoreMockRecorder) CheckIfReviewExists(arg0, arg1 interface{}) *go
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfReviewExists", reflect.TypeOf((*MockStore)(nil).CheckIfReviewExists), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfReviewExists", reflect.TypeOf((*MockStore)(nil).CheckIfReviewExists), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateLocation mocks base method.
|
// CreateImage mocks base method.
|
||||||
func (m *MockStore) CreateLocation(arg0 context.Context, arg1 db.CreateLocationParams) error {
|
func (m *MockStore) CreateImage(arg0 context.Context, arg1 []db.CreateImageParams) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "CreateLocation", arg0, arg1)
|
ret := m.ctrl.Call(m, "CreateImage", arg0, arg1)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateImage indicates an expected call of CreateImage.
|
||||||
|
func (mr *MockStoreMockRecorder) CreateImage(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateImage", reflect.TypeOf((*MockStore)(nil).CreateImage), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLocation mocks base method.
|
||||||
|
func (m *MockStore) CreateLocation(arg0 context.Context, arg1 db.CreateLocationParams) (int32, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateLocation", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(int32)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
// CreateLocation indicates an expected call of CreateLocation.
|
// CreateLocation indicates an expected call of CreateLocation.
|
||||||
func (mr *MockStoreMockRecorder) CreateLocation(arg0, arg1 interface{}) *gomock.Call {
|
func (mr *MockStoreMockRecorder) CreateLocation(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocation", reflect.TypeOf((*MockStore)(nil).CreateLocation), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocation", reflect.TypeOf((*MockStore)(nil).CreateLocation), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateLocationTx mocks base method.
|
||||||
|
func (m *MockStore) CreateLocationTx(arg0 context.Context, arg1 db.CreateLocationTxParams) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateLocationTx", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLocationTx indicates an expected call of CreateLocationTx.
|
||||||
|
func (mr *MockStoreMockRecorder) CreateLocationTx(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocationTx", reflect.TypeOf((*MockStore)(nil).CreateLocationTx), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNewsEvents mocks base method.
|
||||||
|
func (m *MockStore) CreateNewsEvents(arg0 context.Context, arg1 db.CreateNewsEventsParams) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateNewsEvents", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNewsEvents indicates an expected call of CreateNewsEvents.
|
||||||
|
func (mr *MockStoreMockRecorder) CreateNewsEvents(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewsEvents", reflect.TypeOf((*MockStore)(nil).CreateNewsEvents), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateReview mocks base method.
|
// CreateReview mocks base method.
|
||||||
func (m *MockStore) CreateReview(arg0 context.Context, arg1 db.CreateReviewParams) (db.Review, error) {
|
func (m *MockStore) CreateReview(arg0 context.Context, arg1 db.CreateReviewParams) (db.Review, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -169,6 +227,21 @@ func (mr *MockStoreMockRecorder) GetListLocations(arg0 interface{}) *gomock.Call
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocations", reflect.TypeOf((*MockStore)(nil).GetListLocations), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocations", reflect.TypeOf((*MockStore)(nil).GetListLocations), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetListProvinces mocks base method.
|
||||||
|
func (m *MockStore) GetListProvinces(arg0 context.Context) ([]db.GetListProvincesRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetListProvinces", arg0)
|
||||||
|
ret0, _ := ret[0].([]db.GetListProvincesRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetListProvinces indicates an expected call of GetListProvinces.
|
||||||
|
func (mr *MockStoreMockRecorder) GetListProvinces(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListProvinces", reflect.TypeOf((*MockStore)(nil).GetListProvinces), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
// GetListRecentLocationsWithRatings mocks base method.
|
// GetListRecentLocationsWithRatings mocks base method.
|
||||||
func (m *MockStore) GetListRecentLocationsWithRatings(arg0 context.Context, arg1 int32) ([]db.GetListRecentLocationsWithRatingsRow, error) {
|
func (m *MockStore) GetListRecentLocationsWithRatings(arg0 context.Context, arg1 int32) ([]db.GetListRecentLocationsWithRatingsRow, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -184,6 +257,36 @@ func (mr *MockStoreMockRecorder) GetListRecentLocationsWithRatings(arg0, arg1 in
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRecentLocationsWithRatings", reflect.TypeOf((*MockStore)(nil).GetListRecentLocationsWithRatings), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRecentLocationsWithRatings", reflect.TypeOf((*MockStore)(nil).GetListRecentLocationsWithRatings), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetListRegencies mocks base method.
|
||||||
|
func (m *MockStore) GetListRegencies(arg0 context.Context) ([]db.GetListRegenciesRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetListRegencies", arg0)
|
||||||
|
ret0, _ := ret[0].([]db.GetListRegenciesRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetListRegencies indicates an expected call of GetListRegencies.
|
||||||
|
func (mr *MockStoreMockRecorder) GetListRegencies(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegencies", reflect.TypeOf((*MockStore)(nil).GetListRegencies), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetListRegions mocks base method.
|
||||||
|
func (m *MockStore) GetListRegions(arg0 context.Context) ([]db.GetListRegionsRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetListRegions", arg0)
|
||||||
|
ret0, _ := ret[0].([]db.GetListRegionsRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetListRegions indicates an expected call of GetListRegions.
|
||||||
|
func (mr *MockStoreMockRecorder) GetListRegions(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegions", reflect.TypeOf((*MockStore)(nil).GetListRegions), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
// GetLocation mocks base method.
|
// GetLocation mocks base method.
|
||||||
func (m *MockStore) GetLocation(arg0 context.Context, arg1 int32) (db.GetLocationRow, error) {
|
func (m *MockStore) GetLocation(arg0 context.Context, arg1 int32) (db.GetLocationRow, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -214,6 +317,21 @@ func (mr *MockStoreMockRecorder) GetLocationTag(arg0, arg1 interface{}) *gomock.
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocationTag", reflect.TypeOf((*MockStore)(nil).GetLocationTag), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocationTag", reflect.TypeOf((*MockStore)(nil).GetLocationTag), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetNewsEventsList mocks base method.
|
||||||
|
func (m *MockStore) GetNewsEventsList(arg0 context.Context, arg1 db.GetNewsEventsListParams) ([]db.NewsEventRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetNewsEventsList", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]db.NewsEventRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNewsEventsList indicates an expected call of GetNewsEventsList.
|
||||||
|
func (mr *MockStoreMockRecorder) GetNewsEventsList(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNewsEventsList", reflect.TypeOf((*MockStore)(nil).GetNewsEventsList), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// GetSession mocks base method.
|
// GetSession mocks base method.
|
||||||
func (m *MockStore) GetSession(arg0 context.Context, arg1 int32) (db.UserSession, error) {
|
func (m *MockStore) GetSession(arg0 context.Context, arg1 int32) (db.UserSession, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -274,6 +392,64 @@ func (mr *MockStoreMockRecorder) GetUserReviewByLocation(arg0, arg1 interface{})
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserReviewByLocation", reflect.TypeOf((*MockStore)(nil).GetUserReviewByLocation), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserReviewByLocation", reflect.TypeOf((*MockStore)(nil).GetUserReviewByLocation), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserStats mocks base method.
|
||||||
|
func (m *MockStore) GetUserStats(arg0 context.Context, arg1 int32) (db.GetUserStatsRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetUserStats", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(db.GetUserStatsRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserStats indicates an expected call of GetUserStats.
|
||||||
|
func (mr *MockStoreMockRecorder) GetUserStats(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStats", reflect.TypeOf((*MockStore)(nil).GetUserStats), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFollowUser mocks base method.
|
||||||
|
func (m *MockStore) RemoveFollowUser(arg0 context.Context, arg1 db.RemoveFollowUserParams) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "RemoveFollowUser", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFollowUser indicates an expected call of RemoveFollowUser.
|
||||||
|
func (mr *MockStoreMockRecorder) RemoveFollowUser(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFollowUser", reflect.TypeOf((*MockStore)(nil).RemoveFollowUser), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAvatar mocks base method.
|
||||||
|
func (m *MockStore) UpdateAvatar(arg0 context.Context, arg1 db.UpdateAvatarParams) (sql.NullString, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "UpdateAvatar", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(sql.NullString)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAvatar indicates an expected call of UpdateAvatar.
|
||||||
|
func (mr *MockStoreMockRecorder) UpdateAvatar(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAvatar", reflect.TypeOf((*MockStore)(nil).UpdateAvatar), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLocationThumbnail mocks base method.
|
||||||
|
func (m *MockStore) UpdateLocationThumbnail(arg0 context.Context, arg1 db.UpdateLocationThumbnailParams) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "UpdateLocationThumbnail", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLocationThumbnail indicates an expected call of UpdateLocationThumbnail.
|
||||||
|
func (mr *MockStoreMockRecorder) UpdateLocationThumbnail(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLocationThumbnail", reflect.TypeOf((*MockStore)(nil).UpdateLocationThumbnail), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdatePassword mocks base method.
|
// UpdatePassword mocks base method.
|
||||||
func (m *MockStore) UpdatePassword(arg0 context.Context, arg1 db.UpdatePasswordParams) error {
|
func (m *MockStore) UpdatePassword(arg0 context.Context, arg1 db.UpdatePasswordParams) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -289,10 +465,10 @@ func (mr *MockStoreMockRecorder) UpdatePassword(arg0, arg1 interface{}) *gomock.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser mocks base method.
|
// UpdateUser mocks base method.
|
||||||
func (m *MockStore) UpdateUser(arg0 context.Context, arg1 db.UpdateUserParams) (db.User, error) {
|
func (m *MockStore) UpdateUser(arg0 context.Context, arg1 db.UpdateUserParams) (db.UpdateUserRow, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "UpdateUser", arg0, arg1)
|
ret := m.ctrl.Call(m, "UpdateUser", arg0, arg1)
|
||||||
ret0, _ := ret[0].(db.User)
|
ret0, _ := ret[0].(db.UpdateUserRow)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
10
db/queries/follow.sql
Normal file
10
db/queries/follow.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
-- name: AddFollowUser :exec
|
||||||
|
INSERT INTO user_follow (
|
||||||
|
follower_id,
|
||||||
|
followee_id
|
||||||
|
) VALUES($1, $2);
|
||||||
|
|
||||||
|
-- name: RemoveFollowUser :exec
|
||||||
|
DELETE FROM
|
||||||
|
user_follow
|
||||||
|
WHERE follower_id = $1 AND followee_id = $2;
|
@ -19,17 +19,6 @@ WHERE approved_by IS NOT NULL
|
|||||||
ORDER BY l.created_at ASC
|
ORDER BY l.created_at ASC
|
||||||
LIMIT $1;
|
LIMIT $1;
|
||||||
|
|
||||||
-- name: CreateLocation :exec
|
|
||||||
INSERT INTO locations(
|
|
||||||
address,
|
|
||||||
name,
|
|
||||||
submitted_by,
|
|
||||||
regency_id,
|
|
||||||
google_maps_link
|
|
||||||
) values (
|
|
||||||
$1, $2, $3, $4, $5
|
|
||||||
);
|
|
||||||
|
|
||||||
-- name: GetLocationTag :many
|
-- name: GetLocationTag :many
|
||||||
SELECT
|
SELECT
|
||||||
name
|
name
|
||||||
@ -38,4 +27,10 @@ WHERE tags_type = 'location'
|
|||||||
AND
|
AND
|
||||||
target_id = $1
|
target_id = $1
|
||||||
AND
|
AND
|
||||||
approved_by IS NOT NULL;
|
approved_by IS NOT NULL;
|
||||||
|
|
||||||
|
|
||||||
|
-- name: UpdateLocationThumbnail :exec
|
||||||
|
UPDATE locations
|
||||||
|
SET thumbnail = $1
|
||||||
|
WHERE id = $2;
|
8
db/queries/news_events.sql
Normal file
8
db/queries/news_events.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-- name: CreateNewsEvents :exec
|
||||||
|
INSERT INTO news_events(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
source,
|
||||||
|
description,
|
||||||
|
submitted_by
|
||||||
|
) VALUES ( $1, $2, $3, $4, $5);
|
6
db/queries/provinces.sql
Normal file
6
db/queries/provinces.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- name: GetListProvinces :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
province_name,
|
||||||
|
region_id
|
||||||
|
FROM PROVINCES;
|
6
db/queries/regencies.sql
Normal file
6
db/queries/regencies.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- name: GetListRegencies :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
regency_name,
|
||||||
|
province_id
|
||||||
|
FROM regencies;
|
5
db/queries/regions.sql
Normal file
5
db/queries/regions.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-- name: GetListRegions :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
region_name
|
||||||
|
FROM regions;
|
@ -5,17 +5,13 @@ INSERT INTO users (
|
|||||||
) VALUES ($1, $2)
|
) VALUES ($1, $2)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
-- name: UpdateUser :one
|
|
||||||
UPDATE users
|
|
||||||
SET
|
|
||||||
email = COALESCE(sqlc.narg(email), email),
|
|
||||||
username = COALESCE(sqlc.narg(username), username),
|
|
||||||
avatar_picture = COALESCE(sqlc.narg(avatar_picture), avatar_picture)
|
|
||||||
WHERE
|
|
||||||
id = sqlc.arg(id)
|
|
||||||
RETURNING *;
|
|
||||||
|
|
||||||
-- name: UpdatePassword :exec
|
-- name: UpdatePassword :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET password = $1
|
SET password = $1
|
||||||
WHERE id = $2;
|
WHERE id = $2;
|
||||||
|
|
||||||
|
-- name: UpdateAvatar :one
|
||||||
|
UPDATE users
|
||||||
|
SET avatar_picture = $1
|
||||||
|
WHERE id = $2
|
||||||
|
RETURNING avatar_picture;
|
||||||
|
43
db/sqlc/follow.sql.go
Normal file
43
db/sqlc/follow.sql.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.20.0
|
||||||
|
// source: follow.sql
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const addFollowUser = `-- name: AddFollowUser :exec
|
||||||
|
INSERT INTO user_follow (
|
||||||
|
follower_id,
|
||||||
|
followee_id
|
||||||
|
) VALUES($1, $2)
|
||||||
|
`
|
||||||
|
|
||||||
|
type AddFollowUserParams struct {
|
||||||
|
FollowerID int32 `json:"follower_id"`
|
||||||
|
FolloweeID int32 `json:"followee_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) AddFollowUser(ctx context.Context, arg AddFollowUserParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, addFollowUser, arg.FollowerID, arg.FolloweeID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeFollowUser = `-- name: RemoveFollowUser :exec
|
||||||
|
DELETE FROM
|
||||||
|
user_follow
|
||||||
|
WHERE follower_id = $1 AND followee_id = $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type RemoveFollowUserParams struct {
|
||||||
|
FollowerID int32 `json:"follower_id"`
|
||||||
|
FolloweeID int32 `json:"followee_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) RemoveFollowUser(ctx context.Context, arg RemoveFollowUserParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, removeFollowUser, arg.FollowerID, arg.FolloweeID)
|
||||||
|
return err
|
||||||
|
}
|
@ -2,7 +2,10 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.nochill.in/nochill/hiling_go/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GetImagesByLocationParams struct {
|
type GetImagesByLocationParams struct {
|
||||||
@ -59,3 +62,38 @@ func (q *Queries) GetImagesByLocation(ctx context.Context, arg GetImagesByLocati
|
|||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateImageParams struct {
|
||||||
|
ImageUrl string `json:"url"`
|
||||||
|
UploadedBy int32 `json:"uploaded_by"`
|
||||||
|
ImageType string `json:"image_type"`
|
||||||
|
ImageOf int32 `json:"image_of"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateImage(ctx context.Context, arg []CreateImageParams) error {
|
||||||
|
|
||||||
|
values := []interface{}{}
|
||||||
|
|
||||||
|
queryStr := ` INSERT INTO
|
||||||
|
images(image_url, uploaded_by, image_type, image_of)
|
||||||
|
VALUES
|
||||||
|
`
|
||||||
|
|
||||||
|
for _, row := range arg {
|
||||||
|
queryStr += "(?, ?, ?, ?),"
|
||||||
|
values = append(values, row.ImageUrl, row.UploadedBy, row.ImageType, row.ImageOf)
|
||||||
|
}
|
||||||
|
|
||||||
|
queryStr = strings.TrimSuffix(queryStr, ",")
|
||||||
|
|
||||||
|
// Replacing ? with $n for postgres
|
||||||
|
queryStr = util.ReplaceSQL(queryStr, "?")
|
||||||
|
|
||||||
|
// prepare the statement
|
||||||
|
stmt, _ := q.db.PrepareContext(ctx, queryStr)
|
||||||
|
|
||||||
|
// format all vals at once
|
||||||
|
_, err := stmt.ExecContext(ctx, values...)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -46,9 +46,9 @@ func (q *Queries) GetTopListLocations(ctx context.Context, arg GetTopListLocatio
|
|||||||
FROM(
|
FROM(
|
||||||
SELECT
|
SELECT
|
||||||
*,
|
*,
|
||||||
(SELECT 5 * 4 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) as critic_bayes,
|
(SELECT 5 * 5 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) as critic_bayes,
|
||||||
(SELECT 50 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) as user_bayes,
|
(SELECT 5 * 5 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 5 + COALESCE(user_count, 0)) as user_bayes,
|
||||||
((SELECT 50 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) + (SELECT 5 * 4 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) ) / 2 as avg_bayes
|
((SELECT 5 * 5 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) + (SELECT 5 * 5 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) ) / 2 as avg_bayes
|
||||||
|
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
@ -171,3 +171,53 @@ func (q *Queries) GetLocation(ctx context.Context, location_id int32) (GetLocati
|
|||||||
|
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createLocation = `-- name: CreateLocation :exec
|
||||||
|
INSERT INTO locations(
|
||||||
|
address,
|
||||||
|
name,
|
||||||
|
submitted_by,
|
||||||
|
location_type,
|
||||||
|
regency_id,
|
||||||
|
google_maps_link,
|
||||||
|
approved_by,
|
||||||
|
is_deleted
|
||||||
|
) values (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8
|
||||||
|
)
|
||||||
|
RETURNING id
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateLocationParams struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SubmittedBy int32 `json:"submitted_by"`
|
||||||
|
LocationType LocationType `json:"location_type"`
|
||||||
|
RegencyID int16 `json:"regency_id"`
|
||||||
|
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
||||||
|
IsDeleted bool `json:"is_deleted"`
|
||||||
|
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createLocation,
|
||||||
|
arg.Address,
|
||||||
|
arg.Name,
|
||||||
|
arg.SubmittedBy,
|
||||||
|
arg.LocationType,
|
||||||
|
arg.RegencyID,
|
||||||
|
arg.GoogleMapsLink,
|
||||||
|
arg.ApprovedBy,
|
||||||
|
arg.IsDeleted,
|
||||||
|
)
|
||||||
|
|
||||||
|
var i int32
|
||||||
|
|
||||||
|
err := row.Scan(
|
||||||
|
&i,
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(i)
|
||||||
|
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
@ -10,39 +10,8 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
)
|
)
|
||||||
|
|
||||||
const createLocation = `-- name: CreateLocation :exec
|
|
||||||
INSERT INTO locations(
|
|
||||||
address,
|
|
||||||
name,
|
|
||||||
submitted_by,
|
|
||||||
regency_id,
|
|
||||||
google_maps_link
|
|
||||||
) values (
|
|
||||||
$1, $2, $3, $4, $5
|
|
||||||
)
|
|
||||||
`
|
|
||||||
|
|
||||||
type CreateLocationParams struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
SubmittedBy int32 `json:"submitted_by"`
|
|
||||||
RegencyID int16 `json:"regency_id"`
|
|
||||||
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) error {
|
|
||||||
_, err := q.db.ExecContext(ctx, createLocation,
|
|
||||||
arg.Address,
|
|
||||||
arg.Name,
|
|
||||||
arg.SubmittedBy,
|
|
||||||
arg.RegencyID,
|
|
||||||
arg.GoogleMapsLink,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const getListLocations = `-- name: GetListLocations :many
|
const getListLocations = `-- name: GetListLocations :many
|
||||||
SELECT id, address, name, google_maps_link, submitted_by, total_visited, thumbnail, regency_id, is_deleted, created_at, updated_at, approved_by, approved_at FROM locations
|
SELECT id, address, name, google_maps_link, location_type, submitted_by, total_visited, thumbnail, regency_id, is_deleted, created_at, updated_at, approved_by, approved_at FROM locations
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetListLocations(ctx context.Context) ([]Location, error) {
|
func (q *Queries) GetListLocations(ctx context.Context) ([]Location, error) {
|
||||||
@ -59,6 +28,7 @@ func (q *Queries) GetListLocations(ctx context.Context) ([]Location, error) {
|
|||||||
&i.Address,
|
&i.Address,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.GoogleMapsLink,
|
&i.GoogleMapsLink,
|
||||||
|
&i.LocationType,
|
||||||
&i.SubmittedBy,
|
&i.SubmittedBy,
|
||||||
&i.TotalVisited,
|
&i.TotalVisited,
|
||||||
&i.Thumbnail,
|
&i.Thumbnail,
|
||||||
@ -179,3 +149,19 @@ func (q *Queries) GetLocationTag(ctx context.Context, targetID int32) ([]string,
|
|||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateLocationThumbnail = `-- name: UpdateLocationThumbnail :exec
|
||||||
|
UPDATE locations
|
||||||
|
SET thumbnail = $1
|
||||||
|
WHERE id = $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateLocationThumbnailParams struct {
|
||||||
|
Thumbnail sql.NullString `json:"thumbnail"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateLocationThumbnail(ctx context.Context, arg UpdateLocationThumbnailParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, updateLocationThumbnail, arg.Thumbnail, arg.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -57,6 +57,51 @@ func (ns NullCommentType) Value() (driver.Value, error) {
|
|||||||
return string(ns.CommentType), nil
|
return string(ns.CommentType), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LocationType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LocationTypeBeach LocationType = "beach"
|
||||||
|
LocationTypeAmusementpark LocationType = "amusement park"
|
||||||
|
LocationTypeCulinary LocationType = "culinary"
|
||||||
|
LocationTypeHikingCamping LocationType = "hiking / camping"
|
||||||
|
LocationTypeOther LocationType = "other"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *LocationType) Scan(src interface{}) error {
|
||||||
|
switch s := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
*e = LocationType(s)
|
||||||
|
case string:
|
||||||
|
*e = LocationType(s)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported scan type for LocationType: %T", src)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullLocationType struct {
|
||||||
|
LocationType LocationType `json:"location_type"`
|
||||||
|
Valid bool `json:"valid"` // Valid is true if LocationType is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (ns *NullLocationType) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
ns.LocationType, ns.Valid = "", false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ns.Valid = true
|
||||||
|
return ns.LocationType.Scan(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (ns NullLocationType) Value() (driver.Value, error) {
|
||||||
|
if !ns.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return string(ns.LocationType), nil
|
||||||
|
}
|
||||||
|
|
||||||
type UserReportsType string
|
type UserReportsType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -140,11 +185,12 @@ type Location struct {
|
|||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
||||||
|
LocationType LocationType `json:"location_type"`
|
||||||
SubmittedBy int32 `json:"submitted_by"`
|
SubmittedBy int32 `json:"submitted_by"`
|
||||||
TotalVisited sql.NullInt32 `json:"total_visited"`
|
TotalVisited sql.NullInt32 `json:"total_visited"`
|
||||||
Thumbnail sql.NullString `json:"thumbnail"`
|
Thumbnail sql.NullString `json:"thumbnail"`
|
||||||
RegencyID int16 `json:"regency_id"`
|
RegencyID int16 `json:"regency_id"`
|
||||||
IsDeleted sql.NullBool `json:"is_deleted"`
|
IsDeleted bool `json:"is_deleted"`
|
||||||
CreatedAt sql.NullTime `json:"created_at"`
|
CreatedAt sql.NullTime `json:"created_at"`
|
||||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
||||||
@ -160,39 +206,54 @@ type LocationImage struct {
|
|||||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Province struct {
|
type NewsEvent struct {
|
||||||
ID int32 `json:"id"`
|
|
||||||
ProvinceName sql.NullString `json:"province_name"`
|
|
||||||
RegionID int16 `json:"region_id"`
|
|
||||||
CreatedAt sql.NullTime `json:"created_at"`
|
|
||||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Regency struct {
|
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
RegencyName sql.NullString `json:"regency_name"`
|
Title string `json:"title"`
|
||||||
ProvinceID int16 `json:"province_id"`
|
Url string `json:"url"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Thumbnail sql.NullString `json:"thumbnail"`
|
||||||
|
Description sql.NullString `json:"description"`
|
||||||
|
IsDeleted bool `json:"is_deleted"`
|
||||||
|
SubmittedBy int32 `json:"submitted_by"`
|
||||||
|
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
||||||
CreatedAt sql.NullTime `json:"created_at"`
|
CreatedAt sql.NullTime `json:"created_at"`
|
||||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Province struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
ProvinceName string `json:"province_name"`
|
||||||
|
RegionID int16 `json:"region_id"`
|
||||||
|
CreatedAt sql.NullTime `json:"created_at"`
|
||||||
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Regency struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
RegencyName string `json:"regency_name"`
|
||||||
|
ProvinceID int16 `json:"province_id"`
|
||||||
|
CreatedAt sql.NullTime `json:"created_at"`
|
||||||
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type Region struct {
|
type Region struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
RegionName sql.NullString `json:"region_name"`
|
RegionName string `json:"region_name"`
|
||||||
CreatedAt sql.NullTime `json:"created_at"`
|
CreatedAt sql.NullTime `json:"created_at"`
|
||||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Review struct {
|
type Review struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
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"`
|
||||||
IsFromCritic bool `json:"is_from_critic"`
|
IsFromCritic bool `json:"is_from_critic"`
|
||||||
IsHided bool `json:"is_hided"`
|
CostApprox sql.NullInt32 `json:"cost_approx"`
|
||||||
LocationID int32 `json:"location_id"`
|
IsHided bool `json:"is_hided"`
|
||||||
CreatedAt sql.NullTime `json:"created_at"`
|
LocationID int32 `json:"location_id"`
|
||||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
CreatedAt sql.NullTime `json:"created_at"`
|
||||||
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
@ -222,6 +283,8 @@ type User struct {
|
|||||||
SocialMedia pqtype.NullRawMessage `json:"social_media"`
|
SocialMedia pqtype.NullRawMessage `json:"social_media"`
|
||||||
CreatedAt sql.NullTime `json:"created_at"`
|
CreatedAt sql.NullTime `json:"created_at"`
|
||||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||||
|
About sql.NullString `json:"about"`
|
||||||
|
Website sql.NullString `json:"website"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserActivity struct {
|
type UserActivity struct {
|
||||||
@ -235,6 +298,14 @@ type UserActivity struct {
|
|||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserFollow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
FollowerID int32 `json:"follower_id"`
|
||||||
|
FolloweeID int32 `json:"followee_id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type UserReport struct {
|
type UserReport struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
87
db/sqlc/news_events.go
Normal file
87
db/sqlc/news_events.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetNewsEventsListParams struct {
|
||||||
|
Limit int32
|
||||||
|
Offset int32
|
||||||
|
IsWithApproved string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewsEventRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsDeleted bool `json:"is_deleted"`
|
||||||
|
SubmittedBy string `json:"submitted_by"`
|
||||||
|
ApprovedBy int32 `json:"approved_by"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetNewsEventsList(ctx context.Context, arg GetNewsEventsListParams) ([]NewsEventRow, error) {
|
||||||
|
getNewsEventsListQ := fmt.Sprintf(`
|
||||||
|
SELECT
|
||||||
|
n.id,
|
||||||
|
n.title,
|
||||||
|
n.url,
|
||||||
|
n.source,
|
||||||
|
COALESCE(n.thumbnail, '') as thumbnail,
|
||||||
|
COALESCE(n.description, '') as description,
|
||||||
|
n.is_deleted,
|
||||||
|
u.username as submitted_by,
|
||||||
|
COALESCE(n.approved_by, 0) as approved_by,
|
||||||
|
n.created_at,
|
||||||
|
n.updated_at
|
||||||
|
FROM news_events n
|
||||||
|
JOIN users u ON n.submitted_by = u.id
|
||||||
|
%s
|
||||||
|
ORDER BY n.created_at DESC
|
||||||
|
LIMIT $1
|
||||||
|
OFFSET $2
|
||||||
|
`, arg.IsWithApproved)
|
||||||
|
|
||||||
|
rows, err := q.db.QueryContext(ctx, getNewsEventsListQ, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
items := []NewsEventRow{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var i NewsEventRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Url,
|
||||||
|
&i.Source,
|
||||||
|
&i.Thumbnail,
|
||||||
|
&i.Description,
|
||||||
|
&i.IsDeleted,
|
||||||
|
&i.SubmittedBy,
|
||||||
|
&i.ApprovedBy,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
40
db/sqlc/news_events.sql.go
Normal file
40
db/sqlc/news_events.sql.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.20.0
|
||||||
|
// source: news_events.sql
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
const createNewsEvents = `-- name: CreateNewsEvents :exec
|
||||||
|
INSERT INTO news_events(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
source,
|
||||||
|
description,
|
||||||
|
submitted_by
|
||||||
|
) VALUES ( $1, $2, $3, $4, $5)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateNewsEventsParams struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Description sql.NullString `json:"description"`
|
||||||
|
SubmittedBy int32 `json:"submitted_by"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateNewsEvents(ctx context.Context, arg CreateNewsEventsParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, createNewsEvents,
|
||||||
|
arg.Title,
|
||||||
|
arg.Url,
|
||||||
|
arg.Source,
|
||||||
|
arg.Description,
|
||||||
|
arg.SubmittedBy,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
47
db/sqlc/provinces.sql.go
Normal file
47
db/sqlc/provinces.sql.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.20.0
|
||||||
|
// source: provinces.sql
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const getListProvinces = `-- name: GetListProvinces :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
province_name,
|
||||||
|
region_id
|
||||||
|
FROM PROVINCES
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetListProvincesRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
ProvinceName string `json:"province_name"`
|
||||||
|
RegionID int16 `json:"region_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetListProvinces(ctx context.Context) ([]GetListProvincesRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getListProvinces)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetListProvincesRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetListProvincesRow
|
||||||
|
if err := rows.Scan(&i.ID, &i.ProvinceName, &i.RegionID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
@ -6,21 +6,28 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Querier interface {
|
type Querier interface {
|
||||||
|
AddFollowUser(ctx context.Context, arg AddFollowUserParams) error
|
||||||
CheckIfReviewExists(ctx context.Context, arg CheckIfReviewExistsParams) (int64, error)
|
CheckIfReviewExists(ctx context.Context, arg CheckIfReviewExistsParams) (int64, error)
|
||||||
CreateLocation(ctx context.Context, arg CreateLocationParams) error
|
CreateNewsEvents(ctx context.Context, arg CreateNewsEventsParams) error
|
||||||
CreateSession(ctx context.Context, arg CreateSessionParams) (UserSession, error)
|
CreateSession(ctx context.Context, arg CreateSessionParams) (UserSession, error)
|
||||||
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
|
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
|
||||||
GetCountImageByLocation(ctx context.Context, imageOf int32) (int64, error)
|
GetCountImageByLocation(ctx context.Context, imageOf int32) (int64, error)
|
||||||
GetListLocations(ctx context.Context) ([]Location, error)
|
GetListLocations(ctx context.Context) ([]Location, error)
|
||||||
|
GetListProvinces(ctx context.Context) ([]GetListProvincesRow, error)
|
||||||
GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error)
|
GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error)
|
||||||
|
GetListRegencies(ctx context.Context) ([]GetListRegenciesRow, error)
|
||||||
|
GetListRegions(ctx context.Context) ([]GetListRegionsRow, error)
|
||||||
GetLocationTag(ctx context.Context, targetID int32) ([]string, error)
|
GetLocationTag(ctx context.Context, targetID int32) ([]string, error)
|
||||||
GetSession(ctx context.Context, id int32) (UserSession, error)
|
GetSession(ctx context.Context, id int32) (UserSession, error)
|
||||||
GetUserReviewByLocation(ctx context.Context, arg GetUserReviewByLocationParams) (GetUserReviewByLocationRow, error)
|
GetUserReviewByLocation(ctx context.Context, arg GetUserReviewByLocationParams) (GetUserReviewByLocationRow, error)
|
||||||
|
RemoveFollowUser(ctx context.Context, arg RemoveFollowUserParams) error
|
||||||
|
UpdateAvatar(ctx context.Context, arg UpdateAvatarParams) (sql.NullString, error)
|
||||||
|
UpdateLocationThumbnail(ctx context.Context, arg UpdateLocationThumbnailParams) error
|
||||||
UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error
|
UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error
|
||||||
UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Querier = (*Queries)(nil)
|
var _ Querier = (*Queries)(nil)
|
||||||
|
47
db/sqlc/regencies.sql.go
Normal file
47
db/sqlc/regencies.sql.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.20.0
|
||||||
|
// source: regencies.sql
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const getListRegencies = `-- name: GetListRegencies :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
regency_name,
|
||||||
|
province_id
|
||||||
|
FROM regencies
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetListRegenciesRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
RegencyName string `json:"regency_name"`
|
||||||
|
ProvinceID int16 `json:"province_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetListRegencies(ctx context.Context) ([]GetListRegenciesRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getListRegencies)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetListRegenciesRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetListRegenciesRow
|
||||||
|
if err := rows.Scan(&i.ID, &i.RegencyName, &i.ProvinceID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
45
db/sqlc/regions.sql.go
Normal file
45
db/sqlc/regions.sql.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.20.0
|
||||||
|
// source: regions.sql
|
||||||
|
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const getListRegions = `-- name: GetListRegions :many
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
region_name
|
||||||
|
FROM regions
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetListRegionsRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
RegionName string `json:"region_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetListRegions(ctx context.Context) ([]GetListRegionsRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getListRegions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetListRegionsRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetListRegionsRow
|
||||||
|
if err := rows.Scan(&i.ID, &i.RegionName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
@ -3,6 +3,7 @@ package db
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/yiplee/sqlc"
|
"github.com/yiplee/sqlc"
|
||||||
)
|
)
|
||||||
@ -13,8 +14,14 @@ type Store interface {
|
|||||||
GetImagesByLocation(ctx context.Context, arg GetImagesByLocationParams) ([]GetImagesByLocationRow, error)
|
GetImagesByLocation(ctx context.Context, arg GetImagesByLocationParams) ([]GetImagesByLocationRow, error)
|
||||||
GetLocation(ctx context.Context, location_id int32) (GetLocationRow, error)
|
GetLocation(ctx context.Context, location_id int32) (GetLocationRow, error)
|
||||||
GetUser(ctx context.Context, username string) (GetUserRow, error)
|
GetUser(ctx context.Context, username string) (GetUserRow, error)
|
||||||
|
UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error)
|
||||||
|
GetUserStats(ctx context.Context, user_id int32) (GetUserStatsRow, error)
|
||||||
CreateReview(ctx context.Context, arg CreateReviewParams) (Review, error)
|
CreateReview(ctx context.Context, arg CreateReviewParams) (Review, error)
|
||||||
GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error)
|
GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error)
|
||||||
|
CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error)
|
||||||
|
CreateImage(ctx context.Context, arg []CreateImageParams) error
|
||||||
|
CreateLocationTx(ctx context.Context, arg CreateLocationTxParams) error
|
||||||
|
GetNewsEventsList(ctx context.Context, arg GetNewsEventsListParams) ([]NewsEventRow, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SQLStore struct {
|
type SQLStore struct {
|
||||||
@ -30,19 +37,19 @@ func NewStore(db *sql.DB) Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TRANSACTION QUERY FUNCTION
|
// TRANSACTION QUERY FUNCTION
|
||||||
// func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) error {
|
func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) error {
|
||||||
// tx, err := store.db.BeginTx(ctx, nil)
|
tx, err := store.db.BeginTx(ctx, nil)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return err
|
return err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// q := New(tx)
|
q := New(tx)
|
||||||
// err = fn(q)
|
err = fn(q)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// if rbErr := tx.Rollback(); rbErr != nil {
|
if rbErr := tx.Rollback(); rbErr != nil {
|
||||||
// return fmt.Errorf("tx err: %v, rb err : %v", err, rbErr)
|
return fmt.Errorf("tx err: %v, rb err : %v", err, rbErr)
|
||||||
// }
|
}
|
||||||
// return err
|
return err
|
||||||
// }
|
}
|
||||||
// return tx.Commit()
|
return tx.Commit()
|
||||||
// }
|
}
|
||||||
|
@ -31,6 +31,6 @@ func TestCreateLocation(t *testing.T) {
|
|||||||
GoogleMapsLink: sql.NullString{Valid: true, String: util.RandomString(10)},
|
GoogleMapsLink: sql.NullString{Valid: true, String: util.RandomString(10)},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := testQueries.CreateLocation(context.Background(), arg)
|
_, err := testQueries.CreateLocation(context.Background(), arg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
59
db/sqlc/tx_location.go
Normal file
59
db/sqlc/tx_location.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateLocationTxParams struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SubmittedBy int32 `json:"submitted_by"`
|
||||||
|
LocationType LocationType `json:"location_type"`
|
||||||
|
RegencyID int16 `json:"regency_id"`
|
||||||
|
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
||||||
|
IsDeleted bool `json:"is_deleted"`
|
||||||
|
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
||||||
|
Thumbnail []CreateImageParams `json:"thumbnails"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *SQLStore) CreateLocationTx(ctx context.Context, arg CreateLocationTxParams) error {
|
||||||
|
err := store.execTx(ctx, func(q *Queries) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
location_id, err := q.CreateLocation(ctx, CreateLocationParams{
|
||||||
|
Address: arg.Address,
|
||||||
|
Name: arg.Name,
|
||||||
|
SubmittedBy: arg.SubmittedBy,
|
||||||
|
LocationType: arg.LocationType,
|
||||||
|
RegencyID: arg.RegencyID,
|
||||||
|
GoogleMapsLink: arg.GoogleMapsLink,
|
||||||
|
IsDeleted: arg.IsDeleted,
|
||||||
|
ApprovedBy: arg.ApprovedBy,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(arg.Thumbnail) > 0 {
|
||||||
|
err := q.CreateImage(ctx, arg.Thumbnail)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.UpdateLocationThumbnail(ctx, UpdateLocationThumbnailParams{
|
||||||
|
Thumbnail: sql.NullString{Valid: true, String: arg.Thumbnail[0].ImageUrl},
|
||||||
|
ID: location_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
202
db/sqlc/users.go
202
db/sqlc/users.go
@ -3,16 +3,21 @@ package db
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sqlc-dev/pqtype"
|
"github.com/sqlc-dev/pqtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
const getUser = `-- name: GetUser :one
|
const getUserQ = `-- name: GetUser :one
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
COALESCE(email, '') as email,
|
COALESCE(email, '') as email,
|
||||||
password,
|
password,
|
||||||
username,
|
username,
|
||||||
|
COALESCE(google_sign_in_payload, '') as google_sign_in_payload,
|
||||||
|
COALESCE(about, '') as about,
|
||||||
|
COALESCE(website, '') as website,
|
||||||
COALESCE(avatar_picture, '') as avatar_picture,
|
COALESCE(avatar_picture, '') as avatar_picture,
|
||||||
banned_at,
|
banned_at,
|
||||||
banned_until,
|
banned_until,
|
||||||
@ -21,35 +26,45 @@ SELECT
|
|||||||
is_admin,
|
is_admin,
|
||||||
is_critics,
|
is_critics,
|
||||||
is_verified,
|
is_verified,
|
||||||
social_media
|
COALESCE(social_media, '[]'),
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
FROM USERS
|
FROM USERS
|
||||||
WHERE username = $1
|
WHERE username = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetUserRow struct {
|
type GetUserRow struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"-"`
|
Password string `json:"-"`
|
||||||
Username string `json:"username"`
|
About string `json:"about"`
|
||||||
AvatarPicture string `json:"avatar_picture"`
|
Website string `json:"website"`
|
||||||
BannedAt sql.NullTime `json:"banned_at"`
|
Username string `json:"username"`
|
||||||
BannedUntil sql.NullTime `json:"banned_until"`
|
GoogleSignInPayload string `json:"google_sign_in_payload"`
|
||||||
BanReason string `json:"ban_reason"`
|
AvatarPicture string `json:"avatar_picture"`
|
||||||
IsPermaban bool `json:"is_permaban"`
|
BannedAt sql.NullTime `json:"banned_at"`
|
||||||
IsAdmin bool `json:"is_admin"`
|
BannedUntil sql.NullTime `json:"banned_until"`
|
||||||
IsCritics bool `json:"is_critics"`
|
BanReason string `json:"ban_reason"`
|
||||||
IsVerified bool `json:"is_verified"`
|
IsPermaban bool `json:"is_permaban"`
|
||||||
SocialMedia pqtype.NullRawMessage `json:"social_media"`
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
IsCritics bool `json:"is_critics"`
|
||||||
|
IsVerified bool `json:"is_verified"`
|
||||||
|
SocialMedia json.RawMessage `json:"social_media"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, error) {
|
func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, error) {
|
||||||
row := q.db.QueryRowContext(ctx, getUser, username)
|
row := q.db.QueryRowContext(ctx, getUserQ, username)
|
||||||
var i GetUserRow
|
var i GetUserRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Password,
|
&i.Password,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
|
&i.GoogleSignInPayload,
|
||||||
|
&i.About,
|
||||||
|
&i.Website,
|
||||||
&i.AvatarPicture,
|
&i.AvatarPicture,
|
||||||
&i.BannedAt,
|
&i.BannedAt,
|
||||||
&i.BannedUntil,
|
&i.BannedUntil,
|
||||||
@ -59,6 +74,161 @@ func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, err
|
|||||||
&i.IsCritics,
|
&i.IsCritics,
|
||||||
&i.IsVerified,
|
&i.IsVerified,
|
||||||
&i.SocialMedia,
|
&i.SocialMedia,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUserStatsQ = `
|
||||||
|
SELECT
|
||||||
|
json_agg(ur.*) AS reviews,
|
||||||
|
( SELECT COUNT(id) FROM user_follow u WHERE u.followee_id = $1) as followers,
|
||||||
|
( SELECT COUNT(id) FROM reviews r WHERE r.submitted_by = $1 ) as score_count,
|
||||||
|
( SELECT
|
||||||
|
json_agg(r1.*) AS scores_distribution
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
( SELECT COUNT(id) as "0" FROM reviews r WHERE r.score >= 0 AND r.score <= 9 AND r.submitted_by = $1) as score0,
|
||||||
|
( SELECT COUNT(id) as "1" FROM reviews r WHERE r.score >= 10 AND r.score <= 19 AND r.submitted_by = $1) as score1,
|
||||||
|
( SELECT COUNT(id) as "2" FROM reviews r WHERE r.score >= 20 AND r.score <= 29 AND r.submitted_by = $1) as score2,
|
||||||
|
( SELECT COUNT(id) as "3" FROM reviews r WHERE r.score >= 30 AND r.score <= 39 AND r.submitted_by = $1) as score3,
|
||||||
|
( SELECT COUNT(id) as "4" FROM reviews r WHERE r.score >= 40 AND r.score <= 49 AND r.submitted_by = $1) as score4,
|
||||||
|
( SELECT COUNT(id) as "5" FROM reviews r WHERE r.score >= 50 AND r.score <= 59 AND r.submitted_by = $1) as score5,
|
||||||
|
( SELECT COUNT(id) as "6" FROM reviews r WHERE r.score >= 60 AND r.score <= 69 AND r.submitted_by = $1) as score6,
|
||||||
|
( SELECT COUNT(id) as "7" FROM reviews r WHERE r.score >= 70 AND r.score <= 79 AND r.submitted_by = $1) as score7,
|
||||||
|
( SELECT COUNT(id) as "8" FROM reviews r WHERE r.score >= 80 AND r.score <= 89 AND r.submitted_by = $1) as score8,
|
||||||
|
( SELECT COUNT(id) as "9" FROM reviews r WHERE r.score >= 90 AND r.score <= 99 AND r.submitted_by = $1) as score9,
|
||||||
|
( SELECT COUNT(id) as "99" FROM reviews r WHERE r.score = 100 AND r.submitted_by = $1) as score10
|
||||||
|
) r1
|
||||||
|
)
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
reviews.id,
|
||||||
|
reviews.comments,
|
||||||
|
score,
|
||||||
|
l.name,
|
||||||
|
p.province_name,
|
||||||
|
COALESCE(l.thumbnail, '') as thumbnail
|
||||||
|
FROM
|
||||||
|
reviews
|
||||||
|
JOIN locations l on reviews.location_id = l.id
|
||||||
|
JOIN regencies r on l.regency_id = r.id
|
||||||
|
JOIN provinces p on r.province_id = p.id
|
||||||
|
WHERE reviews.submitted_by = $1
|
||||||
|
ORDER BY score ASC
|
||||||
|
LIMIT 10
|
||||||
|
) ur
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetUserStatsRow struct {
|
||||||
|
Reviews []byte `json:"-"`
|
||||||
|
Followers int32 `json:"followers"`
|
||||||
|
ScoreCount int32 `json:"score_count"`
|
||||||
|
ScoresDistribution []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetUserStats(ctx context.Context, user_id int32) (GetUserStatsRow, error) {
|
||||||
|
var i GetUserStatsRow
|
||||||
|
row := q.db.QueryRowContext(ctx, getUserStatsQ, user_id)
|
||||||
|
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Reviews,
|
||||||
|
&i.Followers,
|
||||||
|
&i.ScoreCount,
|
||||||
|
&i.ScoresDistribution,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateUser = `-- name: UpdateUser :one
|
||||||
|
UPDATE users
|
||||||
|
SET
|
||||||
|
about = $1,
|
||||||
|
social_media = $2,
|
||||||
|
website = $3
|
||||||
|
WHERE
|
||||||
|
id = $4
|
||||||
|
RETURNING
|
||||||
|
id,
|
||||||
|
COALESCE(email, ''),
|
||||||
|
username,
|
||||||
|
COALESCE(avatar_picture, ''),
|
||||||
|
COALESCE(about, ''),
|
||||||
|
COALESCE(website, ''),
|
||||||
|
COALESCE(google_sign_in_payload, ''),
|
||||||
|
banned_at,
|
||||||
|
banned_until,
|
||||||
|
COALESCE(ban_reason, ''),
|
||||||
|
is_permaban,
|
||||||
|
is_admin,
|
||||||
|
is_critics,
|
||||||
|
is_verified,
|
||||||
|
is_active,
|
||||||
|
COALESCE(social_media, '[]'),
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateUserParams struct {
|
||||||
|
About sql.NullString `json:"about"`
|
||||||
|
SocialMedia pqtype.NullRawMessage `json:"social_media"`
|
||||||
|
Website sql.NullString `json:"website"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateUserRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
AvatarPicture string `json:"avatar_picture"`
|
||||||
|
About string `json:"about"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
GoogleSignInPayload string `json:"google_sign_in_payload"`
|
||||||
|
BannedAt sql.NullTime `json:"banned_at"`
|
||||||
|
BannedUntil sql.NullTime `json:"banned_until"`
|
||||||
|
BanReason string `json:"ban_reason"`
|
||||||
|
IsPermaban bool `json:"is_permaban"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
IsCritics bool `json:"is_critics"`
|
||||||
|
IsVerified bool `json:"is_verified"`
|
||||||
|
IsActive bool `json:"is_active"`
|
||||||
|
SocialMedia json.RawMessage `json:"social_media"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, updateUser,
|
||||||
|
arg.About,
|
||||||
|
arg.SocialMedia,
|
||||||
|
arg.Website,
|
||||||
|
arg.ID,
|
||||||
|
)
|
||||||
|
var i UpdateUserRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Email,
|
||||||
|
&i.Username,
|
||||||
|
&i.AvatarPicture,
|
||||||
|
&i.About,
|
||||||
|
&i.Website,
|
||||||
|
&i.GoogleSignInPayload,
|
||||||
|
&i.BannedAt,
|
||||||
|
&i.BannedUntil,
|
||||||
|
&i.BanReason,
|
||||||
|
&i.IsPermaban,
|
||||||
|
&i.IsAdmin,
|
||||||
|
&i.IsCritics,
|
||||||
|
&i.IsVerified,
|
||||||
|
&i.IsActive,
|
||||||
|
&i.SocialMedia,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ INSERT INTO users (
|
|||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
) VALUES ($1, $2)
|
) VALUES ($1, $2)
|
||||||
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at
|
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at, about, website
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateUserParams struct {
|
type CreateUserParams struct {
|
||||||
@ -44,10 +44,31 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
|
|||||||
&i.SocialMedia,
|
&i.SocialMedia,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
|
&i.About,
|
||||||
|
&i.Website,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateAvatar = `-- name: UpdateAvatar :one
|
||||||
|
UPDATE users
|
||||||
|
SET avatar_picture = $1
|
||||||
|
WHERE id = $2
|
||||||
|
RETURNING avatar_picture
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateAvatarParams struct {
|
||||||
|
AvatarPicture sql.NullString `json:"avatar_picture"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateAvatar(ctx context.Context, arg UpdateAvatarParams) (sql.NullString, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, updateAvatar, arg.AvatarPicture, arg.ID)
|
||||||
|
var avatar_picture sql.NullString
|
||||||
|
err := row.Scan(&avatar_picture)
|
||||||
|
return avatar_picture, err
|
||||||
|
}
|
||||||
|
|
||||||
const updatePassword = `-- name: UpdatePassword :exec
|
const updatePassword = `-- name: UpdatePassword :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET password = $1
|
SET password = $1
|
||||||
@ -63,51 +84,3 @@ func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams)
|
|||||||
_, err := q.db.ExecContext(ctx, updatePassword, arg.Password, arg.ID)
|
_, err := q.db.ExecContext(ctx, updatePassword, arg.Password, arg.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateUser = `-- name: UpdateUser :one
|
|
||||||
UPDATE users
|
|
||||||
SET
|
|
||||||
email = COALESCE($1, email),
|
|
||||||
username = COALESCE($2, username),
|
|
||||||
avatar_picture = COALESCE($3, avatar_picture)
|
|
||||||
WHERE
|
|
||||||
id = $4
|
|
||||||
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at
|
|
||||||
`
|
|
||||||
|
|
||||||
type UpdateUserParams struct {
|
|
||||||
Email sql.NullString `json:"email"`
|
|
||||||
Username sql.NullString `json:"username"`
|
|
||||||
AvatarPicture sql.NullString `json:"avatar_picture"`
|
|
||||||
ID int32 `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
|
|
||||||
row := q.db.QueryRowContext(ctx, updateUser,
|
|
||||||
arg.Email,
|
|
||||||
arg.Username,
|
|
||||||
arg.AvatarPicture,
|
|
||||||
arg.ID,
|
|
||||||
)
|
|
||||||
var i User
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.Email,
|
|
||||||
&i.Username,
|
|
||||||
&i.Password,
|
|
||||||
&i.AvatarPicture,
|
|
||||||
&i.GoogleSignInPayload,
|
|
||||||
&i.BannedAt,
|
|
||||||
&i.BannedUntil,
|
|
||||||
&i.BanReason,
|
|
||||||
&i.IsPermaban,
|
|
||||||
&i.IsAdmin,
|
|
||||||
&i.IsCritics,
|
|
||||||
&i.IsVerified,
|
|
||||||
&i.IsActive,
|
|
||||||
&i.SocialMedia,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UpdatedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
@ -5,7 +5,7 @@ sudo -u postgres psql \
|
|||||||
-c '\copy regions(id, region_name) FROM '"'/tmp/regions.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy regions(id, region_name) FROM '"'/tmp/regions.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
-c '\copy provinces(id, province_name, region_id) FROM '"'/tmp/provinsi.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy provinces(id, province_name, region_id) FROM '"'/tmp/provinsi.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
-c '\copy regencies(id, province_id, regency_name) FROM '"'/tmp/kabupaten.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy regencies(id, province_id, regency_name) FROM '"'/tmp/kabupaten.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
-c '\copy locations(id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
-c '\copy locations(id,location_type,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by,is_deleted) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||||
-c '\copy reviews(id,submitted_by,is_from_critic,comments,score,is_hided,location_id) FROM '"'/tmp/reviews.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
-c '\copy reviews(id,submitted_by,is_from_critic,comments,score,is_hided,location_id) FROM '"'/tmp/reviews.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||||
-c '\copy images(id,image_url,uploaded_by,image_type,image_of) FROM '"'/tmp/images.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
-c '\copy images(id,image_url,uploaded_by,image_type,image_of) FROM '"'/tmp/images.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||||
-c '\copy tags(id,name,submitted_by,target_id,tags_type) FROM '"'/tmp/tags.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy tags(id,name,submitted_by,target_id,tags_type) FROM '"'/tmp/tags.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
@ -15,7 +15,7 @@ sudo -u postgres psql \
|
|||||||
-c '\copy regions(id, region_name) FROM '"'/tmp/regions.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy regions(id, region_name) FROM '"'/tmp/regions.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
-c '\copy provinces(id, province_name, region_id) FROM '"'/tmp/provinsi.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy provinces(id, province_name, region_id) FROM '"'/tmp/provinsi.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
-c '\copy regencies(id, province_id, regency_name) FROM '"'/tmp/kabupaten.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy regencies(id, province_id, regency_name) FROM '"'/tmp/kabupaten.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
-c '\copy locations(id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
-c '\copy locations(id,location_type,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by,is_deleted) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||||
-c '\copy reviews(id,submitted_by,is_from_critic,comments,score,is_hided,location_id) FROM '"'/tmp/reviews.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
-c '\copy reviews(id,submitted_by,is_from_critic,comments,score,is_hided,location_id) FROM '"'/tmp/reviews.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||||
-c '\copy images(id,image_url,uploaded_by,image_type,image_of) FROM '"'/tmp/images.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
-c '\copy images(id,image_url,uploaded_by,image_type,image_of) FROM '"'/tmp/images.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||||
-c '\copy tags(id,name,submitted_by,target_id,tags_type) FROM '"'/tmp/tags.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
-c '\copy tags(id,name,submitted_by,target_id,tags_type) FROM '"'/tmp/tags.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||||
|
11
notes
11
notes
@ -104,4 +104,15 @@ PAGINATION
|
|||||||
|
|
||||||
http://andreyzavadskiy.com/2016/12/03/pagination-and-total-number-of-rows-from-one-select/
|
http://andreyzavadskiy.com/2016/12/03/pagination-and-total-number-of-rows-from-one-select/
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
PAGINATION
|
||||||
|
|
||||||
|
http://andreyzavadskiy.com/2016/12/03/pagination-and-total-number-of-rows-from-one-select/
|
||||||
|
https://medium.easyread.co/how-to-do-pagination-in-postgres-with-golang-in-4-common-ways-12365b9fb528?gi=a4683590d1d2
|
||||||
|
|
||||||
##########################################################################################
|
##########################################################################################
|
14
util/common.go
Normal file
14
util/common.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReplaceSQL(old, searchPattern string) string {
|
||||||
|
tmpCount := strings.Count(old, searchPattern)
|
||||||
|
for m := 1; m <= tmpCount; m++ {
|
||||||
|
old = strings.Replace(old, searchPattern, "$"+strconv.Itoa(m), 1)
|
||||||
|
}
|
||||||
|
return old
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user