Merge branch 'main' of https://github.com/dimuuu/haystack-app
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,2 +1,5 @@
|
||||
.env
|
||||
db
|
||||
screenmark
|
||||
node_modules
|
||||
dist
|
||||
dist
|
||||
|
18
backend/.gen/haystack/haystack/model/image_links.go
Normal file
18
backend/.gen/haystack/haystack/model/image_links.go
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ImageLinks struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
Link string
|
||||
ImageID uuid.UUID
|
||||
}
|
17
backend/.gen/haystack/haystack/model/image_tags.go
Normal file
17
backend/.gen/haystack/haystack/model/image_tags.go
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ImageTags struct {
|
||||
TagID uuid.UUID
|
||||
ImageID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/image_text.go
Normal file
18
backend/.gen/haystack/haystack/model/image_text.go
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ImageText struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
ImageText string
|
||||
ImageID uuid.UUID
|
||||
}
|
19
backend/.gen/haystack/haystack/model/user_images.go
Normal file
19
backend/.gen/haystack/haystack/model/user_images.go
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UserImages struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
ImageName string
|
||||
Image []byte
|
||||
UserID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/user_tags.go
Normal file
18
backend/.gen/haystack/haystack/model/user_tags.go
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UserTags struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
Tag string
|
||||
UserID uuid.UUID
|
||||
}
|
16
backend/.gen/haystack/haystack/model/users.go
Normal file
16
backend/.gen/haystack/haystack/model/users.go
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Users struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image_links.go
Normal file
81
backend/.gen/haystack/haystack/table/image_links.go
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
var ImageLinks = newImageLinksTable("haystack", "image_links", "")
|
||||
|
||||
type imageLinksTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
Link postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageLinksTable struct {
|
||||
imageLinksTable
|
||||
|
||||
EXCLUDED imageLinksTable
|
||||
}
|
||||
|
||||
// AS creates new ImageLinksTable with assigned alias
|
||||
func (a ImageLinksTable) AS(alias string) *ImageLinksTable {
|
||||
return newImageLinksTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageLinksTable with assigned schema name
|
||||
func (a ImageLinksTable) FromSchema(schemaName string) *ImageLinksTable {
|
||||
return newImageLinksTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageLinksTable with assigned table prefix
|
||||
func (a ImageLinksTable) WithPrefix(prefix string) *ImageLinksTable {
|
||||
return newImageLinksTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageLinksTable with assigned table suffix
|
||||
func (a ImageLinksTable) WithSuffix(suffix string) *ImageLinksTable {
|
||||
return newImageLinksTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageLinksTable(schemaName, tableName, alias string) *ImageLinksTable {
|
||||
return &ImageLinksTable{
|
||||
imageLinksTable: newImageLinksTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageLinksTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageLinksTableImpl(schemaName, tableName, alias string) imageLinksTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
LinkColumn = postgres.StringColumn("link")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, LinkColumn, ImageIDColumn}
|
||||
mutableColumns = postgres.ColumnList{LinkColumn, ImageIDColumn}
|
||||
)
|
||||
|
||||
return imageLinksTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
Link: LinkColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
78
backend/.gen/haystack/haystack/table/image_tags.go
Normal file
78
backend/.gen/haystack/haystack/table/image_tags.go
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
var ImageTags = newImageTagsTable("haystack", "image_tags", "")
|
||||
|
||||
type imageTagsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
TagID postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageTagsTable struct {
|
||||
imageTagsTable
|
||||
|
||||
EXCLUDED imageTagsTable
|
||||
}
|
||||
|
||||
// AS creates new ImageTagsTable with assigned alias
|
||||
func (a ImageTagsTable) AS(alias string) *ImageTagsTable {
|
||||
return newImageTagsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageTagsTable with assigned schema name
|
||||
func (a ImageTagsTable) FromSchema(schemaName string) *ImageTagsTable {
|
||||
return newImageTagsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageTagsTable with assigned table prefix
|
||||
func (a ImageTagsTable) WithPrefix(prefix string) *ImageTagsTable {
|
||||
return newImageTagsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageTagsTable with assigned table suffix
|
||||
func (a ImageTagsTable) WithSuffix(suffix string) *ImageTagsTable {
|
||||
return newImageTagsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageTagsTable(schemaName, tableName, alias string) *ImageTagsTable {
|
||||
return &ImageTagsTable{
|
||||
imageTagsTable: newImageTagsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageTagsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageTagsTableImpl(schemaName, tableName, alias string) imageTagsTable {
|
||||
var (
|
||||
TagIDColumn = postgres.StringColumn("tag_id")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
allColumns = postgres.ColumnList{TagIDColumn, ImageIDColumn}
|
||||
mutableColumns = postgres.ColumnList{TagIDColumn, ImageIDColumn}
|
||||
)
|
||||
|
||||
return imageTagsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
TagID: TagIDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image_text.go
Normal file
81
backend/.gen/haystack/haystack/table/image_text.go
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
var ImageText = newImageTextTable("haystack", "image_text", "")
|
||||
|
||||
type imageTextTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
ImageText postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageTextTable struct {
|
||||
imageTextTable
|
||||
|
||||
EXCLUDED imageTextTable
|
||||
}
|
||||
|
||||
// AS creates new ImageTextTable with assigned alias
|
||||
func (a ImageTextTable) AS(alias string) *ImageTextTable {
|
||||
return newImageTextTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageTextTable with assigned schema name
|
||||
func (a ImageTextTable) FromSchema(schemaName string) *ImageTextTable {
|
||||
return newImageTextTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageTextTable with assigned table prefix
|
||||
func (a ImageTextTable) WithPrefix(prefix string) *ImageTextTable {
|
||||
return newImageTextTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageTextTable with assigned table suffix
|
||||
func (a ImageTextTable) WithSuffix(suffix string) *ImageTextTable {
|
||||
return newImageTextTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageTextTable(schemaName, tableName, alias string) *ImageTextTable {
|
||||
return &ImageTextTable{
|
||||
imageTextTable: newImageTextTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageTextTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageTextTableImpl(schemaName, tableName, alias string) imageTextTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
ImageTextColumn = postgres.StringColumn("image_text")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, ImageTextColumn, ImageIDColumn}
|
||||
mutableColumns = postgres.ColumnList{ImageTextColumn, ImageIDColumn}
|
||||
)
|
||||
|
||||
return imageTextTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
ImageText: ImageTextColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
19
backend/.gen/haystack/haystack/table/table_use_schema.go
Normal file
19
backend/.gen/haystack/haystack/table/table_use_schema.go
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
// UseSchema sets a new schema name for all generated table SQL builder types. It is recommended to invoke
|
||||
// this method only once at the beginning of the program.
|
||||
func UseSchema(schema string) {
|
||||
ImageLinks = ImageLinks.FromSchema(schema)
|
||||
ImageTags = ImageTags.FromSchema(schema)
|
||||
ImageText = ImageText.FromSchema(schema)
|
||||
UserImages = UserImages.FromSchema(schema)
|
||||
UserTags = UserTags.FromSchema(schema)
|
||||
Users = Users.FromSchema(schema)
|
||||
}
|
84
backend/.gen/haystack/haystack/table/user_images.go
Normal file
84
backend/.gen/haystack/haystack/table/user_images.go
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
var UserImages = newUserImagesTable("haystack", "user_images", "")
|
||||
|
||||
type userImagesTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
ImageName postgres.ColumnString
|
||||
Image postgres.ColumnString
|
||||
UserID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UserImagesTable struct {
|
||||
userImagesTable
|
||||
|
||||
EXCLUDED userImagesTable
|
||||
}
|
||||
|
||||
// AS creates new UserImagesTable with assigned alias
|
||||
func (a UserImagesTable) AS(alias string) *UserImagesTable {
|
||||
return newUserImagesTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UserImagesTable with assigned schema name
|
||||
func (a UserImagesTable) FromSchema(schemaName string) *UserImagesTable {
|
||||
return newUserImagesTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UserImagesTable with assigned table prefix
|
||||
func (a UserImagesTable) WithPrefix(prefix string) *UserImagesTable {
|
||||
return newUserImagesTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UserImagesTable with assigned table suffix
|
||||
func (a UserImagesTable) WithSuffix(suffix string) *UserImagesTable {
|
||||
return newUserImagesTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUserImagesTable(schemaName, tableName, alias string) *UserImagesTable {
|
||||
return &UserImagesTable{
|
||||
userImagesTable: newUserImagesTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUserImagesTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUserImagesTableImpl(schemaName, tableName, alias string) userImagesTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
ImageNameColumn = postgres.StringColumn("image_name")
|
||||
ImageColumn = postgres.StringColumn("image")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, ImageNameColumn, ImageColumn, UserIDColumn}
|
||||
mutableColumns = postgres.ColumnList{ImageNameColumn, ImageColumn, UserIDColumn}
|
||||
)
|
||||
|
||||
return userImagesTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
ImageName: ImageNameColumn,
|
||||
Image: ImageColumn,
|
||||
UserID: UserIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/user_tags.go
Normal file
81
backend/.gen/haystack/haystack/table/user_tags.go
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
var UserTags = newUserTagsTable("haystack", "user_tags", "")
|
||||
|
||||
type userTagsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
Tag postgres.ColumnString
|
||||
UserID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UserTagsTable struct {
|
||||
userTagsTable
|
||||
|
||||
EXCLUDED userTagsTable
|
||||
}
|
||||
|
||||
// AS creates new UserTagsTable with assigned alias
|
||||
func (a UserTagsTable) AS(alias string) *UserTagsTable {
|
||||
return newUserTagsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UserTagsTable with assigned schema name
|
||||
func (a UserTagsTable) FromSchema(schemaName string) *UserTagsTable {
|
||||
return newUserTagsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UserTagsTable with assigned table prefix
|
||||
func (a UserTagsTable) WithPrefix(prefix string) *UserTagsTable {
|
||||
return newUserTagsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UserTagsTable with assigned table suffix
|
||||
func (a UserTagsTable) WithSuffix(suffix string) *UserTagsTable {
|
||||
return newUserTagsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUserTagsTable(schemaName, tableName, alias string) *UserTagsTable {
|
||||
return &UserTagsTable{
|
||||
userTagsTable: newUserTagsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUserTagsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUserTagsTableImpl(schemaName, tableName, alias string) userTagsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
TagColumn = postgres.StringColumn("tag")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, TagColumn, UserIDColumn}
|
||||
mutableColumns = postgres.ColumnList{TagColumn, UserIDColumn}
|
||||
)
|
||||
|
||||
return userTagsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
Tag: TagColumn,
|
||||
UserID: UserIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
75
backend/.gen/haystack/haystack/table/users.go
Normal file
75
backend/.gen/haystack/haystack/table/users.go
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
)
|
||||
|
||||
var Users = newUsersTable("haystack", "users", "")
|
||||
|
||||
type usersTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UsersTable struct {
|
||||
usersTable
|
||||
|
||||
EXCLUDED usersTable
|
||||
}
|
||||
|
||||
// AS creates new UsersTable with assigned alias
|
||||
func (a UsersTable) AS(alias string) *UsersTable {
|
||||
return newUsersTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UsersTable with assigned schema name
|
||||
func (a UsersTable) FromSchema(schemaName string) *UsersTable {
|
||||
return newUsersTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UsersTable with assigned table prefix
|
||||
func (a UsersTable) WithPrefix(prefix string) *UsersTable {
|
||||
return newUsersTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UsersTable with assigned table suffix
|
||||
func (a UsersTable) WithSuffix(suffix string) *UsersTable {
|
||||
return newUsersTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUsersTable(schemaName, tableName, alias string) *UsersTable {
|
||||
return &UsersTable{
|
||||
usersTable: newUsersTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUsersTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUsersTableImpl(schemaName, tableName, alias string) usersTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
allColumns = postgres.ColumnList{IDColumn}
|
||||
mutableColumns = postgres.ColumnList{}
|
||||
)
|
||||
|
||||
return usersTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
14
backend/go.mod
Normal file
14
backend/go.mod
Normal file
@ -0,0 +1,14 @@
|
||||
module screenmark/screenmark
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-jet/jet/v2 v2.12.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
17
backend/go.sum
Normal file
17
backend/go.sum
Normal file
@ -0,0 +1,17 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-jet/jet/v2 v2.12.0 h1:z2JfvBAZgsfxlQz6NXBYdZTXc7ep3jhbszTLtETv1JE=
|
||||
github.com/go-jet/jet/v2 v2.12.0/go.mod h1:ufQVRQeI1mbcO5R8uCEVcVf3Foej9kReBdwDx7YMWUM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
109
backend/main.go
Normal file
109
backend/main.go
Normal file
@ -0,0 +1,109 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"screenmark/screenmark/models"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = models.InitDatabase()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
listener := pq.NewListener(models.CONNECTION, time.Second, time.Second, func(event pq.ListenerEventType, err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
defer listener.Close()
|
||||
|
||||
go func() {
|
||||
err := listener.Listen("new_image")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case parameters := <-listener.Notify:
|
||||
log.Println("received notification, new image available: " + parameters.Extra)
|
||||
|
||||
go func() {
|
||||
openAiClient, err := CreateOpenAiClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
image, err := models.GetImage(parameters.Extra)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
imageInfo, err := openAiClient.GetImageInfo(image.ImageName, image.Image)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Info: %+v\n", imageInfo)
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("OPTIONS /image/{name}", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Add("Access-Control-Allow-Credentials", "*")
|
||||
w.Header().Add("Access-Control-Allow-Headers", "*")
|
||||
})
|
||||
|
||||
mux.HandleFunc("POST /image/{name}", func(w http.ResponseWriter, r *http.Request) {
|
||||
imageName := r.PathValue("name")
|
||||
|
||||
userId := r.Header.Get("userId")
|
||||
|
||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Add("Access-Control-Allow-Credentials", "*")
|
||||
w.Header().Add("Access-Control-Allow-Headers", "*")
|
||||
|
||||
if len(imageName) == 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "You need to provide a name in the path")
|
||||
return
|
||||
}
|
||||
|
||||
image, err := io.ReadAll(r.Body)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Couldnt read the image from the request body")
|
||||
return
|
||||
}
|
||||
|
||||
err = models.SaveImage(userId, imageName, image)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Could not save image to DB")
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
log.Println("Listening and serving.")
|
||||
|
||||
http.ListenAndServe(":3040", mux)
|
||||
}
|
19
backend/models/database.go
Normal file
19
backend/models/database.go
Normal file
@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
const CONNECTION = "postgresql://localhost:5432/haystack?sslmode=disable"
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
func InitDatabase() error {
|
||||
database, err := sql.Open("postgres", CONNECTION)
|
||||
|
||||
db = database
|
||||
|
||||
return err
|
||||
}
|
33
backend/models/image.go
Normal file
33
backend/models/image.go
Normal file
@ -0,0 +1,33 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func SaveImage(userId string, imageName string, imageData []byte) error {
|
||||
stmt := UserImages.INSERT(UserImages.UserID, UserImages.ImageName, UserImages.Image).VALUES(userId, imageName, imageData)
|
||||
_, err := stmt.Exec(db)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetImage(imageId string) (model.UserImages, error) {
|
||||
id := uuid.MustParse(imageId)
|
||||
stmt := UserImages.SELECT(UserImages.ImageName, UserImages.Image).WHERE(UserImages.ID.EQ(UUID(id)))
|
||||
|
||||
images := []model.UserImages{}
|
||||
err := stmt.Query(db, &images)
|
||||
|
||||
if len(images) != 1 {
|
||||
return model.UserImages{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
||||
}
|
||||
|
||||
return images[0], err
|
||||
}
|
280
backend/openai.go
Normal file
280
backend/openai.go
Normal file
@ -0,0 +1,280 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type ImageInfo struct {
|
||||
Tags []string `json:"tags"`
|
||||
Text []string `json:"text"`
|
||||
Links []string `json:"links"`
|
||||
}
|
||||
|
||||
type ResponseFormat struct {
|
||||
Type string `json:"type"`
|
||||
JsonSchema any `json:"json_schema"`
|
||||
}
|
||||
|
||||
type OpenAiRequestBody struct {
|
||||
Model string `json:"model"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
ResponseFormat ResponseFormat `json:"response_format"`
|
||||
|
||||
OpenAiMessages
|
||||
}
|
||||
|
||||
type OpenAiMessages struct {
|
||||
Messages []OpenAiMessage `json:"messages"`
|
||||
}
|
||||
|
||||
type OpenAiMessage interface {
|
||||
MessageToJson() ([]byte, error)
|
||||
}
|
||||
|
||||
type OpenAiTextMessage struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func (textContent OpenAiTextMessage) MessageToJson() ([]byte, error) {
|
||||
// TODO: Validate the `Role`.
|
||||
return json.Marshal(textContent)
|
||||
}
|
||||
|
||||
type OpenAiArrayMessage struct {
|
||||
Role string `json:"role"`
|
||||
Content []OpenAiContent `json:"content"`
|
||||
}
|
||||
|
||||
func (arrayContent OpenAiArrayMessage) MessageToJson() ([]byte, error) {
|
||||
return json.Marshal(arrayContent)
|
||||
}
|
||||
|
||||
func (content *OpenAiMessages) AddImage(imageName string, image []byte) error {
|
||||
extension := filepath.Ext(imageName)
|
||||
if len(extension) == 0 {
|
||||
// TODO: could also validate for image types we support.
|
||||
return errors.New("Image does not have extension")
|
||||
}
|
||||
|
||||
extension = extension[1:]
|
||||
|
||||
encodedString := base64.StdEncoding.EncodeToString(image)
|
||||
|
||||
arrayMessage := OpenAiArrayMessage{Role: ROLE_USER, Content: make([]OpenAiContent, 1)}
|
||||
arrayMessage.Content[0] = OpenAiImage{
|
||||
ImageType: IMAGE_TYPE,
|
||||
ImageUrl: ImageUrl{
|
||||
Url: fmt.Sprintf("data:image/%s;base64,%s", extension, encodedString),
|
||||
},
|
||||
}
|
||||
|
||||
content.Messages = append(content.Messages, arrayMessage)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (content *OpenAiMessages) AddSystem(prompt string) error {
|
||||
if len(content.Messages) != 0 {
|
||||
return errors.New("You can only add a system prompt at the beginning")
|
||||
}
|
||||
|
||||
content.Messages = append(content.Messages, OpenAiTextMessage{
|
||||
Role: ROLE_SYSTEM,
|
||||
Content: prompt,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type OpenAiContent interface {
|
||||
ToJson() ([]byte, error)
|
||||
}
|
||||
|
||||
type ImageUrl struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type OpenAiImage struct {
|
||||
ImageType string `json:"type"`
|
||||
ImageUrl ImageUrl `json:"image_url"`
|
||||
}
|
||||
|
||||
func (imageMessage OpenAiImage) ToJson() ([]byte, error) {
|
||||
imageMessage.ImageType = IMAGE_TYPE
|
||||
return json.Marshal(imageMessage)
|
||||
}
|
||||
|
||||
type OpenAiClient struct {
|
||||
url string
|
||||
apiKey string
|
||||
systemPrompt string
|
||||
responseFormat string
|
||||
|
||||
Do func(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// func (client OpenAiClient) Do(req *http.Request) () {
|
||||
// httpClient := http.Client{}
|
||||
// return httpClient.Do(req)
|
||||
// }
|
||||
|
||||
const OPENAI_API_KEY = "OPENAI_API_KEY"
|
||||
const ROLE_USER = "user"
|
||||
const ROLE_SYSTEM = "system"
|
||||
const IMAGE_TYPE = "image_url"
|
||||
|
||||
// TODO: extract to text file probably
|
||||
const PROMPT = `
|
||||
You are an image information extractor. The user will provide you with screenshots and your job is to extract any relevant links and text
|
||||
that the image might contain. You will also try your best to assign some tags to this image, avoid too many tags.
|
||||
|
||||
This system is part of a bookmark manager, who's main goal is to allow the user to search through various screenshots.
|
||||
`
|
||||
|
||||
const RESPONSE_FORMAT = `
|
||||
{
|
||||
"name": "schema_description",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"description": "A list of tags you think the image is relevant to.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "array",
|
||||
"description": "A list of sentences the image contains.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"type": "array",
|
||||
"description": "A list of all the links you can find in the image.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tags",
|
||||
"text",
|
||||
"links"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
`
|
||||
|
||||
func CreateOpenAiClient() (OpenAiClient, error) {
|
||||
apiKey := os.Getenv(OPENAI_API_KEY)
|
||||
|
||||
if len(apiKey) == 0 {
|
||||
return OpenAiClient{}, errors.New(OPENAI_API_KEY + " was not found.")
|
||||
}
|
||||
|
||||
return OpenAiClient{
|
||||
apiKey: apiKey,
|
||||
url: "https://api.openai.com/v1/chat/completions",
|
||||
systemPrompt: PROMPT,
|
||||
Do: func(req *http.Request) (*http.Response, error) {
|
||||
client := &http.Client{}
|
||||
return client.Do(req)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client OpenAiClient) getRequest(body []byte) (*http.Request, error) {
|
||||
req, err := http.NewRequest("POST", client.url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "Bearer "+client.apiKey)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func getCompletionsForImage(model string, temperature float64, prompt, imageName string, imageData []byte) (OpenAiRequestBody, error) {
|
||||
request := OpenAiRequestBody{
|
||||
Model: model,
|
||||
Temperature: temperature,
|
||||
}
|
||||
|
||||
// TODO: Add build pattern here that deals with errors in some internal state?
|
||||
// I want a monad!!!
|
||||
err := request.AddSystem(prompt)
|
||||
if err != nil {
|
||||
return request, err
|
||||
}
|
||||
|
||||
err = request.AddImage(imageName, imageData)
|
||||
if err != nil {
|
||||
return request, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (client OpenAiClient) GetImageInfo(imageName string, imageData []byte) (ImageInfo, error) {
|
||||
aiRequest, err := getCompletionsForImage("gpt-4o-mini", 1.0, client.systemPrompt, imageName, imageData)
|
||||
if err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
|
||||
var jsonSchema any
|
||||
err = json.Unmarshal([]byte(RESPONSE_FORMAT), &jsonSchema)
|
||||
if err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
|
||||
aiRequest.ResponseFormat = ResponseFormat{
|
||||
Type: "json_schema",
|
||||
JsonSchema: jsonSchema,
|
||||
}
|
||||
|
||||
jsonAiRequest, err := json.Marshal(aiRequest)
|
||||
if err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
|
||||
request, err := client.getRequest(jsonAiRequest)
|
||||
if err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
|
||||
response, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
|
||||
info := ImageInfo{}
|
||||
err = json.Unmarshal(response, &info)
|
||||
if err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
|
||||
log.Println(string(response))
|
||||
|
||||
return info, nil
|
||||
}
|
151
backend/openai_test.go
Normal file
151
backend/openai_test.go
Normal file
@ -0,0 +1,151 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMessageBuilder(t *testing.T) {
|
||||
content := OpenAiMessages{}
|
||||
|
||||
err := content.AddSystem("Some prompt")
|
||||
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if len(content.Messages) != 1 {
|
||||
t.Logf("Expected length 1, got %d.\n", len(content.Messages))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageBuilderImage(t *testing.T) {
|
||||
content := OpenAiMessages{}
|
||||
|
||||
prompt := "some prompt"
|
||||
imageTitle := "image.png"
|
||||
data := []byte("some data")
|
||||
|
||||
content.AddSystem(prompt)
|
||||
content.AddImage(imageTitle, data)
|
||||
|
||||
if len(content.Messages) != 2 {
|
||||
t.Logf("Expected length 2, got %d.\n", len(content.Messages))
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
promptMessage, ok := content.Messages[0].(OpenAiTextMessage)
|
||||
if !ok {
|
||||
t.Logf("Expected text content message, got %T\n", content.Messages[0])
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if promptMessage.Role != ROLE_SYSTEM {
|
||||
t.Log("Prompt message role is incorrect.")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if promptMessage.Content != prompt {
|
||||
t.Log("Prompt message content is incorrect.")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
arrayContentMessage, ok := content.Messages[1].(OpenAiArrayMessage)
|
||||
if !ok {
|
||||
t.Logf("Expected text content message, got %T\n", content.Messages[1])
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if arrayContentMessage.Role != ROLE_USER {
|
||||
t.Log("Array content message role is incorrect.")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if len(arrayContentMessage.Content) != 1 {
|
||||
t.Logf("Expected length 1, got %d.\n", len(arrayContentMessage.Content))
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
imageContent, ok := arrayContentMessage.Content[0].(OpenAiImage)
|
||||
if !ok {
|
||||
t.Logf("Expected text content message, got %T\n", arrayContentMessage.Content[0])
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
base64data := base64.StdEncoding.EncodeToString(data)
|
||||
url := fmt.Sprintf("data:image/%s;base64,%s", "png", base64data)
|
||||
|
||||
if imageContent.ImageUrl.Url != url {
|
||||
t.Logf("Expected %s, but got %s.\n", url, imageContent.ImageUrl.Url)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestFullImageRequest(t *testing.T) {
|
||||
request, err := getCompletionsForImage("model", 0.1, "You are an assistant", "image.png", []byte("some data"))
|
||||
if err != nil {
|
||||
t.Log(request)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expectedJson := `{"model":"model","temperature":0.1,"response_format":{"type":"","json_schema":""},"messages":[{"role":"system","content":"You are an assistant"},{"role":"user","content":[{"type":"image_url","image_url":{"url":"data:image/png;base64,c29tZSBkYXRh"}}]}]}`
|
||||
|
||||
if string(jsonData) != expectedJson {
|
||||
t.Logf("Expected:\n%s\n Got:\n%s\n", expectedJson, string(jsonData))
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponse(t *testing.T) {
|
||||
testResponse := `{"tags": ["tag1", "tag2"], "text": ["text1"], "links": []}`
|
||||
buffer := bytes.NewReader([]byte(testResponse))
|
||||
|
||||
body := io.NopCloser(buffer)
|
||||
|
||||
client := OpenAiClient{
|
||||
url: "http://localhost:1234",
|
||||
apiKey: "some-key",
|
||||
Do: func(_req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{Body: body}, nil
|
||||
},
|
||||
}
|
||||
|
||||
info, err := client.GetImageInfo("image.png", []byte("some data"))
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if len(info.Tags) != 2 || len(info.Text) != 1 || len(info.Links) != 0 {
|
||||
t.Logf("Some lengths are wrong.\nTags: %d\nText: %d\nLinks: %d\n", len(info.Tags), len(info.Text), len(info.Links))
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if info.Tags[0] != "tag1" {
|
||||
t.Log("0th tag is wrong.")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if info.Tags[1] != "tag2" {
|
||||
t.Log("1th tag is wrong.")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if info.Text[0] != "text1" {
|
||||
t.Log("0th text is wrong.")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
56
backend/schema.sql
Normal file
56
backend/schema.sql
Normal file
@ -0,0 +1,56 @@
|
||||
DROP SCHEMA IF EXISTS haystack CASCADE;
|
||||
|
||||
CREATE SCHEMA haystack;
|
||||
|
||||
/* -----| Schema tables |----- */
|
||||
|
||||
CREATE TABLE haystack.users (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid()
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_images (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
image_name TEXT NOT NULL,
|
||||
image BYTEA NOT NULL,
|
||||
user_id uuid NOT NULL REFERENCES haystack.users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_tags (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tag TEXT NOT NULL,
|
||||
user_id uuid NOT NULL REFERENCES haystack.users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_tags (
|
||||
tag_id UUID NOT NULL REFERENCES haystack.user_tags (id),
|
||||
image_id UUID NOT NULL REFERENCES haystack.user_images (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_text (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
image_text TEXT NOT NULL,
|
||||
image_id UUID NOT NULL REFERENCES haystack.user_images (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_links (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
link TEXT NOT NULL,
|
||||
image_id UUID NOT NULL REFERENCES haystack.user_images (id)
|
||||
);
|
||||
|
||||
/* -----| Stored Procedures |----- */
|
||||
|
||||
CREATE OR REPLACE FUNCTION notify_new_image()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
PERFORM pg_notify('new_image', NEW.id::texT);
|
||||
RETURN NEW;
|
||||
END
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
/* -----| Triggers |----- */
|
||||
|
||||
CREATE OR REPLACE TRIGGER on_new_image AFTER INSERT
|
||||
ON haystack.user_images
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE notify_new_image()
|
Reference in New Issue
Block a user