diff --git a/README.md b/README.md index 76cabf2..72773cb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ If you want to contribute join us on the [Pebble Dev Discord server](http://disc ## Requirements -Backend/API layer requires `git`, `go`, `npm`, and `apib2swagger`. +Backend/API layer requires `git`, `go`, `npm`, `postgresql` and `apib2swagger`. To make the backend do anything, you also need to download a copy of the Pebble App Store. You can already start downloading it [here](https://drive.google.com/file/d/0B1rumprSXUAhTjB1aU9GUFVPUW8/view) while you setup the development environment. @@ -30,7 +30,8 @@ Instructions to setup the database: 1. If you haven't already, download a copy of the Pebble App Store by using [this tool](https://github.com/azertyfun/PebbleAppStoreCrawler). To ease the load on fitbit's servers, you can download it directly [here](https://drive.google.com/file/d/0B1rumprSXUAhTjB1aU9GUFVPUW8/view); 2. Extract the PebbleAppStore folder to the project directory: `tar -xzf PebbleAppStore.tar.gz -C $GOPATH/src/pebble-dev/rebblestore-api`; -3. Start `./rebblestore-api` and access http://localhost:8080/admin/rebuild/db to rebuild the database. +3. Setup the PostgreSQL database with a `rebblestore` user and a `rebblestore` database (as described on the [Arch Wiki](https://wiki.archlinux.org/index.php/PostgreSQL)), then [enable SSL](https://www.postgresql.org/docs/current/static/ssl-tcp.html) in `postgresql.conf`; +4. Start `./rebblestore-api` and access http://localhost:8080/admin/rebuild/db to rebuild the database. ## Contributing diff --git a/db/models.go b/db/models.go index 9d3e0c2..738ef49 100644 --- a/db/models.go +++ b/db/models.go @@ -1,5 +1,7 @@ package db +import "time" + // RebbleCard contains succint information about an app, to display on a result page for example type RebbleCard struct { Id string `json:"id"` @@ -23,7 +25,7 @@ type RebbleApplication struct { ThumbsUp int `json:"thumbs_up"` Type string `json:"type"` SupportedPlatforms []string `json:"supported_platforms"` - Published JSONTime `json:"published_date"` + Published time.Time `json:"published_date"` AppInfo RebbleAppInfo `json:"appInfo"` Assets RebbleAssets `json:"assets"` DoomsdayBackup bool `json:"doomsday_backup"` @@ -34,7 +36,7 @@ type RebbleAppInfo struct { PbwUrl string `json:"pbwUrl"` RebbleReady bool `json:"rebbleReady"` Tags []RebbleCollection `json:"tags"` - Updated JSONTime `json:"updated"` + Updated time.Time `json:"updated"` Version string `json:"version"` SupportUrl string `json:"supportUrl"` AuthorUrl string `json:"authorUrl"` @@ -69,7 +71,7 @@ type RebbleScreenshotsPlatform struct { // RebbleVersion contains information about a specific version of an app type RebbleVersion struct { - Number string `json:"number"` - ReleaseDate JSONTime `json:"release_date"` - Description string `json:"description"` + Number string `json:"number"` + ReleaseDate time.Time `json:"release_date"` + Description string `json:"description"` } diff --git a/db/queries.go b/db/queries.go index ae5f4fa..8d6ff3b 100644 --- a/db/queries.go +++ b/db/queries.go @@ -4,6 +4,7 @@ import ( "database/sql" "encoding/json" "errors" + "log" "regexp" "strings" "time" @@ -24,10 +25,11 @@ func (handler Handler) Search(query string) (RebbleCards, error) { var cards RebbleCards rows, err := handler.Query( - "SELECT id, name, type, thumbs_up, screenshots FROM apps WHERE name LIKE ? ESCAPE '!' ORDER BY thumbs_up DESC LIMIT 12", + "SELECT id, name, type, thumbs_up, screenshots FROM apps WHERE name LIKE $1 ESCAPE '!' ORDER BY thumbs_up DESC LIMIT 12", query, ) if err != nil { + log.Printf("SQL error while searching for `%v`", query) return cards, err } cards.Cards = make([]RebbleCard, 0) @@ -37,10 +39,12 @@ func (handler Handler) Search(query string) (RebbleCards, error) { var screenshots []RebbleScreenshotsPlatform err = rows.Scan(&card.Id, &card.Title, &card.Type, &card.ThumbsUp, &screenshots_b) if err != nil { + log.Printf("SQL error: Could not scan search results: %v", err) return RebbleCards{}, err } err = json.Unmarshal(screenshots_b, &screenshots) if err != nil { + log.Printf("Error: Could not unmarshal `screenshots`: %v", err) return RebbleCards{}, err } if len(screenshots) != 0 && len(screenshots[0].Screenshots) != 0 { @@ -61,11 +65,12 @@ func (handler Handler) GetAppsForCollection(collectionID string, sortByPopular b order = "published_date" } - row := handler.QueryRow("SELECT apps FROM collections WHERE id=?", collectionID) + row := handler.QueryRow("SELECT apps FROM collections WHERE id=$1", collectionID) var appIdsB []byte var appIds []string err := row.Scan(&appIdsB) if err != nil { + log.Printf("SQL error: Could not scan collection: %v", err) return nil, err } json.Unmarshal(appIdsB, &appIds) @@ -79,6 +84,7 @@ func (handler Handler) GetAppsForCollection(collectionID string, sortByPopular b // There is no feasible way for idList to contain user generated data, and therefore for it to be a SQL injection vector. But just in case, we strip any non-authorized character reg, err := regexp.Compile("[^a-zA-Z0-9, ']+") if err != nil { + log.Printf("Error: Could not Compile regex: %v", err) return nil, err } idList = reg.ReplaceAllString(idList, "") @@ -88,6 +94,7 @@ func (handler Handler) GetAppsForCollection(collectionID string, sortByPopular b // It is not possible to give a list or an order via a prepared statement, so this will have to do. We just sanitized idList, so SQL injection isn't a concern. rows, err := handler.Query("SELECT id, name, type, thumbs_up, screenshots, published_date, supported_platforms FROM apps WHERE id IN (" + idList + ") ORDER BY " + order + " DESC") if err != nil { + log.Printf("SQL error: Could not query app from collection: %v", err) return nil, err } @@ -100,15 +107,18 @@ func (handler Handler) GetAppsForCollection(collectionID string, sortByPopular b var screenshots_b []byte err = rows.Scan(&app.Id, &app.Name, &app.Type, &app.ThumbsUp, &screenshots_b, &t, &supported_platforms_b) if err != nil { + log.Printf("SQL error: Could not scan app for collection: %v", err) return []RebbleApplication{}, err } - app.Published.Time = time.Unix(0, t) + app.Published = time.Unix(0, t) err = json.Unmarshal(supported_platforms_b, &app.SupportedPlatforms) if err != nil { + log.Printf("Error: Could not unmarshal `supported_platforms`: %v", err) return []RebbleApplication{}, err } err = json.Unmarshal(screenshots_b, &app.Assets.Screenshots) if err != nil { + log.Printf("Error: Could not unmarshal `screenshots`: %v", err) return []RebbleApplication{}, err } apps = append(apps, app) @@ -118,8 +128,9 @@ func (handler Handler) GetAppsForCollection(collectionID string, sortByPopular b // GetCollectionName returns the name of a collection func (handler Handler) GetCollectionName(collectionID string) (string, error) { - rows, err := handler.Query("SELECT name FROM collections WHERE id=?", collectionID) + rows, err := handler.Query("SELECT name FROM collections WHERE id=$1", collectionID) if err != nil { + log.Printf("SQL error: Could not SELECT collection name: %v", err) return "", err } if !rows.Next() { @@ -128,6 +139,7 @@ func (handler Handler) GetCollectionName(collectionID string) (string, error) { var name string err = rows.Scan(&name) if err != nil { + log.Printf("SQL error: Could not scan collection name: %v", err) return "", err } @@ -158,18 +170,17 @@ func (handler Handler) GetAllApps(sortby string, ascending bool, offset int, lim FROM apps JOIN authors ON apps.author_id = authors.id ORDER BY `+orderCol+" "+order+` - LIMIT ? - OFFSET ? + LIMIT $1 + OFFSET $1 `, limit, offset) if err != nil { + log.Printf("SQL error: Could not select all apps: %v", err) return nil, err } apps := make([]RebbleApplication, 0) for rows.Next() { app := RebbleApplication{} - var t_published int64 - err = rows.Scan(&app.Name, &app.Author.Name, &app.Assets.Icon, &app.Id, &app.ThumbsUp, &t_published) - app.Published.Time = time.Unix(0, t_published) + err = rows.Scan(&app.Name, &app.Author.Name, &app.Assets.Icon, &app.Id, &app.ThumbsUp, &app.Published) apps = append(apps, app) } @@ -178,35 +189,38 @@ func (handler Handler) GetAllApps(sortby string, ascending bool, offset int, lim // GetApp returns a specific app func (handler Handler) GetApp(id string) (RebbleApplication, error) { - row := handler.QueryRow("SELECT apps.id, apps.name, apps.author_id, authors.name, apps.tag_ids, apps.description, apps.thumbs_up, apps.type, apps.supported_platforms, apps.published_date, apps.pbw_url, apps.rebble_ready, apps.updated, apps.version, apps.support_url, apps.author_url, apps.source_url, apps.screenshots, apps.banner_url, apps.icon_url, apps.doomsday_backup FROM apps JOIN authors ON apps.author_id = authors.id WHERE apps.id=?", id) + row := handler.QueryRow(` + SELECT apps.id, apps.name, apps.author_id, authors.name, apps.tag_ids, apps.description, apps.thumbs_up, apps.type, apps.supported_platforms, apps.published_date, apps.pbw_url, apps.rebble_ready, apps.updated, apps.version, apps.support_url, apps.author_url, apps.source_url, apps.screenshots, apps.banner_url, apps.icon_url, apps.doomsday_backup FROM apps + JOIN authors ON apps.author_id = authors.id + WHERE apps.id=$1 + `, id) app := RebbleApplication{} var supportedPlatforms_b []byte - var t_published, t_updated int64 var tagIds_b []byte var tagIds []string var screenshots_b []byte var screenshots *([]RebbleScreenshotsPlatform) - err := row.Scan(&app.Id, &app.Name, &app.Author.Id, &app.Author.Name, &tagIds_b, &app.Description, &app.ThumbsUp, &app.Type, &supportedPlatforms_b, &t_published, &app.AppInfo.PbwUrl, &app.AppInfo.RebbleReady, &t_updated, &app.AppInfo.Version, &app.AppInfo.SupportUrl, &app.AppInfo.AuthorUrl, &app.AppInfo.SourceUrl, &screenshots_b, &app.Assets.Banner, &app.Assets.Icon, &app.DoomsdayBackup) + err := row.Scan(&app.Id, &app.Name, &app.Author.Id, &app.Author.Name, &tagIds_b, &app.Description, &app.ThumbsUp, &app.Type, &supportedPlatforms_b, &app.Published, &app.AppInfo.PbwUrl, &app.AppInfo.RebbleReady, &app.AppInfo.Updated, &app.AppInfo.Version, &app.AppInfo.SupportUrl, &app.AppInfo.AuthorUrl, &app.AppInfo.SourceUrl, &screenshots_b, &app.Assets.Banner, &app.Assets.Icon, &app.DoomsdayBackup) if err == sql.ErrNoRows { return RebbleApplication{}, errors.New("No application with this ID") } else if err != nil { + log.Printf("SQL error: Could not SELECT app: %v", err) return RebbleApplication{}, err } json.Unmarshal(supportedPlatforms_b, &app.SupportedPlatforms) - app.Published.Time = time.Unix(0, t_published) - app.AppInfo.Updated.Time = time.Unix(0, t_updated) json.Unmarshal(tagIds_b, &tagIds) app.AppInfo.Tags = make([]RebbleCollection, len(tagIds)) json.Unmarshal(screenshots_b, &screenshots) app.Assets.Screenshots = screenshots for i, tagID := range tagIds { - row := handler.QueryRow("SELECT id, name, color FROM collections WHERE id=?", tagID) + row := handler.QueryRow("SELECT id, name, color FROM collections WHERE id=$1", tagID) err = row.Scan(&app.AppInfo.Tags[i].Id, &app.AppInfo.Tags[i].Name, &app.AppInfo.Tags[i].Color) if err != nil { + log.Printf("SQL error: Could not scan app: %v", err) return RebbleApplication{}, err } } @@ -216,12 +230,14 @@ func (handler Handler) GetApp(id string) (RebbleApplication, error) { // GetAppTags returns the the list of tags of the application with the id `id` func (handler Handler) GetAppTags(id string) ([]RebbleCollection, error) { - rows, err := handler.Query("SELECT apps.tag_ids FROM apps WHERE id=?", id) + rows, err := handler.Query("SELECT apps.tag_ids FROM apps WHERE id=$1", id) if err != nil { + log.Printf("SQL error: Could not SELECT app tags: %v", err) return []RebbleCollection{}, err } exists := rows.Next() if !exists { + log.Printf("Error: Tag `%v` doesn't exist!", id) return []RebbleCollection{}, err } @@ -229,20 +245,23 @@ func (handler Handler) GetAppTags(id string) ([]RebbleCollection, error) { var tagIds []string err = rows.Scan(&tagIds_b) if err != nil { + log.Printf("SQL error: Could not scan app tags: %v", err) return []RebbleCollection{}, err } json.Unmarshal(tagIds_b, &tagIds) collections := make([]RebbleCollection, len(tagIds)) for i, tagId := range tagIds { - rows, err := handler.Query("SELECT id, name, color FROM collections WHERE id=?", tagId) + rows, err := handler.Query("SELECT id, name, color FROM collections WHERE id=$1", tagId) if err != nil { + log.Printf("SQL error: Could not SELECT collection from app tag: %v", err) return []RebbleCollection{}, err } rows.Next() err = rows.Scan(&collections[i].Id, &collections[i].Name, &collections[i].Color) if err != nil { + log.Printf("SQL error: Could not scan collection from app tag: %v", err) return []RebbleCollection{}, err } } @@ -252,8 +271,9 @@ func (handler Handler) GetAppTags(id string) ([]RebbleCollection, error) { // GetAppVersions returns the the list of versions of the application with the id `id` func (handler Handler) GetAppVersions(id string) ([]RebbleVersion, error) { - rows, err := handler.Query("SELECT apps.versions FROM apps WHERE id=?", id) + rows, err := handler.Query("SELECT apps.versions FROM apps WHERE id=$1", id) if err != nil { + log.Printf("SQL error: Could not SELECT app versions: %v", err) return []RebbleVersion{}, err } exists := rows.Next() @@ -265,6 +285,7 @@ func (handler Handler) GetAppVersions(id string) ([]RebbleVersion, error) { var versions []RebbleVersion err = rows.Scan(&versions_b) if err != nil { + log.Printf("SQL error: Could not scan app versions: %v", err) return []RebbleVersion{}, err } json.Unmarshal(versions_b, &versions) @@ -274,8 +295,9 @@ func (handler Handler) GetAppVersions(id string) ([]RebbleVersion, error) { // GetAuthor returns a RebbleAuthor func (handler Handler) GetAuthor(id int) (RebbleAuthor, error) { - rows, err := handler.Query("SELECT authors.name FROM authors WHERE id=?", id) + rows, err := handler.Query("SELECT authors.name FROM authors WHERE id=$1", id) if err != nil { + log.Printf("SQL error: Could not SELECT author: %v", err) return RebbleAuthor{}, err } exists := rows.Next() @@ -288,6 +310,7 @@ func (handler Handler) GetAuthor(id int) (RebbleAuthor, error) { } err = rows.Scan(&author.Name) if err != nil { + log.Printf("SQL error: Could not scan author: %v", err) return RebbleAuthor{}, err } @@ -299,10 +322,11 @@ func (handler Handler) GetAuthorCards(id int) (RebbleCards, error) { rows, err := handler.Query(` SELECT id, name, type, screenshots, thumbs_up FROM apps - WHERE author_id=? + WHERE author_id=$1 ORDER BY published_date ASC `, id) if err != nil { + log.Printf("SQL error: Could not SELECT author cards: %v", err) return RebbleCards{}, err } @@ -318,11 +342,13 @@ func (handler Handler) GetAuthorCards(id int) (RebbleCards, error) { err = rows.Scan(&card.Id, &card.Title, &card.Type, &screenshots_b, &card.ThumbsUp) if err != nil { + log.Printf("SQL error: Could not scan author card: %v", err) return RebbleCards{}, err } err = json.Unmarshal(screenshots_b, &screenshots) if err != nil { + log.Printf("Error: could not unmarshal author card `screenshots`", err) return RebbleCards{}, err } if len(screenshots) != 0 && len(screenshots[0].Screenshots) != 0 { diff --git a/db/types.go b/db/types.go index 432dad4..3a49c63 100644 --- a/db/types.go +++ b/db/types.go @@ -1,30 +1 @@ package db - -import ( - "time" -) - -// JSONTime is a dummy time object that is meant to allow Go's JSON module to -// properly de-serialize the JSON time format. -type JSONTime struct { - time.Time -} - -// UnmarshalJSON allows for the custom time format within the application JSON -// to be decoded into Go's native time format. -func (self *JSONTime) UnmarshalJSON(b []byte) (err error) { - s := string(b) - - // Return an empty time.Time object if it didn't exist in the first place. - if s == "null" { - self.Time = time.Time{} - return - } - - t, err := time.Parse("\"2006-01-02T15:04:05.999Z\"", s) - if err != nil { - t = time.Time{} - } - self.Time = t - return -} diff --git a/main.go b/main.go index ec341d2..98ce111 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "pebble-dev/rebblestore-api/rebbleHandlers" "github.com/gorilla/handlers" - _ "github.com/mattn/go-sqlite3" + _ "github.com/lib/pq" "github.com/pborman/getopt" ) @@ -27,7 +27,7 @@ func main() { return } - database, err := sql.Open("sqlite3", "./RebbleAppStore.db") + database, err := sql.Open("postgres", "user=rebblestore dbname=rebblestore") if err != nil { panic("Could not connect to database" + err.Error()) } diff --git a/main_test.go b/main_test.go index 28347d4..cb6b694 100644 --- a/main_test.go +++ b/main_test.go @@ -11,7 +11,7 @@ import ( "github.com/adams-sarah/test2doc/test" "github.com/gorilla/handlers" "github.com/gorilla/mux" - _ "github.com/mattn/go-sqlite3" + _ "github.com/lib/pq" ) var server *test.Server @@ -19,7 +19,7 @@ var server *test.Server func TestMain(m *testing.M) { var err error - database, err := sql.Open("sqlite3", "./RebbleAppStore.db") + database, err := sql.Open("postgres", "user=rebblestore dbname=rebblestore") if err != nil { panic("Could not connect to database" + err.Error()) } diff --git a/rebbleHandlers/admin.go b/rebbleHandlers/admin.go index 01acb0a..b08c181 100644 --- a/rebbleHandlers/admin.go +++ b/rebbleHandlers/admin.go @@ -49,54 +49,38 @@ func walkFiles(root string) (<-chan string, <-chan error) { // AdminRebuildDBHandler allows an administrator to rebuild the database from // the application directory after hitting a single API end point. func AdminRebuildDBHandler(ctx *HandlerContext, w http.ResponseWriter, r *http.Request) (int, error) { - //w.WriteHeader(418) - //fmt.Fprintf(w, "I'm a teapot!") - /* - w.Header().Add("content-type", "text/html") - path, errc := walkFiles("PebbleAppStore/apps") - for item := range path { - fmt.Fprintf(w, "File: %s
", item) - } - if err := <-errc; err != nil { - log.Fatal(err) - } - /**/ - - //return /* - //db.Close() - dbHandler := ctx.Database - // tag_ids and screenshot_urls are Marshaled arrays, hence the BLOB type. sqlStmt := ` drop table if exists apps; create table apps ( id text not null primary key, name text, author_id integer, - tag_ids blob, + tag_ids text, description text, thumbs_up integer, type text, - supported_platforms blob, - published_date integer, + supported_platforms text, + published_date timestamp, pbw_url text, - rebble_ready integer, - updated integer, + rebble_ready boolean, + updated timestamp, version text, support_url text, author_url text, source_url text, - screenshots blob, + screenshots text, banner_url text, icon_url text, - doomsday_backup integer, - versions blob + doomsday_backup boolean, + versions text ); delete from apps; ` _, err := dbHandler.Exec(sqlStmt) if err != nil { + log.Printf("SQL error: Could not create database `apps`: %v", err) return http.StatusInternalServerError, err } @@ -104,13 +88,14 @@ func AdminRebuildDBHandler(ctx *HandlerContext, w http.ResponseWriter, r *http.R sqlStmt = ` drop table if exists authors; create table authors ( - id text not null primary key, + id integer not null primary key, name text ); delete from authors; ` _, err = dbHandler.Exec(sqlStmt) if err != nil { + log.Printf("SQL error: Could not create database `authors`: %v", err) return http.StatusInternalServerError, fmt.Errorf("%q: %s", err, sqlStmt) } @@ -121,25 +106,28 @@ func AdminRebuildDBHandler(ctx *HandlerContext, w http.ResponseWriter, r *http.R id text not null primary key, name text, color text, - apps blob, - cache_apps_most_popular blob, + apps text, + cache_apps_most_popular text, cache_time integer ); delete from collections; ` _, err = dbHandler.Exec(sqlStmt) if err != nil { + log.Printf("SQL error: Could not create database `collections`: %v", err) return http.StatusInternalServerError, fmt.Errorf("%q: %s", err, sqlStmt) } tx, err := dbHandler.Begin() if err != nil { + log.Printf("SQL error: Could not begin transaction: %v", err) return http.StatusInternalServerError, err } defer tx.Rollback() - stmt, err := tx.Prepare("INSERT INTO apps(id, name, author_id, tag_ids, description, thumbs_up, type, supported_platforms, published_date, pbw_url, rebble_ready, updated, version, support_url, author_url, source_url, screenshots, banner_url, icon_url, doomsday_backup, versions) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") + stmt, err := tx.Prepare("INSERT INTO apps(id, name, author_id, tag_ids, description, thumbs_up, type, supported_platforms, published_date, pbw_url, rebble_ready, updated, version, support_url, author_url, source_url, screenshots, banner_url, icon_url, doomsday_backup, versions) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)") if err != nil { + log.Printf("SQL error: Could not prepare `apps` INSERT: %v", err) return http.StatusInternalServerError, err } defer stmt.Close() @@ -153,6 +141,7 @@ func AdminRebuildDBHandler(ctx *HandlerContext, w http.ResponseWriter, r *http.R for item := range path { app, v, err := parseApp(item, &authors, &lastAuthorId, &collections) if err != nil { + log.Printf("SQL error: Could not parse application from mirror: %v", err) return http.StatusInternalServerError, err } @@ -178,33 +167,40 @@ func AdminRebuildDBHandler(ctx *HandlerContext, w http.ResponseWriter, r *http.R tag_ids_s[0] = app.AppInfo.Tags[0].Id tag_ids, err := json.Marshal(tag_ids_s) if err != nil { + log.Println("Could not marshal `tag_ids_s`") return http.StatusInternalServerError, err } screenshots, err := json.Marshal(app.Assets.Screenshots) if err != nil { + log.Println("Could not marshal `app.Assets.Screenshots`") return http.StatusInternalServerError, err } supported_platforms, err := json.Marshal(app.SupportedPlatforms) if err != nil { + log.Println("Could not marshal `app.SupportedPlatforms`") return http.StatusInternalServerError, err } versions, err := json.Marshal(versions[app.Id]) if err != nil { + log.Println("Could not marshal `versions[app.Id]`") return http.StatusInternalServerError, err } - _, err = stmt.Exec(app.Id, app.Name, app.Author.Id, tag_ids, app.Description, app.ThumbsUp, app.Type, supported_platforms, app.Published.UnixNano(), app.AppInfo.PbwUrl, app.AppInfo.RebbleReady, app.AppInfo.Updated.UnixNano(), app.AppInfo.Version, app.AppInfo.SupportUrl, app.AppInfo.AuthorUrl, app.AppInfo.SourceUrl, screenshots, app.Assets.Banner, app.Assets.Icon, app.DoomsdayBackup, versions) + _, err = stmt.Exec(app.Id, app.Name, app.Author.Id, tag_ids, app.Description, app.ThumbsUp, app.Type, supported_platforms, app.Published, app.AppInfo.PbwUrl, app.AppInfo.RebbleReady, app.AppInfo.Updated, app.AppInfo.Version, app.AppInfo.SupportUrl, app.AppInfo.AuthorUrl, app.AppInfo.SourceUrl, screenshots, app.Assets.Banner, app.Assets.Icon, app.DoomsdayBackup, versions) if err != nil { + log.Printf("SQL error: Could not execute `apps` INSERT: %v", err) return http.StatusInternalServerError, err } } if err := <-errc; err != nil { + print("Error while walking mirror files: %v", err) return http.StatusInternalServerError, err } for author, id := range authors { - _, err = tx.Exec("INSERT INTO authors(id, name) VALUES(?, ?)", id, author) + _, err = tx.Exec("INSERT INTO authors(id, name) VALUES($1, $2)", id, author) if err != nil { + log.Printf("SQL error: Could not execute `authors` INSERT: %v", err) return http.StatusInternalServerError, err } } @@ -221,8 +217,9 @@ func AdminRebuildDBHandler(ctx *HandlerContext, w http.ResponseWriter, r *http.R collectionApps_b, err := json.Marshal(collectionApps) - _, err = tx.Exec("INSERT INTO collections(id, name, color, apps) VALUES(?, ?, ?, ?)", id, collection.Name, collection.Color, collectionApps_b) + _, err = tx.Exec("INSERT INTO collections(id, name, color, apps) VALUES($1, $2, $3, $4)", id, collection.Name, collection.Color, collectionApps_b) if err != nil { + log.Printf("SQL error: Could not execute `collections` INSERT: %v", err) return http.StatusInternalServerError, err } } @@ -342,7 +339,7 @@ func AdminRebuildImagesHandler(ctx *HandlerContext, w http.ResponseWriter, r *ht if err != nil { return http.StatusInternalServerError, err } - _, err = tx.Exec("UPDATE apps SET screenshots=? WHERE id=?", screenshots, id) + _, err = tx.Exec("UPDATE apps SET screenshots=$1 WHERE id=$2", screenshots, id) if err != nil { return http.StatusInternalServerError, err } diff --git a/rebbleHandlers/application.go b/rebbleHandlers/application.go index 4c19182..d953cc9 100644 --- a/rebbleHandlers/application.go +++ b/rebbleHandlers/application.go @@ -9,6 +9,7 @@ import ( "net/http" "os" "strconv" + "time" "pebble-dev/rebblestore-api/db" @@ -39,7 +40,7 @@ type PebbleApplication struct { CategoryName string `json:"category_name"` CategoryColor string `json:"category_color"` Description string `json:"description"` - Published db.JSONTime `json:"published_date"` + Published time.Time `json:"published_date"` Release PebbleApplicationRelease `json:"latest_release"` Website string `json:"website"` Source string `json:"source"` @@ -55,17 +56,17 @@ type PebbleApplication struct { // PebbleApplicationRelease describes the `release` tag of a pebble JSON type PebbleApplicationRelease struct { - Id string `json:"id"` - PbwUrl string `json:"pbw_file"` - Published db.JSONTime `json:"published_date"` - Version string `json:"version"` + Id string `json:"id"` + PbwUrl string `json:"pbw_file"` + Published time.Time `json:"published_date"` + Version string `json:"version"` } // PebbleVersion describes a version change type PebbleVersion struct { - Version string `json:"version"` - Published db.JSONTime `json:"published_date"` - Notes string `json:"release_notes"` + Version string `json:"version"` + Published time.Time `json:"published_date"` + Notes string `json:"release_notes"` } // PebbleCompatibility describes the `compatibility` tag of a pebble JSON