refactor(models): using more organised structure
This commit is contained in:
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -10,11 +11,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||||
"screenmark/screenmark/models"
|
"screenmark/screenmark/models"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
)
|
)
|
||||||
@ -51,11 +54,16 @@ func main() {
|
|||||||
mode := os.Getenv("MODE")
|
mode := os.Getenv("MODE")
|
||||||
log.Printf("Mode: %s\n", mode)
|
log.Printf("Mode: %s\n", mode)
|
||||||
|
|
||||||
err = models.InitDatabase()
|
db, err := models.InitDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageModel := models.NewImageModel(db)
|
||||||
|
linkModel := models.NewLinkModel(db)
|
||||||
|
tagModel := models.NewTagModel(db)
|
||||||
|
textModel := models.NewTextModel(db)
|
||||||
|
|
||||||
listener := pq.NewListener(os.Getenv("DB_CONNECTION"), time.Second, time.Second, func(event pq.ListenerEventType, err error) {
|
listener := pq.NewListener(os.Getenv("DB_CONNECTION"), time.Second, time.Second, func(event pq.ListenerEventType, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -73,9 +81,9 @@ func main() {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case parameters := <-listener.Notify:
|
case parameters := <-listener.Notify:
|
||||||
imageId := parameters.Extra
|
imageId := uuid.MustParse(parameters.Extra)
|
||||||
|
|
||||||
log.Println("received notification, new image available: " + imageId)
|
ctx := context.Background()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
openAiClient, err := GetAiClient()
|
openAiClient, err := GetAiClient()
|
||||||
@ -83,46 +91,34 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
image, err := models.GetImageToProcessWithData(imageId)
|
image, err := imageModel.GetToProcessWithData(ctx, imageId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("1")
|
|
||||||
log.Println(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
imageInfo, err := openAiClient.GetImageInfo(image.Image.ImageName, image.Image.Image)
|
imageInfo, err := openAiClient.GetImageInfo(image.Image.ImageName, image.Image.Image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("2")
|
|
||||||
log.Println(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
savedImage, err := models.SaveImage(image.ID)
|
savedImage, err := imageModel.FinishProcessing(ctx, image.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("3")
|
|
||||||
log.Println(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Finished processing image " + imageId)
|
err = tagModel.SaveToImage(ctx, savedImage.ID, imageInfo.Tags)
|
||||||
log.Printf("Image attributes: %+v\n", imageInfo)
|
|
||||||
|
|
||||||
_, err = models.SaveImageTags(savedImage.ID.String(), imageInfo.Tags)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("1")
|
return
|
||||||
log.Println(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = models.SaveImageLinks(savedImage.ID.String(), imageInfo.Links)
|
err = linkModel.Save(ctx, savedImage.ID, imageInfo.Links)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("2")
|
return
|
||||||
log.Println(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = models.SaveImageTexts(savedImage.ID.String(), imageInfo.Text)
|
err = textModel.Save(ctx, savedImage.ID, imageInfo.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("3")
|
return
|
||||||
log.Println(err)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@ -153,7 +149,7 @@ func main() {
|
|||||||
w.Header().Add("Access-Control-Allow-Credentials", "*")
|
w.Header().Add("Access-Control-Allow-Credentials", "*")
|
||||||
w.Header().Add("Access-Control-Allow-Headers", "*")
|
w.Header().Add("Access-Control-Allow-Headers", "*")
|
||||||
|
|
||||||
images, err := models.GetUserImages(userId)
|
images, err := imageModel.ListWithProperties(r.Context(), uuid.MustParse(userId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
@ -180,7 +176,7 @@ func main() {
|
|||||||
w.Header().Add("Access-Control-Allow-Headers", "*")
|
w.Header().Add("Access-Control-Allow-Headers", "*")
|
||||||
|
|
||||||
// TODO: really need authorization here!
|
// TODO: really need authorization here!
|
||||||
image, err := models.GetImage(imageId)
|
image, err := imageModel.Get(r.Context(), uuid.MustParse(imageId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
@ -259,7 +255,10 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userImage, err := models.SaveImageToProcess(userId, imageName, image)
|
userImage, err := imageModel.Process(r.Context(), uuid.MustParse(userId), model.Image{
|
||||||
|
Image: image,
|
||||||
|
ImageName: imageName,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Second case")
|
log.Println("Second case")
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
@ -8,18 +8,12 @@ import (
|
|||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
var db *sql.DB
|
func InitDatabase() (*sql.DB, error) {
|
||||||
|
|
||||||
func InitDatabase() error {
|
|
||||||
connection := os.Getenv("DB_CONNECTION")
|
connection := os.Getenv("DB_CONNECTION")
|
||||||
|
|
||||||
if len(connection) == 0 {
|
if len(connection) == 0 {
|
||||||
return errors.New("DB_CONNECTION env was not found.")
|
return nil, errors.New("DB_CONNECTION env was not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := sql.Open("postgres", connection)
|
return sql.Open("postgres", connection)
|
||||||
|
|
||||||
db = database
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||||
@ -11,76 +13,8 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SaveImageToProcess(userId string, imageName string, imageData []byte) (model.UserImagesToProcess, error) {
|
type ImageModel struct {
|
||||||
insertImageStmt := Image.INSERT(Image.ImageName, Image.Image).VALUES(imageName, imageData).RETURNING(Image.ID)
|
dbPool *sql.DB
|
||||||
|
|
||||||
// TODO: should be a transaction
|
|
||||||
|
|
||||||
image := model.Image{}
|
|
||||||
err := insertImageStmt.Query(db, &image)
|
|
||||||
if err != nil {
|
|
||||||
return model.UserImagesToProcess{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt := UserImagesToProcess.INSERT(UserImagesToProcess.UserID, UserImagesToProcess.ImageID).VALUES(userId, image.ID).RETURNING(UserImagesToProcess.AllColumns)
|
|
||||||
|
|
||||||
fmt.Println(stmt.DebugSql())
|
|
||||||
|
|
||||||
userImage := model.UserImagesToProcess{}
|
|
||||||
err = stmt.Query(db, &userImage)
|
|
||||||
|
|
||||||
return userImage, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeImageToProcess(imageId string) error {
|
|
||||||
id := uuid.MustParse(imageId)
|
|
||||||
|
|
||||||
stmt := UserImagesToProcess.DELETE().WHERE(UserImagesToProcess.ID.EQ(UUID(id)))
|
|
||||||
|
|
||||||
_, err := stmt.Exec(db)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUserId(imageId uuid.UUID) (uuid.UUID, error) {
|
|
||||||
stmt := UserImages.SELECT(UserImages.UserID).WHERE(UserImages.ID.EQ(UUID(imageId)))
|
|
||||||
|
|
||||||
fmt.Println(stmt.DebugSql())
|
|
||||||
|
|
||||||
userIds := make([]string, 0)
|
|
||||||
|
|
||||||
err := stmt.Query(db, &userIds)
|
|
||||||
if err != nil {
|
|
||||||
return uuid.Nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(userIds) != 1 {
|
|
||||||
return uuid.Nil, errors.New("expect only one user id per image id")
|
|
||||||
}
|
|
||||||
|
|
||||||
return uuid.Parse(userIds[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveImage(imageId uuid.UUID) (model.UserImages, error) {
|
|
||||||
imageToProcess, err := GetImageToProcess(imageId.String())
|
|
||||||
if err != nil {
|
|
||||||
return model.UserImages{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt := UserImages.INSERT(UserImages.UserID, UserImages.ImageID).VALUES(imageToProcess.UserID, imageToProcess.ImageID).RETURNING(UserImages.ID, UserImages.UserID, UserImages.ImageID)
|
|
||||||
|
|
||||||
userImage := model.UserImages{}
|
|
||||||
err = stmt.Query(db, &userImage)
|
|
||||||
if err != nil {
|
|
||||||
return model.UserImages{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = removeImageToProcess(imageId.String())
|
|
||||||
if err != nil {
|
|
||||||
return model.UserImages{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return userImage, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageData struct {
|
type ImageData struct {
|
||||||
@ -89,49 +23,62 @@ type ImageData struct {
|
|||||||
Image model.Image
|
Image model.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetImage(imageId string) (ImageData, error) {
|
type ProcessingImageData struct {
|
||||||
id := uuid.MustParse(imageId)
|
|
||||||
stmt := SELECT(UserImages.AllColumns, Image.AllColumns).FROM(UserImages.INNER_JOIN(Image, Image.ID.EQ(UserImages.ImageID))).WHERE(UserImages.ID.EQ(UUID(id)))
|
|
||||||
|
|
||||||
images := []ImageData{}
|
|
||||||
err := stmt.Query(db, &images)
|
|
||||||
|
|
||||||
if len(images) != 1 {
|
|
||||||
return ImageData{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return images[0], err
|
|
||||||
}
|
|
||||||
|
|
||||||
type ImageToProcessData struct {
|
|
||||||
model.UserImagesToProcess
|
model.UserImagesToProcess
|
||||||
|
|
||||||
Image model.Image
|
Image model.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetImageToProcessWithData(imageId string) (ImageToProcessData, error) {
|
type ImageWithProperties struct {
|
||||||
id := uuid.MustParse(imageId)
|
ID uuid.UUID
|
||||||
// stmt := UserImagesToProcess.SELECT(UserImages.AllColumns).WHERE(UserImages.ID.EQ(UUID(id)))
|
|
||||||
|
|
||||||
// TODO: Image should be `Images`
|
Image model.UserImages
|
||||||
stmt := SELECT(UserImagesToProcess.AllColumns, Image.AllColumns).FROM(UserImagesToProcess.INNER_JOIN(Image, Image.ID.EQ(UserImagesToProcess.ImageID))).WHERE(UserImagesToProcess.ID.EQ(UUID(id)))
|
|
||||||
|
|
||||||
images := []ImageToProcessData{}
|
Tags []model.ImageTags
|
||||||
err := stmt.Query(db, &images)
|
Links []model.ImageLinks
|
||||||
|
Text []model.ImageText
|
||||||
if len(images) != 1 {
|
|
||||||
return ImageToProcessData{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return images[0], err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetImageToProcess(imageId string) (model.UserImagesToProcess, error) {
|
func (m ImageModel) Process(ctx context.Context, userId uuid.UUID, image model.Image) (model.UserImagesToProcess, error) {
|
||||||
id := uuid.MustParse(imageId)
|
tx, err := m.dbPool.BeginTx(ctx, nil)
|
||||||
stmt := UserImagesToProcess.SELECT(UserImagesToProcess.AllColumns).WHERE(UserImagesToProcess.ID.EQ(UUID(id)))
|
if err != nil {
|
||||||
|
return model.UserImagesToProcess{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
insertImageStmt := Image.
|
||||||
|
INSERT(Image.ImageName, Image.Image).
|
||||||
|
VALUES(image.ImageName, image.Image).
|
||||||
|
RETURNING(Image.ID)
|
||||||
|
|
||||||
|
insertedImage := model.Image{}
|
||||||
|
err = insertImageStmt.QueryContext(ctx, tx, &insertedImage)
|
||||||
|
if err != nil {
|
||||||
|
return model.UserImagesToProcess{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt := UserImagesToProcess.
|
||||||
|
INSERT(UserImagesToProcess.UserID, UserImagesToProcess.ImageID).
|
||||||
|
VALUES(userId, insertedImage.ID).
|
||||||
|
RETURNING(UserImagesToProcess.AllColumns)
|
||||||
|
|
||||||
|
userImage := model.UserImagesToProcess{}
|
||||||
|
err = stmt.QueryContext(ctx, tx, &userImage)
|
||||||
|
if err != nil {
|
||||||
|
return model.UserImagesToProcess{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
|
||||||
|
return userImage, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ImageModel) GetToProcess(ctx context.Context, imageId uuid.UUID) (model.UserImagesToProcess, error) {
|
||||||
|
getToProcessStmt := UserImagesToProcess.
|
||||||
|
SELECT(UserImagesToProcess.AllColumns).
|
||||||
|
WHERE(UserImagesToProcess.ID.EQ(UUID(imageId)))
|
||||||
|
|
||||||
images := []model.UserImagesToProcess{}
|
images := []model.UserImagesToProcess{}
|
||||||
err := stmt.Query(db, &images)
|
err := getToProcessStmt.QueryContext(ctx, m.dbPool, &images)
|
||||||
|
|
||||||
if len(images) != 1 {
|
if len(images) != 1 {
|
||||||
return model.UserImagesToProcess{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
return model.UserImagesToProcess{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
||||||
@ -140,97 +87,94 @@ func GetImageToProcess(imageId string) (model.UserImagesToProcess, error) {
|
|||||||
return images[0], err
|
return images[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserImagesWithInfo struct {
|
func (m ImageModel) GetToProcessWithData(ctx context.Context, imageId uuid.UUID) (ProcessingImageData, error) {
|
||||||
ID uuid.UUID
|
stmt := SELECT(UserImagesToProcess.AllColumns, Image.AllColumns).
|
||||||
|
FROM(
|
||||||
|
UserImagesToProcess.INNER_JOIN(
|
||||||
|
Image, Image.ID.EQ(UserImagesToProcess.ImageID),
|
||||||
|
),
|
||||||
|
).WHERE(UserImagesToProcess.ID.EQ(UUID(imageId)))
|
||||||
|
|
||||||
// TODO: this shit
|
images := []ProcessingImageData{}
|
||||||
Image model.Image
|
err := stmt.QueryContext(ctx, m.dbPool, &images)
|
||||||
|
|
||||||
Tags []model.ImageTags
|
if len(images) != 1 {
|
||||||
Links []model.ImageLinks
|
return ProcessingImageData{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
||||||
Text []model.ImageText
|
}
|
||||||
|
|
||||||
|
return images[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserImages(userId string) ([]UserImagesWithInfo, error) {
|
func (m ImageModel) FinishProcessing(ctx context.Context, imageId uuid.UUID) (model.UserImages, error) {
|
||||||
id := uuid.MustParse(userId)
|
imageToProcess, err := m.GetToProcess(ctx, imageId)
|
||||||
stmt := SELECT(UserImages.ID.AS("UserImagesWithInfo.ID"), Image.ID, Image.ImageName, ImageTags.AllColumns, ImageText.AllColumns, ImageLinks.AllColumns).FROM(UserImages.INNER_JOIN(Image, Image.ID.EQ(UserImages.ImageID)).LEFT_JOIN(ImageTags, ImageTags.ImageID.EQ(UserImages.ID)).LEFT_JOIN(ImageText, ImageText.ImageID.EQ(UserImages.ID)).LEFT_JOIN(ImageLinks, ImageLinks.ImageID.EQ(UserImages.ID))).WHERE(UserImages.UserID.EQ(UUID(id)))
|
if err != nil {
|
||||||
|
return model.UserImages{}, err
|
||||||
|
}
|
||||||
|
|
||||||
images := []UserImagesWithInfo{}
|
tx, err := m.dbPool.Begin()
|
||||||
err := stmt.Query(db, &images)
|
if err != nil {
|
||||||
|
return model.UserImages{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
insertImageStmt := UserImages.
|
||||||
|
INSERT(UserImages.UserID, UserImages.ImageID).
|
||||||
|
VALUES(imageToProcess.UserID, imageToProcess.ImageID).
|
||||||
|
RETURNING(UserImages.ID, UserImages.UserID, UserImages.ImageID)
|
||||||
|
|
||||||
|
userImage := model.UserImages{}
|
||||||
|
err = insertImageStmt.QueryContext(ctx, tx, &userImage)
|
||||||
|
if err != nil {
|
||||||
|
return model.UserImages{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
removeProcessingStmt := UserImagesToProcess.
|
||||||
|
DELETE().
|
||||||
|
WHERE(UserImagesToProcess.ID.EQ(UUID(imageToProcess.ID)))
|
||||||
|
|
||||||
|
_, err = removeProcessingStmt.ExecContext(ctx, tx)
|
||||||
|
|
||||||
|
return userImage, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ImageModel) Get(ctx context.Context, imageId uuid.UUID) (ImageData, error) {
|
||||||
|
getImageStmt := SELECT(UserImages.AllColumns, Image.AllColumns).
|
||||||
|
FROM(
|
||||||
|
UserImages.INNER_JOIN(Image, Image.ID.EQ(UserImages.ImageID)),
|
||||||
|
).
|
||||||
|
WHERE(UserImages.ID.EQ(UUID(imageId)))
|
||||||
|
|
||||||
|
images := []ImageData{}
|
||||||
|
err := getImageStmt.QueryContext(ctx, m.dbPool, &images)
|
||||||
|
|
||||||
|
if len(images) != 1 {
|
||||||
|
return ImageData{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return images[0], err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move this to `user.go` model file
|
||||||
|
func (m ImageModel) ListWithProperties(ctx context.Context, userId uuid.UUID) ([]ImageWithProperties, error) {
|
||||||
|
listWithPropertiesStmt := SELECT(
|
||||||
|
UserImages.ID.AS("UserImagesWithInfo.ID"),
|
||||||
|
Image.ID,
|
||||||
|
Image.ImageName,
|
||||||
|
ImageTags.AllColumns,
|
||||||
|
ImageText.AllColumns,
|
||||||
|
ImageLinks.AllColumns).
|
||||||
|
FROM(
|
||||||
|
UserImages.INNER_JOIN(Image, Image.ID.EQ(UserImages.ImageID)).
|
||||||
|
LEFT_JOIN(ImageTags, ImageTags.ImageID.EQ(UserImages.ID)).
|
||||||
|
LEFT_JOIN(ImageText, ImageText.ImageID.EQ(UserImages.ID)).
|
||||||
|
LEFT_JOIN(ImageLinks, ImageLinks.ImageID.EQ(UserImages.ID))).
|
||||||
|
WHERE(UserImages.UserID.EQ(UUID(userId)))
|
||||||
|
|
||||||
|
images := []ImageWithProperties{}
|
||||||
|
err := listWithPropertiesStmt.QueryContext(ctx, m.dbPool, &images)
|
||||||
|
|
||||||
return images, err
|
return images, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveImageTags(imageId string, tags []string) ([]model.ImageTags, error) {
|
func NewImageModel(db *sql.DB) ImageModel {
|
||||||
id := uuid.MustParse(imageId)
|
return ImageModel{dbPool: db}
|
||||||
|
|
||||||
userId, err := getUserId(id)
|
|
||||||
if err != nil {
|
|
||||||
return []model.ImageTags{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = CreateTags(userId, tags)
|
|
||||||
if err != nil {
|
|
||||||
return []model.ImageTags{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userTagsExpression := make([]Expression, 0)
|
|
||||||
for _, tag := range tags {
|
|
||||||
userTagsExpression = append(userTagsExpression, String(tag))
|
|
||||||
}
|
|
||||||
|
|
||||||
userTags := make([]model.UserTags, 0)
|
|
||||||
|
|
||||||
getTagsStmt := UserTags.SELECT(UserTags.ID, UserTags.Tag).WHERE(UserTags.Tag.IN(userTagsExpression...))
|
|
||||||
err = getTagsStmt.Query(db, &userTags)
|
|
||||||
if err != nil {
|
|
||||||
return []model.ImageTags{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt := ImageTags.INSERT(ImageTags.ImageID, ImageTags.TagID)
|
|
||||||
|
|
||||||
for _, t := range userTags {
|
|
||||||
stmt = stmt.VALUES(id, t.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt.RETURNING(ImageTags.AllColumns)
|
|
||||||
|
|
||||||
imageTags := make([]model.ImageTags, 0)
|
|
||||||
err = stmt.Query(db, &imageTags)
|
|
||||||
|
|
||||||
return imageTags, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveImageLinks(imageId string, links []string) ([]model.ImageLinks, error) {
|
|
||||||
id := uuid.MustParse(imageId)
|
|
||||||
|
|
||||||
stmt := ImageLinks.INSERT(ImageLinks.ImageID, ImageLinks.Link)
|
|
||||||
|
|
||||||
for _, t := range links {
|
|
||||||
stmt = stmt.VALUES(id, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt.RETURNING(ImageLinks.AllColumns)
|
|
||||||
|
|
||||||
imageLinks := []model.ImageLinks{}
|
|
||||||
err := stmt.Query(db, &imageLinks)
|
|
||||||
|
|
||||||
return imageLinks, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveImageTexts(imageId string, texts []string) ([]model.ImageText, error) {
|
|
||||||
id := uuid.MustParse(imageId)
|
|
||||||
|
|
||||||
stmt := ImageText.INSERT(ImageText.ImageID, ImageText.ImageText)
|
|
||||||
|
|
||||||
for _, t := range texts {
|
|
||||||
stmt = stmt.VALUES(id, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt.RETURNING(ImageText.AllColumns)
|
|
||||||
|
|
||||||
imageTags := []model.ImageText{}
|
|
||||||
err := stmt.Query(db, &imageTags)
|
|
||||||
|
|
||||||
return imageTags, err
|
|
||||||
}
|
}
|
||||||
|
29
backend/models/links.go
Normal file
29
backend/models/links.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinkModel struct {
|
||||||
|
dbPool *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m LinkModel) Save(ctx context.Context, imageId uuid.UUID, links []string) error {
|
||||||
|
stmt := ImageLinks.INSERT(ImageLinks.ImageID, ImageLinks.Link)
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
stmt = stmt.VALUES(imageId, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := stmt.ExecContext(ctx, m.dbPool)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinkModel(db *sql.DB) LinkModel {
|
||||||
|
return LinkModel{dbPool: db}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||||
@ -9,6 +11,10 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TagModel struct {
|
||||||
|
dbPool *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
// Raw dogging SQL is kinda based though?
|
// Raw dogging SQL is kinda based though?
|
||||||
//
|
//
|
||||||
// | nO, usE OrM!!
|
// | nO, usE OrM!!
|
||||||
@ -20,7 +26,7 @@ import (
|
|||||||
// | -- --
|
// | -- --
|
||||||
// | -- --
|
// | -- --
|
||||||
// | ---- IQ ----
|
// | ---- IQ ----
|
||||||
func getNonExistantTags(userId uuid.UUID, tags []string) ([]string, error) {
|
func (m TagModel) getNonExistantTags(ctx context.Context, userId uuid.UUID, tags []string) ([]string, error) {
|
||||||
values := ""
|
values := ""
|
||||||
counter := 1
|
counter := 1
|
||||||
// big big SQL injection problem here?
|
// big big SQL injection problem here?
|
||||||
@ -29,20 +35,7 @@ func getNonExistantTags(userId uuid.UUID, tags []string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
values = values[0 : len(values)-1]
|
values = values[0 : len(values)-1]
|
||||||
|
|
||||||
/*
|
getNonExistingTags := fmt.Sprintf(`WITH given_tags
|
||||||
WITH given_tags
|
|
||||||
AS (SELECT given_tags.tag FROM (VALUES ('c')) AS given_tags (tag)),
|
|
||||||
this_user_tags as (
|
|
||||||
SELECT id, tag
|
|
||||||
FROM haystack.user_tags
|
|
||||||
where user_tags.user_id = 'fcc22dbb-7792-4595-be8e-d0439e13990a'
|
|
||||||
)
|
|
||||||
select given_tags.tag from given_tags
|
|
||||||
LEFT OUTER JOIN this_user_tags ON this_user_tags.tag = given_tags.tag
|
|
||||||
where this_user_tags.tag is null;
|
|
||||||
*/
|
|
||||||
|
|
||||||
withStuff := fmt.Sprintf(`WITH given_tags
|
|
||||||
AS (SELECT given_tags.tag FROM (VALUES `+values+`) AS given_tags (tag)),
|
AS (SELECT given_tags.tag FROM (VALUES `+values+`) AS given_tags (tag)),
|
||||||
this_user_tags AS
|
this_user_tags AS
|
||||||
(SELECT id, tag FROM haystack.user_tags WHERE user_tags.user_id = $%d)
|
(SELECT id, tag FROM haystack.user_tags WHERE user_tags.user_id = $%d)
|
||||||
@ -51,23 +44,20 @@ func getNonExistantTags(userId uuid.UUID, tags []string) ([]string, error) {
|
|||||||
LEFT OUTER JOIN haystack.user_tags ON haystack.user_tags.tag = given_tags.tag
|
LEFT OUTER JOIN haystack.user_tags ON haystack.user_tags.tag = given_tags.tag
|
||||||
where user_tags.tag is null`, counter)
|
where user_tags.tag is null`, counter)
|
||||||
|
|
||||||
stmt, err := db.Prepare(withStuff)
|
getNonExistingTagsStmt, err := m.dbPool.PrepareContext(ctx, getNonExistingTags)
|
||||||
fmt.Println(withStuff)
|
defer getNonExistingTagsStmt.Close()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("failing to prepare stmt")
|
|
||||||
return []string{}, err
|
return []string{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
args := make([]any, counter)
|
args := make([]any, counter)
|
||||||
for i, v := range tags {
|
for i, v := range tags {
|
||||||
args[i] = v
|
args[i] = v
|
||||||
}
|
}
|
||||||
args[counter-1] = userId.String()
|
args[counter-1] = userId.String()
|
||||||
|
|
||||||
rows, err := stmt.Query(args...)
|
rows, err := getNonExistingTagsStmt.QueryContext(ctx, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, err
|
return []string{}, err
|
||||||
}
|
}
|
||||||
@ -84,8 +74,8 @@ func getNonExistantTags(userId uuid.UUID, tags []string) ([]string, error) {
|
|||||||
return nonExistantTags, nil
|
return nonExistantTags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateTags(userId uuid.UUID, tags []string) error {
|
func (m TagModel) Save(ctx context.Context, userId uuid.UUID, tags []string) error {
|
||||||
tagsToInsert, err := getNonExistantTags(userId, tags)
|
tagsToInsert, err := m.getNonExistantTags(ctx, userId, tags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -100,17 +90,58 @@ func CreateTags(userId uuid.UUID, tags []string) error {
|
|||||||
stmt = stmt.VALUES(UUID(userId), tag)
|
stmt = stmt.VALUES(UUID(userId), tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = stmt.Exec(db)
|
_, err = stmt.ExecContext(ctx, m.dbPool)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTags(userId uuid.UUID) ([]model.UserTags, error) {
|
func (m TagModel) List(ctx context.Context, userId uuid.UUID) ([]model.UserTags, error) {
|
||||||
stmt := UserTags.SELECT(UserTags.AllColumns).WHERE(UserTags.UserID.EQ(UUID(userId)))
|
listTagsStmt := UserTags.SELECT(UserTags.AllColumns).WHERE(UserTags.UserID.EQ(UUID(userId)))
|
||||||
|
|
||||||
userTags := []model.UserTags{}
|
userTags := []model.UserTags{}
|
||||||
|
|
||||||
err := stmt.Query(db, &userTags)
|
err := listTagsStmt.QueryContext(ctx, m.dbPool, &userTags)
|
||||||
|
|
||||||
return userTags, err
|
return userTags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m TagModel) SaveToImage(ctx context.Context, imageId uuid.UUID, tags []string) error {
|
||||||
|
userId, err := getUserIdFromImage(ctx, m.dbPool, imageId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Save(ctx, userId, tags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userTagsExpression := make([]Expression, 0)
|
||||||
|
for _, tag := range tags {
|
||||||
|
userTagsExpression = append(userTagsExpression, String(tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
userTags := make([]model.UserTags, 0)
|
||||||
|
|
||||||
|
getTagsStmt := UserTags.SELECT(
|
||||||
|
UserTags.ID, UserTags.Tag,
|
||||||
|
).WHERE(UserTags.Tag.IN(userTagsExpression...))
|
||||||
|
err = getTagsStmt.Query(m.dbPool, &userTags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt := ImageTags.INSERT(ImageTags.ImageID, ImageTags.TagID)
|
||||||
|
|
||||||
|
for _, t := range userTags {
|
||||||
|
stmt = stmt.VALUES(imageId, t.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.ExecContext(ctx, m.dbPool)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTagModel(db *sql.DB) TagModel {
|
||||||
|
return TagModel{dbPool: db}
|
||||||
|
}
|
||||||
|
31
backend/models/text.go
Normal file
31
backend/models/text.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TextModel struct {
|
||||||
|
dbPool *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m TextModel) Save(ctx context.Context, imageId uuid.UUID, texts []string) error {
|
||||||
|
saveImageTextStmt := ImageText.INSERT(ImageText.ImageID, ImageText.ImageText)
|
||||||
|
|
||||||
|
for _, t := range texts {
|
||||||
|
saveImageTextStmt = saveImageTextStmt.VALUES(imageId, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
saveImageTextStmt.RETURNING(ImageText.AllColumns)
|
||||||
|
|
||||||
|
_, err := saveImageTextStmt.ExecContext(ctx, m.dbPool)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTextModel(db *sql.DB) TextModel {
|
||||||
|
return TextModel{dbPool: db}
|
||||||
|
}
|
32
backend/models/user.go
Normal file
32
backend/models/user.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||||
|
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||||
|
|
||||||
|
. "github.com/go-jet/jet/v2/postgres"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserModek struct {
|
||||||
|
dbPool *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserIdFromImage(ctx context.Context, dbPool *sql.DB, imageId uuid.UUID) (uuid.UUID, error) {
|
||||||
|
getUserIdStmt := UserImages.SELECT(UserImages.UserID).WHERE(UserImages.ID.EQ(UUID(imageId)))
|
||||||
|
|
||||||
|
user := []model.Users{}
|
||||||
|
err := getUserIdStmt.QueryContext(ctx, dbPool, &user)
|
||||||
|
if err != nil {
|
||||||
|
return uuid.Nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(user) != 1 {
|
||||||
|
return uuid.Nil, errors.New("Expected exactly one choice.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user[0].ID, nil
|
||||||
|
}
|
@ -82,8 +82,8 @@ func TestMessageBuilderImage(t *testing.T) {
|
|||||||
base64data := base64.StdEncoding.EncodeToString(data)
|
base64data := base64.StdEncoding.EncodeToString(data)
|
||||||
url := fmt.Sprintf("data:image/%s;base64,%s", "png", base64data)
|
url := fmt.Sprintf("data:image/%s;base64,%s", "png", base64data)
|
||||||
|
|
||||||
if imageContent.ImageUrl.Url != url {
|
if imageContent.ImageUrl != url {
|
||||||
t.Logf("Expected %s, but got %s.\n", url, imageContent.ImageUrl.Url)
|
t.Logf("Expected %s, but got %s.\n", url, imageContent.ImageUrl)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user