Add stargazers endpoint (#1)
This commit is contained in:
parent
450814760c
commit
852c181ac3
|
@ -32,6 +32,7 @@ go.work
|
||||||
# build
|
# build
|
||||||
cloud-backend
|
cloud-backend
|
||||||
cloud-backend.exe
|
cloud-backend.exe
|
||||||
|
main
|
||||||
|
|
||||||
# docker overrides
|
# docker overrides
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"daos",
|
||||||
"labstack",
|
"labstack",
|
||||||
"Middlewares",
|
"Middlewares",
|
||||||
"pocketbase"
|
"pocketbase",
|
||||||
|
"unstar"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
10
UPGRADE.md
10
UPGRADE.md
|
@ -1,9 +1,17 @@
|
||||||
# Upgrade guide
|
# Upgrade guide
|
||||||
|
|
||||||
|
## From v1.0.1 to v1.1.0
|
||||||
|
|
||||||
|
This version adds stargazers.
|
||||||
|
|
||||||
|
1. import new schema first
|
||||||
|
2. get the new binary or Dockerfile and run `cloud-backend`, make sure everything works as it was before (don't forget to backup `pb_data` data)
|
||||||
|
3. upgrade frontend to `v1.1.0`
|
||||||
|
|
||||||
## From v1.0.0 to v1.0.1
|
## From v1.0.0 to v1.0.1
|
||||||
|
|
||||||
This version adds to new custom endpoints for fetching tune and ini file.
|
This version adds to new custom endpoints for fetching tune and ini file.
|
||||||
|
|
||||||
1. get the new binary and run `cloud-backend`, make sure everything works as it was before (don't forget to backup `pb_data` data)
|
1. get the new binary or Dockerfile and run `cloud-backend`, make sure everything works as it was before (don't forget to backup `pb_data` data)
|
||||||
2. upgrade frontend to `v1.0.1`
|
2. upgrade frontend to `v1.0.1`
|
||||||
3. import new schema
|
3. import new schema
|
||||||
|
|
10
go.mod
10
go.mod
|
@ -1,8 +1,12 @@
|
||||||
module hyper-tuner/cloud-backend
|
module main
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require github.com/pocketbase/pocketbase v0.8.0-rc2.0.20221105112208-a2abeb872aca
|
require (
|
||||||
|
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198
|
||||||
|
github.com/pocketbase/dbx v1.7.0
|
||||||
|
github.com/pocketbase/pocketbase v0.8.0-rc2.0.20221105112208-a2abeb872aca
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.6 // indirect
|
github.com/AlecAivazis/survey/v2 v2.3.6 // indirect
|
||||||
|
@ -42,12 +46,10 @@ require (
|
||||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/pocketbase/dbx v1.7.0 // indirect
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/spf13/cobra v1.6.1 // indirect
|
github.com/spf13/cobra v1.6.1 // indirect
|
||||||
|
|
138
main.go
138
main.go
|
@ -4,14 +4,22 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
_ "main/migrations"
|
||||||
|
|
||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/apis"
|
"github.com/pocketbase/pocketbase/apis"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/daos"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
tunesCollectionName := "tunes"
|
||||||
|
iniFilesCollectionName := "iniFiles"
|
||||||
|
stargazersCollectionName := "stargazers"
|
||||||
|
|
||||||
app := pocketbase.New()
|
app := pocketbase.New()
|
||||||
|
|
||||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||||
|
@ -19,14 +27,14 @@ func main() {
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
Path: "/api/custom/tunes/byTuneId/:tuneId",
|
Path: "/api/custom/tunes/byTuneId/:tuneId",
|
||||||
Handler: func(c echo.Context) error {
|
Handler: func(c echo.Context) error {
|
||||||
record, _err := app.Dao().FindFirstRecordByData("tunes", "tuneId", c.PathParam("tuneId"))
|
record, _err := app.Dao().FindFirstRecordByData(tunesCollectionName, "tuneId", c.PathParam("tuneId"))
|
||||||
|
|
||||||
if _err != nil {
|
if _err != nil {
|
||||||
return apis.NewNotFoundError("Tune not found", nil)
|
return apis.NewNotFoundError("Tune not found", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
_errors := app.Dao().ExpandRecord(record, []string{"author"}, func(relCollection *models.Collection, relIds []string) ([]*models.Record, error) {
|
_errors := app.Dao().ExpandRecord(record, []string{"author"}, func(relCollection *models.Collection, relIds []string) ([]*models.Record, error) {
|
||||||
record, _err := app.Dao().FindFirstRecordByData(relCollection.Name, "id", relIds[0])
|
record, _err := app.Dao().FindRecordById(relCollection.Name, relIds[0])
|
||||||
|
|
||||||
return []*models.Record{record}, _err
|
return []*models.Record{record}, _err
|
||||||
})
|
})
|
||||||
|
@ -43,7 +51,7 @@ func main() {
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
Path: "/api/custom/iniFiles/bySignature/:signature",
|
Path: "/api/custom/iniFiles/bySignature/:signature",
|
||||||
Handler: func(c echo.Context) error {
|
Handler: func(c echo.Context) error {
|
||||||
record, _err := app.Dao().FindFirstRecordByData("iniFiles", "signature", c.PathParam("signature"))
|
record, _err := app.Dao().FindFirstRecordByData(iniFilesCollectionName, "signature", c.PathParam("signature"))
|
||||||
|
|
||||||
if _err != nil {
|
if _err != nil {
|
||||||
return c.JSON(http.StatusNotFound, _err)
|
return c.JSON(http.StatusNotFound, _err)
|
||||||
|
@ -53,6 +61,130 @@ func main() {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
e.Router.AddRoute(echo.Route{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/api/custom/stargazers/toggleStar",
|
||||||
|
Handler: func(c echo.Context) (err error) {
|
||||||
|
auth := c.Get(apis.ContextAuthRecordKey).(*models.Record)
|
||||||
|
isStarred := false
|
||||||
|
|
||||||
|
type Stargazer struct {
|
||||||
|
User string
|
||||||
|
Tune string `json:"tune" form:"tune" query:"tune"`
|
||||||
|
}
|
||||||
|
|
||||||
|
stargazer := new(Stargazer)
|
||||||
|
|
||||||
|
if err = c.Bind(stargazer); err != nil {
|
||||||
|
return c.String(http.StatusBadRequest, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
stargazer.User = auth.Id
|
||||||
|
|
||||||
|
_err := app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
|
||||||
|
collection, err := app.Dao().FindCollectionByNameOrId(stargazersCollectionName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stargazerRecord := models.NewRecord(collection)
|
||||||
|
stargazerRecord.Set("user", stargazer.User)
|
||||||
|
stargazerRecord.Set("tune", stargazer.Tune)
|
||||||
|
|
||||||
|
tune, _err := app.Dao().FindFirstRecordByData(tunesCollectionName, "id", stargazer.Tune)
|
||||||
|
if _err != nil {
|
||||||
|
return apis.NewNotFoundError("Tune not found", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
_err = txDao.SaveRecord(stargazerRecord)
|
||||||
|
|
||||||
|
// UNIQUE constraint failed most likely, try to unstar
|
||||||
|
if _err != nil {
|
||||||
|
currentStargazerRecords, _err := app.Dao().FindRecordsByExpr(
|
||||||
|
stargazersCollectionName,
|
||||||
|
dbx.HashExp{"user": stargazer.User},
|
||||||
|
dbx.HashExp{"tune": stargazer.Tune},
|
||||||
|
)
|
||||||
|
|
||||||
|
if _err != nil || len(currentStargazerRecords) == 0 {
|
||||||
|
return _err
|
||||||
|
}
|
||||||
|
|
||||||
|
_err = txDao.DeleteRecord(currentStargazerRecords[0])
|
||||||
|
if _err != nil {
|
||||||
|
return _err
|
||||||
|
}
|
||||||
|
|
||||||
|
isStarred = false
|
||||||
|
tune.Set("stars", tune.Get("stars").(float64)-1)
|
||||||
|
} else {
|
||||||
|
isStarred = true
|
||||||
|
tune.Set("stars", tune.Get("stars").(float64)+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
_err = txDao.SaveRecord(tune)
|
||||||
|
if _err != nil {
|
||||||
|
return _err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if _err != nil {
|
||||||
|
return apis.NewNotFoundError("Tune not found or already starred", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch again and return current state
|
||||||
|
tune, _err := app.Dao().FindFirstRecordByData(tunesCollectionName, "id", stargazer.Tune)
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, map[string]any{
|
||||||
|
"stars": tune.Get("stars").(float64),
|
||||||
|
"isStarred": isStarred,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Middlewares: []echo.MiddlewareFunc{
|
||||||
|
apis.LoadAuthContext(app.App),
|
||||||
|
apis.RequireAdminOrRecordAuth("users"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
e.Router.AddRoute(echo.Route{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/api/custom/stargazers/starredByMe/:tune",
|
||||||
|
Handler: func(c echo.Context) (err error) {
|
||||||
|
auth := c.Get(apis.ContextAuthRecordKey).(*models.Record)
|
||||||
|
isStarred := true
|
||||||
|
|
||||||
|
type Stargazer struct {
|
||||||
|
User string
|
||||||
|
Tune string
|
||||||
|
}
|
||||||
|
|
||||||
|
stargazer := new(Stargazer)
|
||||||
|
|
||||||
|
stargazer.User = auth.Id
|
||||||
|
stargazer.Tune = c.PathParam("tune")
|
||||||
|
|
||||||
|
stargazerRecords, _err := app.Dao().FindRecordsByExpr(
|
||||||
|
stargazersCollectionName,
|
||||||
|
dbx.HashExp{"user": stargazer.User},
|
||||||
|
dbx.HashExp{"tune": stargazer.Tune},
|
||||||
|
)
|
||||||
|
|
||||||
|
if _err != nil || len(stargazerRecords) == 0 {
|
||||||
|
isStarred = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, map[string]any{
|
||||||
|
"isStarred": isStarred,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Middlewares: []echo.MiddlewareFunc{
|
||||||
|
apis.LoadAuthContext(app.App),
|
||||||
|
apis.RequireAdminOrRecordAuth("users"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/dbx"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(db dbx.Builder) error {
|
||||||
|
// add up queries...
|
||||||
|
db.CreateUniqueIndex("stargazers", "unique_stargazers_on_user_tune", "user", "tune").Execute()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, func(db dbx.Builder) error {
|
||||||
|
// add down queries...
|
||||||
|
db.DropIndex("stargazers", "unique_stargazers_on_user_tune").Execute()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -44,6 +44,18 @@
|
||||||
"pattern": ""
|
"pattern": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "lwbwtgmx",
|
||||||
|
"name": "stars",
|
||||||
|
"type": "number",
|
||||||
|
"system": false,
|
||||||
|
"required": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": 0,
|
||||||
|
"max": null
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "g9b17t9y",
|
"id": "g9b17t9y",
|
||||||
"name": "vehicleName",
|
"name": "vehicleName",
|
||||||
|
@ -427,5 +439,45 @@
|
||||||
"onlyEmailDomains": null,
|
"onlyEmailDomains": null,
|
||||||
"requireEmail": true
|
"requireEmail": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "z8cojwcvlyxxyll",
|
||||||
|
"name": "stargazers",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"id": "him7pbq2",
|
||||||
|
"name": "user",
|
||||||
|
"type": "relation",
|
||||||
|
"system": false,
|
||||||
|
"required": true,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"collectionId": "systemprofiles0",
|
||||||
|
"cascadeDelete": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ny7akrmn",
|
||||||
|
"name": "tune",
|
||||||
|
"type": "relation",
|
||||||
|
"system": false,
|
||||||
|
"required": true,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"maxSelect": 1,
|
||||||
|
"collectionId": "5djmpehuiigg06b",
|
||||||
|
"cascadeDelete": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue