Compare commits
118 Commits
ff25e2d3e4
...
feat/email
Author | SHA1 | Date | |
---|---|---|---|
f89de6db50 | |||
6290c4b843 | |||
fba1618888 | |||
5fee1f9ccc | |||
3960203d26 | |||
2302ba5eeb | |||
c9a6c83649 | |||
3294c1854c | |||
29a5adb40a | |||
51dc8daf35 | |||
a22c56fd2c | |||
11c5c8921b | |||
1a503c8320 | |||
f169fd2ba2 | |||
d36dec8d60 | |||
e065492dd4 | |||
26c6edb6ba | |||
5c5df168ad | |||
e101070851 | |||
5278727c51 | |||
9a354c38a5 | |||
cd8375ce0f | |||
6549643340 | |||
33fb206e2f | |||
49f1990341 | |||
40392e6da3 | |||
d3bc840555 | |||
ede5f16dc1 | |||
75132503c0 | |||
393eaea2f4 | |||
a385ef21cf | |||
a37818fc49 | |||
0d3f86532e | |||
55e50d31ca | |||
1b2a99a3c8 | |||
ae62d2bea5 | |||
0126125837 | |||
c5278554cc | |||
bb5f2bc2fe | |||
b7ed4e2169 | |||
c609b45d99 | |||
c817654f3e | |||
3f53317c06 | |||
254edf3421 | |||
0814e19a68 | |||
382a1f53bd | |||
f90876f499 | |||
caf168c7a1 | |||
4c85f1de79 | |||
410df01b4d | |||
13e5ed9f9e | |||
dfb4b34de3 | |||
7b6c7090f8 | |||
87869543f7 | |||
1cd4698969 | |||
4ea817e81f | |||
3541a4755c | |||
ea5802b61b | |||
cf703f3eee | |||
84881c5c2d | |||
992a8ea282 | |||
f7382b0d2b | |||
47dd025ae3 | |||
f114ca06d8 | |||
20213ff17b | |||
9932568986 | |||
3a0f93e406 | |||
b09063f74a | |||
b3b37d252d | |||
3c71fddbd2 | |||
a3e1db3d77 | |||
5a766b8371 | |||
fd804ae515 | |||
4b120982d0 | |||
7582e4d8d9 | |||
8acf25a2a7 | |||
e505a1617e | |||
028e45bb7a | |||
536a49fe1c | |||
40e854fb87 | |||
5df6c67ee5 | |||
05263d1089 | |||
1bc1b79042 | |||
863716c096 | |||
53ebbb6e8d | |||
bf07c18fd7 | |||
d212584486 | |||
1424ec22f4 | |||
e595783d89 | |||
2df18869e5 | |||
ee69d9c2fe | |||
7e7f3ff732 | |||
3fe48464e4 | |||
d1d6ee6762 | |||
ad61b8e1fa | |||
d8095b0c67 | |||
410270e217 | |||
5bec6c9590 | |||
971f705288 | |||
13ebd80ce9 | |||
b99432c202 | |||
ee0587a16b | |||
64f6bde6a9 | |||
f49589907a | |||
2115da85b5 | |||
43092fa4f5 | |||
46e4043994 | |||
24ef31e00f | |||
993fbb30eb | |||
8cc7e4002f | |||
1fc1079484 | |||
df16298b1f | |||
f4690b52a9 | |||
81590fe622 | |||
97b1619b01 | |||
c0ce4892cd | |||
90431f824a | |||
fe60149769 |
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer", "biomejs.biome", "golang.go", "bradlc.vscode-tailwindcss"]
|
||||
}
|
12
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"biome.enabled": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports.biome": "explicit"
|
||||
},
|
||||
"[rust]": {
|
||||
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
||||
},
|
||||
"rust-analyzer.linkedProjects": ["./frontend/src-tauri/Cargo.toml"]
|
||||
}
|
20
backend/.gen/haystack/haystack/model/contacts.go
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// 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 Contacts struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
Name string
|
||||
Description *string
|
||||
PhoneNumber *string
|
||||
Email *string
|
||||
}
|
23
backend/.gen/haystack/haystack/model/events.go
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Events struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
Name string
|
||||
Description *string
|
||||
StartDateTime *time.Time
|
||||
EndDateTime *time.Time
|
||||
LocationID *uuid.UUID
|
||||
OrganizerID *uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/image.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 Image struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
ImageName string
|
||||
Image []byte
|
||||
}
|
18
backend/.gen/haystack/haystack/model/image_contacts.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 ImageContacts struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
ImageID uuid.UUID
|
||||
ContactID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/image_events.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 ImageEvents struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
EventID uuid.UUID
|
||||
ImageID uuid.UUID
|
||||
}
|
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
|
||||
}
|
18
backend/.gen/haystack/haystack/model/image_locations.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 ImageLocations struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
LocationID uuid.UUID
|
||||
ImageID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/image_notes.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 ImageNotes struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
ImageID uuid.UUID
|
||||
NoteID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/image_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 ImageTags struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
TagID uuid.UUID
|
||||
ImageID uuid.UUID
|
||||
}
|
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/locations.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 Locations struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
Name string
|
||||
Address *string
|
||||
Description *string
|
||||
}
|
19
backend/.gen/haystack/haystack/model/notes.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 Notes struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
Name string
|
||||
Description *string
|
||||
Content string
|
||||
}
|
18
backend/.gen/haystack/haystack/model/user_contacts.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 UserContacts struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
UserID uuid.UUID
|
||||
ContactID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/user_events.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 UserEvents struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
EventID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/user_images.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 UserImages struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
ImageID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
}
|
@ -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 UserImagesToProcess struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
ImageID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/user_locations.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 UserLocations struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
LocationID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
}
|
18
backend/.gen/haystack/haystack/model/user_notes.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 UserNotes struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
UserID uuid.UUID
|
||||
NoteID uuid.UUID
|
||||
}
|
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
|
||||
}
|
17
backend/.gen/haystack/haystack/model/users.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 Users struct {
|
||||
ID uuid.UUID `sql:"primary_key"`
|
||||
Email string
|
||||
}
|
87
backend/.gen/haystack/haystack/table/contacts.go
Normal file
@ -0,0 +1,87 @@
|
||||
//
|
||||
// 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 Contacts = newContactsTable("haystack", "contacts", "")
|
||||
|
||||
type contactsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
Name postgres.ColumnString
|
||||
Description postgres.ColumnString
|
||||
PhoneNumber postgres.ColumnString
|
||||
Email postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ContactsTable struct {
|
||||
contactsTable
|
||||
|
||||
EXCLUDED contactsTable
|
||||
}
|
||||
|
||||
// AS creates new ContactsTable with assigned alias
|
||||
func (a ContactsTable) AS(alias string) *ContactsTable {
|
||||
return newContactsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ContactsTable with assigned schema name
|
||||
func (a ContactsTable) FromSchema(schemaName string) *ContactsTable {
|
||||
return newContactsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ContactsTable with assigned table prefix
|
||||
func (a ContactsTable) WithPrefix(prefix string) *ContactsTable {
|
||||
return newContactsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ContactsTable with assigned table suffix
|
||||
func (a ContactsTable) WithSuffix(suffix string) *ContactsTable {
|
||||
return newContactsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newContactsTable(schemaName, tableName, alias string) *ContactsTable {
|
||||
return &ContactsTable{
|
||||
contactsTable: newContactsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newContactsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newContactsTableImpl(schemaName, tableName, alias string) contactsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
NameColumn = postgres.StringColumn("name")
|
||||
DescriptionColumn = postgres.StringColumn("description")
|
||||
PhoneNumberColumn = postgres.StringColumn("phone_number")
|
||||
EmailColumn = postgres.StringColumn("email")
|
||||
allColumns = postgres.ColumnList{IDColumn, NameColumn, DescriptionColumn, PhoneNumberColumn, EmailColumn}
|
||||
mutableColumns = postgres.ColumnList{NameColumn, DescriptionColumn, PhoneNumberColumn, EmailColumn}
|
||||
)
|
||||
|
||||
return contactsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
Name: NameColumn,
|
||||
Description: DescriptionColumn,
|
||||
PhoneNumber: PhoneNumberColumn,
|
||||
Email: EmailColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
93
backend/.gen/haystack/haystack/table/events.go
Normal file
@ -0,0 +1,93 @@
|
||||
//
|
||||
// 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 Events = newEventsTable("haystack", "events", "")
|
||||
|
||||
type eventsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
Name postgres.ColumnString
|
||||
Description postgres.ColumnString
|
||||
StartDateTime postgres.ColumnTimestamp
|
||||
EndDateTime postgres.ColumnTimestamp
|
||||
LocationID postgres.ColumnString
|
||||
OrganizerID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type EventsTable struct {
|
||||
eventsTable
|
||||
|
||||
EXCLUDED eventsTable
|
||||
}
|
||||
|
||||
// AS creates new EventsTable with assigned alias
|
||||
func (a EventsTable) AS(alias string) *EventsTable {
|
||||
return newEventsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new EventsTable with assigned schema name
|
||||
func (a EventsTable) FromSchema(schemaName string) *EventsTable {
|
||||
return newEventsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new EventsTable with assigned table prefix
|
||||
func (a EventsTable) WithPrefix(prefix string) *EventsTable {
|
||||
return newEventsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new EventsTable with assigned table suffix
|
||||
func (a EventsTable) WithSuffix(suffix string) *EventsTable {
|
||||
return newEventsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newEventsTable(schemaName, tableName, alias string) *EventsTable {
|
||||
return &EventsTable{
|
||||
eventsTable: newEventsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newEventsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newEventsTableImpl(schemaName, tableName, alias string) eventsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
NameColumn = postgres.StringColumn("name")
|
||||
DescriptionColumn = postgres.StringColumn("description")
|
||||
StartDateTimeColumn = postgres.TimestampColumn("start_date_time")
|
||||
EndDateTimeColumn = postgres.TimestampColumn("end_date_time")
|
||||
LocationIDColumn = postgres.StringColumn("location_id")
|
||||
OrganizerIDColumn = postgres.StringColumn("organizer_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, NameColumn, DescriptionColumn, StartDateTimeColumn, EndDateTimeColumn, LocationIDColumn, OrganizerIDColumn}
|
||||
mutableColumns = postgres.ColumnList{NameColumn, DescriptionColumn, StartDateTimeColumn, EndDateTimeColumn, LocationIDColumn, OrganizerIDColumn}
|
||||
)
|
||||
|
||||
return eventsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
Name: NameColumn,
|
||||
Description: DescriptionColumn,
|
||||
StartDateTime: StartDateTimeColumn,
|
||||
EndDateTime: EndDateTimeColumn,
|
||||
LocationID: LocationIDColumn,
|
||||
OrganizerID: OrganizerIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image.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 Image = newImageTable("haystack", "image", "")
|
||||
|
||||
type imageTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
ImageName postgres.ColumnString
|
||||
Image postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageTable struct {
|
||||
imageTable
|
||||
|
||||
EXCLUDED imageTable
|
||||
}
|
||||
|
||||
// AS creates new ImageTable with assigned alias
|
||||
func (a ImageTable) AS(alias string) *ImageTable {
|
||||
return newImageTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageTable with assigned schema name
|
||||
func (a ImageTable) FromSchema(schemaName string) *ImageTable {
|
||||
return newImageTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageTable with assigned table prefix
|
||||
func (a ImageTable) WithPrefix(prefix string) *ImageTable {
|
||||
return newImageTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageTable with assigned table suffix
|
||||
func (a ImageTable) WithSuffix(suffix string) *ImageTable {
|
||||
return newImageTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageTable(schemaName, tableName, alias string) *ImageTable {
|
||||
return &ImageTable{
|
||||
imageTable: newImageTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageTableImpl(schemaName, tableName, alias string) imageTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
ImageNameColumn = postgres.StringColumn("image_name")
|
||||
ImageColumn = postgres.StringColumn("image")
|
||||
allColumns = postgres.ColumnList{IDColumn, ImageNameColumn, ImageColumn}
|
||||
mutableColumns = postgres.ColumnList{ImageNameColumn, ImageColumn}
|
||||
)
|
||||
|
||||
return imageTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
ImageName: ImageNameColumn,
|
||||
Image: ImageColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image_contacts.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 ImageContacts = newImageContactsTable("haystack", "image_contacts", "")
|
||||
|
||||
type imageContactsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
ContactID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageContactsTable struct {
|
||||
imageContactsTable
|
||||
|
||||
EXCLUDED imageContactsTable
|
||||
}
|
||||
|
||||
// AS creates new ImageContactsTable with assigned alias
|
||||
func (a ImageContactsTable) AS(alias string) *ImageContactsTable {
|
||||
return newImageContactsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageContactsTable with assigned schema name
|
||||
func (a ImageContactsTable) FromSchema(schemaName string) *ImageContactsTable {
|
||||
return newImageContactsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageContactsTable with assigned table prefix
|
||||
func (a ImageContactsTable) WithPrefix(prefix string) *ImageContactsTable {
|
||||
return newImageContactsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageContactsTable with assigned table suffix
|
||||
func (a ImageContactsTable) WithSuffix(suffix string) *ImageContactsTable {
|
||||
return newImageContactsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageContactsTable(schemaName, tableName, alias string) *ImageContactsTable {
|
||||
return &ImageContactsTable{
|
||||
imageContactsTable: newImageContactsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageContactsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageContactsTableImpl(schemaName, tableName, alias string) imageContactsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
ContactIDColumn = postgres.StringColumn("contact_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, ImageIDColumn, ContactIDColumn}
|
||||
mutableColumns = postgres.ColumnList{ImageIDColumn, ContactIDColumn}
|
||||
)
|
||||
|
||||
return imageContactsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
ContactID: ContactIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image_events.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 ImageEvents = newImageEventsTable("haystack", "image_events", "")
|
||||
|
||||
type imageEventsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
EventID postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageEventsTable struct {
|
||||
imageEventsTable
|
||||
|
||||
EXCLUDED imageEventsTable
|
||||
}
|
||||
|
||||
// AS creates new ImageEventsTable with assigned alias
|
||||
func (a ImageEventsTable) AS(alias string) *ImageEventsTable {
|
||||
return newImageEventsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageEventsTable with assigned schema name
|
||||
func (a ImageEventsTable) FromSchema(schemaName string) *ImageEventsTable {
|
||||
return newImageEventsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageEventsTable with assigned table prefix
|
||||
func (a ImageEventsTable) WithPrefix(prefix string) *ImageEventsTable {
|
||||
return newImageEventsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageEventsTable with assigned table suffix
|
||||
func (a ImageEventsTable) WithSuffix(suffix string) *ImageEventsTable {
|
||||
return newImageEventsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageEventsTable(schemaName, tableName, alias string) *ImageEventsTable {
|
||||
return &ImageEventsTable{
|
||||
imageEventsTable: newImageEventsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageEventsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageEventsTableImpl(schemaName, tableName, alias string) imageEventsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
EventIDColumn = postgres.StringColumn("event_id")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, EventIDColumn, ImageIDColumn}
|
||||
mutableColumns = postgres.ColumnList{EventIDColumn, ImageIDColumn}
|
||||
)
|
||||
|
||||
return imageEventsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
EventID: EventIDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
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,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image_locations.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 ImageLocations = newImageLocationsTable("haystack", "image_locations", "")
|
||||
|
||||
type imageLocationsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
LocationID postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageLocationsTable struct {
|
||||
imageLocationsTable
|
||||
|
||||
EXCLUDED imageLocationsTable
|
||||
}
|
||||
|
||||
// AS creates new ImageLocationsTable with assigned alias
|
||||
func (a ImageLocationsTable) AS(alias string) *ImageLocationsTable {
|
||||
return newImageLocationsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageLocationsTable with assigned schema name
|
||||
func (a ImageLocationsTable) FromSchema(schemaName string) *ImageLocationsTable {
|
||||
return newImageLocationsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageLocationsTable with assigned table prefix
|
||||
func (a ImageLocationsTable) WithPrefix(prefix string) *ImageLocationsTable {
|
||||
return newImageLocationsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageLocationsTable with assigned table suffix
|
||||
func (a ImageLocationsTable) WithSuffix(suffix string) *ImageLocationsTable {
|
||||
return newImageLocationsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageLocationsTable(schemaName, tableName, alias string) *ImageLocationsTable {
|
||||
return &ImageLocationsTable{
|
||||
imageLocationsTable: newImageLocationsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageLocationsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageLocationsTableImpl(schemaName, tableName, alias string) imageLocationsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
LocationIDColumn = postgres.StringColumn("location_id")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, LocationIDColumn, ImageIDColumn}
|
||||
mutableColumns = postgres.ColumnList{LocationIDColumn, ImageIDColumn}
|
||||
)
|
||||
|
||||
return imageLocationsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
LocationID: LocationIDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image_notes.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 ImageNotes = newImageNotesTable("haystack", "image_notes", "")
|
||||
|
||||
type imageNotesTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
NoteID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type ImageNotesTable struct {
|
||||
imageNotesTable
|
||||
|
||||
EXCLUDED imageNotesTable
|
||||
}
|
||||
|
||||
// AS creates new ImageNotesTable with assigned alias
|
||||
func (a ImageNotesTable) AS(alias string) *ImageNotesTable {
|
||||
return newImageNotesTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new ImageNotesTable with assigned schema name
|
||||
func (a ImageNotesTable) FromSchema(schemaName string) *ImageNotesTable {
|
||||
return newImageNotesTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new ImageNotesTable with assigned table prefix
|
||||
func (a ImageNotesTable) WithPrefix(prefix string) *ImageNotesTable {
|
||||
return newImageNotesTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new ImageNotesTable with assigned table suffix
|
||||
func (a ImageNotesTable) WithSuffix(suffix string) *ImageNotesTable {
|
||||
return newImageNotesTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newImageNotesTable(schemaName, tableName, alias string) *ImageNotesTable {
|
||||
return &ImageNotesTable{
|
||||
imageNotesTable: newImageNotesTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newImageNotesTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newImageNotesTableImpl(schemaName, tableName, alias string) imageNotesTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
NoteIDColumn = postgres.StringColumn("note_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, ImageIDColumn, NoteIDColumn}
|
||||
mutableColumns = postgres.ColumnList{ImageIDColumn, NoteIDColumn}
|
||||
)
|
||||
|
||||
return imageNotesTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
NoteID: NoteIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/image_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 ImageTags = newImageTagsTable("haystack", "image_tags", "")
|
||||
|
||||
type imageTagsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
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 (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
TagIDColumn = postgres.StringColumn("tag_id")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, TagIDColumn, ImageIDColumn}
|
||||
mutableColumns = postgres.ColumnList{TagIDColumn, ImageIDColumn}
|
||||
)
|
||||
|
||||
return imageTagsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
TagID: TagIDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
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,
|
||||
}
|
||||
}
|
84
backend/.gen/haystack/haystack/table/locations.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 Locations = newLocationsTable("haystack", "locations", "")
|
||||
|
||||
type locationsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
Name postgres.ColumnString
|
||||
Address postgres.ColumnString
|
||||
Description postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type LocationsTable struct {
|
||||
locationsTable
|
||||
|
||||
EXCLUDED locationsTable
|
||||
}
|
||||
|
||||
// AS creates new LocationsTable with assigned alias
|
||||
func (a LocationsTable) AS(alias string) *LocationsTable {
|
||||
return newLocationsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new LocationsTable with assigned schema name
|
||||
func (a LocationsTable) FromSchema(schemaName string) *LocationsTable {
|
||||
return newLocationsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new LocationsTable with assigned table prefix
|
||||
func (a LocationsTable) WithPrefix(prefix string) *LocationsTable {
|
||||
return newLocationsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new LocationsTable with assigned table suffix
|
||||
func (a LocationsTable) WithSuffix(suffix string) *LocationsTable {
|
||||
return newLocationsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newLocationsTable(schemaName, tableName, alias string) *LocationsTable {
|
||||
return &LocationsTable{
|
||||
locationsTable: newLocationsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newLocationsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newLocationsTableImpl(schemaName, tableName, alias string) locationsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
NameColumn = postgres.StringColumn("name")
|
||||
AddressColumn = postgres.StringColumn("address")
|
||||
DescriptionColumn = postgres.StringColumn("description")
|
||||
allColumns = postgres.ColumnList{IDColumn, NameColumn, AddressColumn, DescriptionColumn}
|
||||
mutableColumns = postgres.ColumnList{NameColumn, AddressColumn, DescriptionColumn}
|
||||
)
|
||||
|
||||
return locationsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
Name: NameColumn,
|
||||
Address: AddressColumn,
|
||||
Description: DescriptionColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
84
backend/.gen/haystack/haystack/table/notes.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 Notes = newNotesTable("haystack", "notes", "")
|
||||
|
||||
type notesTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
Name postgres.ColumnString
|
||||
Description postgres.ColumnString
|
||||
Content postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type NotesTable struct {
|
||||
notesTable
|
||||
|
||||
EXCLUDED notesTable
|
||||
}
|
||||
|
||||
// AS creates new NotesTable with assigned alias
|
||||
func (a NotesTable) AS(alias string) *NotesTable {
|
||||
return newNotesTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new NotesTable with assigned schema name
|
||||
func (a NotesTable) FromSchema(schemaName string) *NotesTable {
|
||||
return newNotesTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new NotesTable with assigned table prefix
|
||||
func (a NotesTable) WithPrefix(prefix string) *NotesTable {
|
||||
return newNotesTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new NotesTable with assigned table suffix
|
||||
func (a NotesTable) WithSuffix(suffix string) *NotesTable {
|
||||
return newNotesTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newNotesTable(schemaName, tableName, alias string) *NotesTable {
|
||||
return &NotesTable{
|
||||
notesTable: newNotesTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newNotesTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newNotesTableImpl(schemaName, tableName, alias string) notesTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
NameColumn = postgres.StringColumn("name")
|
||||
DescriptionColumn = postgres.StringColumn("description")
|
||||
ContentColumn = postgres.StringColumn("content")
|
||||
allColumns = postgres.ColumnList{IDColumn, NameColumn, DescriptionColumn, ContentColumn}
|
||||
mutableColumns = postgres.ColumnList{NameColumn, DescriptionColumn, ContentColumn}
|
||||
)
|
||||
|
||||
return notesTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
Name: NameColumn,
|
||||
Description: DescriptionColumn,
|
||||
Content: ContentColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
33
backend/.gen/haystack/haystack/table/table_use_schema.go
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// 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) {
|
||||
Contacts = Contacts.FromSchema(schema)
|
||||
Events = Events.FromSchema(schema)
|
||||
Image = Image.FromSchema(schema)
|
||||
ImageContacts = ImageContacts.FromSchema(schema)
|
||||
ImageEvents = ImageEvents.FromSchema(schema)
|
||||
ImageLinks = ImageLinks.FromSchema(schema)
|
||||
ImageLocations = ImageLocations.FromSchema(schema)
|
||||
ImageNotes = ImageNotes.FromSchema(schema)
|
||||
ImageTags = ImageTags.FromSchema(schema)
|
||||
ImageText = ImageText.FromSchema(schema)
|
||||
Locations = Locations.FromSchema(schema)
|
||||
Notes = Notes.FromSchema(schema)
|
||||
UserContacts = UserContacts.FromSchema(schema)
|
||||
UserEvents = UserEvents.FromSchema(schema)
|
||||
UserImages = UserImages.FromSchema(schema)
|
||||
UserImagesToProcess = UserImagesToProcess.FromSchema(schema)
|
||||
UserLocations = UserLocations.FromSchema(schema)
|
||||
UserNotes = UserNotes.FromSchema(schema)
|
||||
UserTags = UserTags.FromSchema(schema)
|
||||
Users = Users.FromSchema(schema)
|
||||
}
|
81
backend/.gen/haystack/haystack/table/user_contacts.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 UserContacts = newUserContactsTable("haystack", "user_contacts", "")
|
||||
|
||||
type userContactsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
UserID postgres.ColumnString
|
||||
ContactID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UserContactsTable struct {
|
||||
userContactsTable
|
||||
|
||||
EXCLUDED userContactsTable
|
||||
}
|
||||
|
||||
// AS creates new UserContactsTable with assigned alias
|
||||
func (a UserContactsTable) AS(alias string) *UserContactsTable {
|
||||
return newUserContactsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UserContactsTable with assigned schema name
|
||||
func (a UserContactsTable) FromSchema(schemaName string) *UserContactsTable {
|
||||
return newUserContactsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UserContactsTable with assigned table prefix
|
||||
func (a UserContactsTable) WithPrefix(prefix string) *UserContactsTable {
|
||||
return newUserContactsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UserContactsTable with assigned table suffix
|
||||
func (a UserContactsTable) WithSuffix(suffix string) *UserContactsTable {
|
||||
return newUserContactsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUserContactsTable(schemaName, tableName, alias string) *UserContactsTable {
|
||||
return &UserContactsTable{
|
||||
userContactsTable: newUserContactsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUserContactsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUserContactsTableImpl(schemaName, tableName, alias string) userContactsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
ContactIDColumn = postgres.StringColumn("contact_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, UserIDColumn, ContactIDColumn}
|
||||
mutableColumns = postgres.ColumnList{UserIDColumn, ContactIDColumn}
|
||||
)
|
||||
|
||||
return userContactsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
UserID: UserIDColumn,
|
||||
ContactID: ContactIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/user_events.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 UserEvents = newUserEventsTable("haystack", "user_events", "")
|
||||
|
||||
type userEventsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
EventID postgres.ColumnString
|
||||
UserID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UserEventsTable struct {
|
||||
userEventsTable
|
||||
|
||||
EXCLUDED userEventsTable
|
||||
}
|
||||
|
||||
// AS creates new UserEventsTable with assigned alias
|
||||
func (a UserEventsTable) AS(alias string) *UserEventsTable {
|
||||
return newUserEventsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UserEventsTable with assigned schema name
|
||||
func (a UserEventsTable) FromSchema(schemaName string) *UserEventsTable {
|
||||
return newUserEventsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UserEventsTable with assigned table prefix
|
||||
func (a UserEventsTable) WithPrefix(prefix string) *UserEventsTable {
|
||||
return newUserEventsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UserEventsTable with assigned table suffix
|
||||
func (a UserEventsTable) WithSuffix(suffix string) *UserEventsTable {
|
||||
return newUserEventsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUserEventsTable(schemaName, tableName, alias string) *UserEventsTable {
|
||||
return &UserEventsTable{
|
||||
userEventsTable: newUserEventsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUserEventsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUserEventsTableImpl(schemaName, tableName, alias string) userEventsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
EventIDColumn = postgres.StringColumn("event_id")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, EventIDColumn, UserIDColumn}
|
||||
mutableColumns = postgres.ColumnList{EventIDColumn, UserIDColumn}
|
||||
)
|
||||
|
||||
return userEventsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
EventID: EventIDColumn,
|
||||
UserID: UserIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/user_images.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 UserImages = newUserImagesTable("haystack", "user_images", "")
|
||||
|
||||
type userImagesTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
ImageID 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")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, ImageIDColumn, UserIDColumn}
|
||||
mutableColumns = postgres.ColumnList{ImageIDColumn, UserIDColumn}
|
||||
)
|
||||
|
||||
return userImagesTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
UserID: UserIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
@ -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 UserImagesToProcess = newUserImagesToProcessTable("haystack", "user_images_to_process", "")
|
||||
|
||||
type userImagesToProcessTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
ImageID postgres.ColumnString
|
||||
UserID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UserImagesToProcessTable struct {
|
||||
userImagesToProcessTable
|
||||
|
||||
EXCLUDED userImagesToProcessTable
|
||||
}
|
||||
|
||||
// AS creates new UserImagesToProcessTable with assigned alias
|
||||
func (a UserImagesToProcessTable) AS(alias string) *UserImagesToProcessTable {
|
||||
return newUserImagesToProcessTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UserImagesToProcessTable with assigned schema name
|
||||
func (a UserImagesToProcessTable) FromSchema(schemaName string) *UserImagesToProcessTable {
|
||||
return newUserImagesToProcessTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UserImagesToProcessTable with assigned table prefix
|
||||
func (a UserImagesToProcessTable) WithPrefix(prefix string) *UserImagesToProcessTable {
|
||||
return newUserImagesToProcessTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UserImagesToProcessTable with assigned table suffix
|
||||
func (a UserImagesToProcessTable) WithSuffix(suffix string) *UserImagesToProcessTable {
|
||||
return newUserImagesToProcessTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUserImagesToProcessTable(schemaName, tableName, alias string) *UserImagesToProcessTable {
|
||||
return &UserImagesToProcessTable{
|
||||
userImagesToProcessTable: newUserImagesToProcessTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUserImagesToProcessTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUserImagesToProcessTableImpl(schemaName, tableName, alias string) userImagesToProcessTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
ImageIDColumn = postgres.StringColumn("image_id")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, ImageIDColumn, UserIDColumn}
|
||||
mutableColumns = postgres.ColumnList{ImageIDColumn, UserIDColumn}
|
||||
)
|
||||
|
||||
return userImagesToProcessTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
ImageID: ImageIDColumn,
|
||||
UserID: UserIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/user_locations.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 UserLocations = newUserLocationsTable("haystack", "user_locations", "")
|
||||
|
||||
type userLocationsTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
LocationID postgres.ColumnString
|
||||
UserID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UserLocationsTable struct {
|
||||
userLocationsTable
|
||||
|
||||
EXCLUDED userLocationsTable
|
||||
}
|
||||
|
||||
// AS creates new UserLocationsTable with assigned alias
|
||||
func (a UserLocationsTable) AS(alias string) *UserLocationsTable {
|
||||
return newUserLocationsTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UserLocationsTable with assigned schema name
|
||||
func (a UserLocationsTable) FromSchema(schemaName string) *UserLocationsTable {
|
||||
return newUserLocationsTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UserLocationsTable with assigned table prefix
|
||||
func (a UserLocationsTable) WithPrefix(prefix string) *UserLocationsTable {
|
||||
return newUserLocationsTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UserLocationsTable with assigned table suffix
|
||||
func (a UserLocationsTable) WithSuffix(suffix string) *UserLocationsTable {
|
||||
return newUserLocationsTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUserLocationsTable(schemaName, tableName, alias string) *UserLocationsTable {
|
||||
return &UserLocationsTable{
|
||||
userLocationsTable: newUserLocationsTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUserLocationsTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUserLocationsTableImpl(schemaName, tableName, alias string) userLocationsTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
LocationIDColumn = postgres.StringColumn("location_id")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, LocationIDColumn, UserIDColumn}
|
||||
mutableColumns = postgres.ColumnList{LocationIDColumn, UserIDColumn}
|
||||
)
|
||||
|
||||
return userLocationsTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
LocationID: LocationIDColumn,
|
||||
UserID: UserIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
81
backend/.gen/haystack/haystack/table/user_notes.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 UserNotes = newUserNotesTable("haystack", "user_notes", "")
|
||||
|
||||
type userNotesTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
UserID postgres.ColumnString
|
||||
NoteID postgres.ColumnString
|
||||
|
||||
AllColumns postgres.ColumnList
|
||||
MutableColumns postgres.ColumnList
|
||||
}
|
||||
|
||||
type UserNotesTable struct {
|
||||
userNotesTable
|
||||
|
||||
EXCLUDED userNotesTable
|
||||
}
|
||||
|
||||
// AS creates new UserNotesTable with assigned alias
|
||||
func (a UserNotesTable) AS(alias string) *UserNotesTable {
|
||||
return newUserNotesTable(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new UserNotesTable with assigned schema name
|
||||
func (a UserNotesTable) FromSchema(schemaName string) *UserNotesTable {
|
||||
return newUserNotesTable(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new UserNotesTable with assigned table prefix
|
||||
func (a UserNotesTable) WithPrefix(prefix string) *UserNotesTable {
|
||||
return newUserNotesTable(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new UserNotesTable with assigned table suffix
|
||||
func (a UserNotesTable) WithSuffix(suffix string) *UserNotesTable {
|
||||
return newUserNotesTable(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func newUserNotesTable(schemaName, tableName, alias string) *UserNotesTable {
|
||||
return &UserNotesTable{
|
||||
userNotesTable: newUserNotesTableImpl(schemaName, tableName, alias),
|
||||
EXCLUDED: newUserNotesTableImpl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func newUserNotesTableImpl(schemaName, tableName, alias string) userNotesTable {
|
||||
var (
|
||||
IDColumn = postgres.StringColumn("id")
|
||||
UserIDColumn = postgres.StringColumn("user_id")
|
||||
NoteIDColumn = postgres.StringColumn("note_id")
|
||||
allColumns = postgres.ColumnList{IDColumn, UserIDColumn, NoteIDColumn}
|
||||
mutableColumns = postgres.ColumnList{UserIDColumn, NoteIDColumn}
|
||||
)
|
||||
|
||||
return userNotesTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
UserID: UserIDColumn,
|
||||
NoteID: NoteIDColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
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,
|
||||
}
|
||||
}
|
78
backend/.gen/haystack/haystack/table/users.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 Users = newUsersTable("haystack", "users", "")
|
||||
|
||||
type usersTable struct {
|
||||
postgres.Table
|
||||
|
||||
// Columns
|
||||
ID postgres.ColumnString
|
||||
Email 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")
|
||||
EmailColumn = postgres.StringColumn("email")
|
||||
allColumns = postgres.ColumnList{IDColumn, EmailColumn}
|
||||
mutableColumns = postgres.ColumnList{EmailColumn}
|
||||
)
|
||||
|
||||
return usersTable{
|
||||
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
ID: IDColumn,
|
||||
Email: EmailColumn,
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
4
backend/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
screenmark
|
||||
.env.docker
|
||||
.env
|
||||
pgdata
|
15
backend/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
FROM golang
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Dependency management
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go build -o /app/haystack
|
||||
|
||||
EXPOSE 3040
|
||||
|
||||
CMD ["/app/haystack"]
|
26
backend/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Running the backend
|
||||
|
||||
1. Create a `.env.docker` file, which must contain the following.
|
||||
|
||||
OPENAI_API_KEY=openai_key
|
||||
DB_CONNECTION=postgresql://postgres:password@database:5432/haystack_db?sslmode=disable
|
||||
|
||||
2. Use `docker-compose up` to spin up the containers.
|
||||
|
||||
You should be able to access the backend through port `3040`
|
||||
|
||||
# Methods
|
||||
|
||||
For now, we cheat and add a `userId` header which if os type `UUID`. Use the auto generated test one (fcc22dbb-7792-4595-be8e-d0439e13990a).
|
||||
|
||||
- `GET /image` | Returns all of the users image, including tags, links and text any image contains.
|
||||
- `GET /image/{imageId}` | Returns the actual image, use this to display images in the UI.
|
||||
- `POST /image/{imageNameWithExtension}` | Sends an image to the backend, saves it and sents it to open ai to later process.
|
||||
|
||||
# Architecture
|
||||
|
||||
1. The user posts an image, which gets saved on our database (all data, including images are saved on DB).
|
||||
2. We listen for table event creation, and we can process this image by sending it to OpenAI.
|
||||
3. After OpenAI responds, we write to the database.
|
||||
|
||||
This means that for now, we don't have a notification system to tell the user when their image is done processing. But will do in the future.
|
208
backend/agents/client/chat.go
Normal file
@ -0,0 +1,208 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Chat struct {
|
||||
Messages []ChatMessage `json:"messages"`
|
||||
}
|
||||
|
||||
type ChatMessage interface {
|
||||
IsResponse() bool
|
||||
}
|
||||
|
||||
// TODO: the role could be inferred from the type.
|
||||
// This would solve some bugs.
|
||||
|
||||
/*
|
||||
|
||||
Is there a world where this actually becomes the product?
|
||||
Where we build such a resilient system of AI calls that we
|
||||
can build some app builder, or even just an API system,
|
||||
with a fancy UI?
|
||||
|
||||
Manage all the complexity for the user?
|
||||
|
||||
*/
|
||||
|
||||
// =============================================
|
||||
// Messages from us to the AI.
|
||||
// =============================================
|
||||
|
||||
type UserRole = string
|
||||
|
||||
const (
|
||||
User UserRole = "user"
|
||||
System UserRole = "system"
|
||||
)
|
||||
|
||||
type ToolRole = string
|
||||
|
||||
const (
|
||||
Tool ToolRole = "tool"
|
||||
)
|
||||
|
||||
type ChatUserMessage struct {
|
||||
Role UserRole `json:"role"`
|
||||
|
||||
MessageContent `json:"MessageContent"`
|
||||
}
|
||||
|
||||
func (m ChatUserMessage) MarshalJSON() ([]byte, error) {
|
||||
switch t := m.MessageContent.(type) {
|
||||
case SingleMessage:
|
||||
return json.Marshal(&struct {
|
||||
Role UserRole `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}{
|
||||
Role: User,
|
||||
Content: t.Content,
|
||||
})
|
||||
case ArrayMessage:
|
||||
return json.Marshal(&struct {
|
||||
Role UserRole `json:"role"`
|
||||
Content []ImageMessageContent `json:"content"`
|
||||
}{
|
||||
Role: User,
|
||||
Content: t.Content,
|
||||
})
|
||||
}
|
||||
|
||||
return []byte{}, errors.New("Unreachable")
|
||||
}
|
||||
|
||||
func (r ChatUserMessage) IsResponse() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type ChatUserToolResponse struct {
|
||||
Role ToolRole `json:"role"`
|
||||
|
||||
// The name of the function we are responding to.
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
ToolCallId string `json:"tool_call_id"`
|
||||
}
|
||||
|
||||
func (r ChatUserToolResponse) IsResponse() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type ChatAiMessage struct {
|
||||
Role string `json:"role"`
|
||||
ToolCalls *[]ToolCall `json:"tool_calls,omitempty"`
|
||||
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func (m ChatAiMessage) IsResponse() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// Unique interface for message content.
|
||||
// =============================================
|
||||
|
||||
type MessageContent interface {
|
||||
IsSingleMessage() bool
|
||||
}
|
||||
|
||||
type SingleMessage struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func (m SingleMessage) IsSingleMessage() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type ArrayMessage struct {
|
||||
Content []ImageMessageContent `json:"content"`
|
||||
}
|
||||
|
||||
func (m ArrayMessage) IsSingleMessage() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type ImageMessageContent struct {
|
||||
ImageType string `json:"type"`
|
||||
ImageUrl string `json:"image_url"`
|
||||
}
|
||||
|
||||
type ImageContentUrl struct {
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// Adjacent interfaces.
|
||||
// =============================================
|
||||
|
||||
type ToolCall struct {
|
||||
Index int `json:"index"`
|
||||
Id string `json:"id"`
|
||||
Function FunctionCall `json:"function"`
|
||||
}
|
||||
|
||||
type FunctionCall struct {
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments"`
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// Chat methods
|
||||
// =============================================
|
||||
|
||||
func (chat *Chat) AddSystem(prompt string) {
|
||||
chat.Messages = append(chat.Messages, ChatUserMessage{
|
||||
Role: System,
|
||||
MessageContent: SingleMessage{
|
||||
Content: prompt,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (chat *Chat) 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)
|
||||
|
||||
messageContent := ArrayMessage{
|
||||
Content: make([]ImageMessageContent, 1),
|
||||
}
|
||||
|
||||
messageContent.Content[0] = ImageMessageContent{
|
||||
ImageType: "image_url",
|
||||
ImageUrl: fmt.Sprintf("data:image/%s;base64,%s", extension, encodedString),
|
||||
}
|
||||
|
||||
arrayMessage := ChatUserMessage{Role: User, MessageContent: messageContent}
|
||||
chat.Messages = append(chat.Messages, arrayMessage)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (chat *Chat) AddAiResponse(res ChatAiMessage) {
|
||||
chat.Messages = append(chat.Messages, res)
|
||||
}
|
||||
|
||||
func (chat *Chat) AddToolResponse(res ChatUserToolResponse) {
|
||||
chat.Messages = append(chat.Messages, res)
|
||||
}
|
||||
|
||||
func (chat Chat) GetLatest() (ChatMessage, error) {
|
||||
if len(chat.Messages) == 0 {
|
||||
return nil, errors.New("Not enough messages")
|
||||
}
|
||||
|
||||
return chat.Messages[len(chat.Messages)-1], nil
|
||||
}
|
24
backend/agents/client/chat_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFlatMarshallSingleMessage(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
message := ChatUserMessage{
|
||||
Role: User,
|
||||
MessageContent: SingleMessage{
|
||||
Content: "Hello",
|
||||
},
|
||||
}
|
||||
|
||||
json, err := json.Marshal(message)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(string(json), "{\"role\":\"user\",\"content\":\"Hello\"}")
|
||||
}
|
194
backend/agents/client/client.go
Normal file
@ -0,0 +1,194 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
type ResponseFormat struct {
|
||||
Type string `json:"type"`
|
||||
JsonSchema any `json:"json_schema"`
|
||||
}
|
||||
|
||||
type AgentRequestBody struct {
|
||||
Model string `json:"model"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
ResponseFormat ResponseFormat `json:"response_format"`
|
||||
|
||||
Tools *any `json:"tools,omitempty"`
|
||||
ToolChoice *string `json:"tool_choice,omitempty"`
|
||||
|
||||
EndToolCall string `json:"-"`
|
||||
|
||||
Chat *Chat `json:"messages"`
|
||||
}
|
||||
|
||||
func (req AgentRequestBody) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
Model string `json:"model"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
ResponseFormat ResponseFormat `json:"response_format"`
|
||||
|
||||
Tools *any `json:"tools,omitempty"`
|
||||
ToolChoice *string `json:"tool_choice,omitempty"`
|
||||
Messages []ChatMessage `json:"messages"`
|
||||
}{
|
||||
Model: req.Model,
|
||||
Temperature: req.Temperature,
|
||||
ResponseFormat: req.ResponseFormat,
|
||||
|
||||
Tools: req.Tools,
|
||||
ToolChoice: req.ToolChoice,
|
||||
|
||||
Messages: req.Chat.Messages,
|
||||
})
|
||||
}
|
||||
|
||||
type ResponseChoice struct {
|
||||
Index int `json:"index"`
|
||||
Message ChatAiMessage `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
}
|
||||
|
||||
type AgentResponse struct {
|
||||
Id string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Choices []ResponseChoice `json:"choices"`
|
||||
Created int `json:"created"`
|
||||
}
|
||||
|
||||
type AgentClient struct {
|
||||
url string
|
||||
apiKey string
|
||||
responseFormat string
|
||||
|
||||
ToolHandler ToolsHandlers
|
||||
|
||||
Do func(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
const OPENAI_API_KEY = "OPENAI_API_KEY"
|
||||
|
||||
func CreateAgentClient() (AgentClient, error) {
|
||||
apiKey := os.Getenv(OPENAI_API_KEY)
|
||||
|
||||
if len(apiKey) == 0 {
|
||||
return AgentClient{}, errors.New(OPENAI_API_KEY + " was not found.")
|
||||
}
|
||||
|
||||
return AgentClient{
|
||||
apiKey: apiKey,
|
||||
url: "https://api.mistral.ai/v1/chat/completions",
|
||||
Do: func(req *http.Request) (*http.Response, error) {
|
||||
client := &http.Client{}
|
||||
return client.Do(req)
|
||||
},
|
||||
|
||||
ToolHandler: ToolsHandlers{
|
||||
handlers: map[string]ToolHandler{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (client AgentClient) 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 (client AgentClient) Request(req *AgentRequestBody) (AgentResponse, error) {
|
||||
jsonAiRequest, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return AgentResponse{}, err
|
||||
}
|
||||
|
||||
httpRequest, err := client.getRequest(jsonAiRequest)
|
||||
if err != nil {
|
||||
return AgentResponse{}, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(httpRequest)
|
||||
if err != nil {
|
||||
return AgentResponse{}, err
|
||||
}
|
||||
|
||||
response, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return AgentResponse{}, err
|
||||
}
|
||||
|
||||
fmt.Println(string(response))
|
||||
|
||||
agentResponse := AgentResponse{}
|
||||
err = json.Unmarshal(response, &agentResponse)
|
||||
|
||||
if err != nil {
|
||||
return AgentResponse{}, err
|
||||
}
|
||||
|
||||
if len(agentResponse.Choices) != 1 {
|
||||
return AgentResponse{}, errors.New("Unsupported. We currently only accept 1 choice from AI.")
|
||||
}
|
||||
|
||||
req.Chat.AddAiResponse(agentResponse.Choices[0].Message)
|
||||
|
||||
return agentResponse, nil
|
||||
}
|
||||
|
||||
func (client AgentClient) ToolLoop(info ToolHandlerInfo, req *AgentRequestBody) error {
|
||||
for {
|
||||
err := client.Process(info, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.Request(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var FinishedCall = errors.New("Last tool tool was called")
|
||||
|
||||
func (client AgentClient) Process(info ToolHandlerInfo, req *AgentRequestBody) error {
|
||||
var err error
|
||||
|
||||
message, err := req.Chat.GetLatest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aiMessage, ok := message.(ChatAiMessage)
|
||||
if !ok {
|
||||
return errors.New("Latest message isnt an AI message")
|
||||
}
|
||||
|
||||
if aiMessage.ToolCalls == nil {
|
||||
// Not an error, we just dont have any tool calls to process.
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, toolCall := range *aiMessage.ToolCalls {
|
||||
if toolCall.Function.Name == req.EndToolCall {
|
||||
return FinishedCall
|
||||
}
|
||||
|
||||
toolResponse := client.ToolHandler.Handle(info, toolCall)
|
||||
|
||||
req.Chat.AddToolResponse(toolResponse)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
70
backend/agents/client/tools.go
Normal file
@ -0,0 +1,70 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ToolHandlerInfo struct {
|
||||
UserId uuid.UUID
|
||||
ImageId uuid.UUID
|
||||
}
|
||||
|
||||
type ToolHandler struct {
|
||||
Fn func(info ToolHandlerInfo, args string, call ToolCall) (string, error)
|
||||
}
|
||||
|
||||
type ToolsHandlers struct {
|
||||
handlers map[string]ToolHandler
|
||||
}
|
||||
|
||||
var NoToolCallError = errors.New("An assistant tool call with no tool calls was provided.")
|
||||
|
||||
const NonExistantTool = "This tool does not exist"
|
||||
|
||||
func (handler ToolsHandlers) Handle(info ToolHandlerInfo, toolCallMessage ToolCall) ChatUserToolResponse {
|
||||
fnName := toolCallMessage.Function.Name
|
||||
arguments := toolCallMessage.Function.Arguments
|
||||
|
||||
responseMessage := ChatUserToolResponse{
|
||||
Role: "tool",
|
||||
Name: fnName,
|
||||
ToolCallId: toolCallMessage.Id,
|
||||
}
|
||||
|
||||
fnHandler, exists := handler.handlers[fnName]
|
||||
if !exists {
|
||||
responseMessage.Content = NonExistantTool
|
||||
return responseMessage
|
||||
}
|
||||
|
||||
res, err := fnHandler.Fn(info, arguments, toolCallMessage)
|
||||
|
||||
if err != nil {
|
||||
responseMessage.Content = err.Error()
|
||||
} else {
|
||||
responseMessage.Content = res
|
||||
}
|
||||
|
||||
return responseMessage
|
||||
}
|
||||
|
||||
func (handler *ToolsHandlers) AddTool(name string, fn func(info ToolHandlerInfo, args string, call ToolCall) (any, error)) {
|
||||
handler.handlers[name] = ToolHandler{
|
||||
Fn: func(info ToolHandlerInfo, args string, call ToolCall) (string, error) {
|
||||
res, err := fn(info, args, call)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
marshalledRes, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(marshalledRes), nil
|
||||
},
|
||||
}
|
||||
}
|
188
backend/agents/client/tools_test.go
Normal file
@ -0,0 +1,188 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ToolTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
handler ToolsHandlers
|
||||
client AgentClient
|
||||
}
|
||||
|
||||
func (suite *ToolTestSuite) SetupTest() {
|
||||
suite.handler = ToolsHandlers{
|
||||
handlers: map[string]ToolHandler{},
|
||||
}
|
||||
|
||||
suite.handler.AddTool("a", func(info ToolHandlerInfo, args string, call ToolCall) (any, error) {
|
||||
return args, nil
|
||||
})
|
||||
|
||||
suite.handler.AddTool("error", func(info ToolHandlerInfo, args string, call ToolCall) (any, error) {
|
||||
return false, errors.New("I will always error")
|
||||
})
|
||||
|
||||
suite.client.ToolHandler = suite.handler
|
||||
}
|
||||
|
||||
func (suite *ToolTestSuite) TestSingleToolCall() {
|
||||
require := suite.Require()
|
||||
|
||||
response := suite.handler.Handle(
|
||||
ToolHandlerInfo{
|
||||
UserId: uuid.Nil,
|
||||
ImageId: uuid.Nil,
|
||||
},
|
||||
ToolCall{
|
||||
Index: 0,
|
||||
Id: "1",
|
||||
Function: FunctionCall{
|
||||
Name: "a",
|
||||
Arguments: "return",
|
||||
},
|
||||
})
|
||||
|
||||
require.EqualValues(response, ChatUserToolResponse{
|
||||
Role: "tool",
|
||||
Content: "\"return\"",
|
||||
ToolCallId: "1",
|
||||
Name: "a",
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *ToolTestSuite) TestMultipleToolCalls() {
|
||||
assert := suite.Assert()
|
||||
require := suite.Require()
|
||||
|
||||
chat := Chat{
|
||||
Messages: []ChatMessage{ChatAiMessage{
|
||||
Role: "assistant",
|
||||
Content: "",
|
||||
ToolCalls: &[]ToolCall{
|
||||
{
|
||||
Index: 0,
|
||||
Id: "1",
|
||||
Function: FunctionCall{
|
||||
Name: "a",
|
||||
Arguments: "first-call",
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Id: "2",
|
||||
Function: FunctionCall{
|
||||
Name: "a",
|
||||
Arguments: "second-call",
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
err := suite.client.Process(
|
||||
ToolHandlerInfo{
|
||||
UserId: uuid.Nil,
|
||||
ImageId: uuid.Nil,
|
||||
},
|
||||
&AgentRequestBody{
|
||||
Chat: &chat,
|
||||
})
|
||||
|
||||
require.NoError(err, "Tool call shouldnt return an error")
|
||||
assert.EqualValues(chat.Messages[1:], []ChatMessage{
|
||||
ChatUserToolResponse{
|
||||
Role: "tool",
|
||||
Content: "\"first-call\"",
|
||||
ToolCallId: "1",
|
||||
Name: "a",
|
||||
},
|
||||
ChatUserToolResponse{
|
||||
Role: "tool",
|
||||
Content: "\"second-call\"",
|
||||
ToolCallId: "2",
|
||||
Name: "a",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *ToolTestSuite) TestMultipleToolCallsWithErrors() {
|
||||
assert := suite.Assert()
|
||||
require := suite.Require()
|
||||
|
||||
chat := Chat{
|
||||
Messages: []ChatMessage{ChatAiMessage{
|
||||
Role: "assistant",
|
||||
Content: "",
|
||||
ToolCalls: &[]ToolCall{
|
||||
{
|
||||
Index: 0,
|
||||
Id: "1",
|
||||
Function: FunctionCall{
|
||||
Name: "error",
|
||||
Arguments: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Id: "2",
|
||||
Function: FunctionCall{
|
||||
Name: "non-existant",
|
||||
Arguments: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Id: "3",
|
||||
Function: FunctionCall{
|
||||
Name: "a",
|
||||
Arguments: "no-error",
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
err := suite.client.Process(
|
||||
ToolHandlerInfo{
|
||||
UserId: uuid.Nil,
|
||||
ImageId: uuid.Nil,
|
||||
},
|
||||
&AgentRequestBody{
|
||||
Chat: &chat,
|
||||
})
|
||||
|
||||
require.NoError(err, "Tool call shouldnt return an error")
|
||||
|
||||
assert.EqualValues(chat.Messages[1:], []ChatMessage{
|
||||
ChatUserToolResponse{
|
||||
Role: "tool",
|
||||
Content: "I will always error",
|
||||
ToolCallId: "1",
|
||||
Name: "error",
|
||||
},
|
||||
ChatUserToolResponse{
|
||||
Role: "tool",
|
||||
Content: "This tool does not exist",
|
||||
ToolCallId: "2",
|
||||
Name: "non-existant",
|
||||
},
|
||||
ChatUserToolResponse{
|
||||
Role: "tool",
|
||||
Content: "\"no-error\"",
|
||||
ToolCallId: "3",
|
||||
Name: "a",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestToolSuite(t *testing.T) {
|
||||
suite.Run(t, &ToolTestSuite{
|
||||
client: AgentClient{},
|
||||
})
|
||||
}
|
295
backend/agents/event_location_agent.go
Normal file
@ -0,0 +1,295 @@
|
||||
package agents
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
"screenmark/screenmark/agents/client"
|
||||
"screenmark/screenmark/models"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// This prompt is probably shit.
|
||||
const eventLocationPrompt = `
|
||||
You are an agent that extracts events, locations, and organizers from an image. Your primary tasks are to identify and create locations and organizers before creating events. Follow these steps:
|
||||
|
||||
Identify and Create Locations:
|
||||
|
||||
Check if the image contains a location.
|
||||
If a location is found, check if it exists in the listLocations.
|
||||
If the location does not exist, create it first.
|
||||
Always reuse existing locations from listLocations to avoid duplicates.
|
||||
|
||||
Identify and Create Events:
|
||||
|
||||
Check if the image contains an event. An event should have a name and a date.
|
||||
If an event is found, ensure you have a location (from step 1) and an organizer (from step 2) before creating the event.
|
||||
Events must have an associated location and organizer. Do not create an event without these.
|
||||
If possible, return a start time and an end time as ISO datetime strings.
|
||||
Handling Images Without Events or Locations:
|
||||
|
||||
It is possible that the image does not contain an event or a location. In such cases, do not create an event.
|
||||
Always prioritize the creation of locations and organizers before events. Ensure that all events have an associated location and organizer.
|
||||
`
|
||||
|
||||
// TODO: this should be read directly from a file on load.
|
||||
const TOOLS = `
|
||||
[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "createLocation",
|
||||
"description": "Creates a location. No not use if you think an existing location is suitable!",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "listLocations",
|
||||
"description": "Lists the locations available",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "createEvent",
|
||||
"description": "Creates a new event",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"startDateTime": {
|
||||
"type": "string",
|
||||
"description": "The start time as an ISO string"
|
||||
},
|
||||
"endDateTime": {
|
||||
"type": "string",
|
||||
"description": "The end time as an ISO string"
|
||||
},
|
||||
"locationId": {
|
||||
"type": "string",
|
||||
"description": "The ID of the location, available by listLocations"
|
||||
},
|
||||
"organizerName": {
|
||||
"type": "string",
|
||||
"description": "The name of the organizer"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "finish",
|
||||
"description": "Nothing else to do. call this function.",
|
||||
"parameters": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
`
|
||||
|
||||
type EventLocationAgent struct {
|
||||
client client.AgentClient
|
||||
|
||||
eventModel models.EventModel
|
||||
locationModel models.LocationModel
|
||||
contactModel models.ContactModel
|
||||
|
||||
toolHandler client.ToolsHandlers
|
||||
}
|
||||
|
||||
type ListLocationArguments struct{}
|
||||
type ListOrganizerArguments struct{}
|
||||
|
||||
type CreateLocationArguments struct {
|
||||
Name string `json:"name"`
|
||||
Address *string `json:"address,omitempty"`
|
||||
Coordinates *string `json:"coordinates,omitempty"`
|
||||
}
|
||||
|
||||
type CreateOrganizerArguments struct {
|
||||
Name string `json:"name"`
|
||||
PhoneNumber *string `json:"phoneNumber,omitempty"`
|
||||
Email *string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
type AttachImageLocationArguments struct {
|
||||
LocationId string `json:"locationId"`
|
||||
}
|
||||
|
||||
type CreateEventArguments struct {
|
||||
Name string `json:"name"`
|
||||
StartDateTime string `json:"startDateTime"`
|
||||
EndDateTime string `json:"endDateTime"`
|
||||
LocationId string `json:"locationId"`
|
||||
OrganizerName string `json:"organizerName"`
|
||||
}
|
||||
|
||||
func (agent EventLocationAgent) GetLocations(userId uuid.UUID, imageId uuid.UUID, imageName string, imageData []byte) error {
|
||||
var tools any
|
||||
err := json.Unmarshal([]byte(TOOLS), &tools)
|
||||
|
||||
toolChoice := "any"
|
||||
|
||||
request := client.AgentRequestBody{
|
||||
Tools: &tools,
|
||||
ToolChoice: &toolChoice,
|
||||
Model: "pixtral-12b-2409",
|
||||
Temperature: 0.3,
|
||||
EndToolCall: "finish",
|
||||
ResponseFormat: client.ResponseFormat{
|
||||
Type: "text",
|
||||
},
|
||||
Chat: &client.Chat{
|
||||
Messages: make([]client.ChatMessage, 0),
|
||||
},
|
||||
}
|
||||
|
||||
request.Chat.AddSystem(eventLocationPrompt)
|
||||
request.Chat.AddImage(imageName, imageData)
|
||||
|
||||
_, err = agent.client.Request(&request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
toolHandlerInfo := client.ToolHandlerInfo{
|
||||
ImageId: imageId,
|
||||
UserId: userId,
|
||||
}
|
||||
|
||||
return agent.client.ToolLoop(toolHandlerInfo, &request)
|
||||
}
|
||||
|
||||
func NewLocationEventAgent(locationModel models.LocationModel, eventModel models.EventModel, contactModel models.ContactModel) (EventLocationAgent, error) {
|
||||
agentClient, err := client.CreateAgentClient()
|
||||
if err != nil {
|
||||
return EventLocationAgent{}, err
|
||||
}
|
||||
|
||||
agent := EventLocationAgent{
|
||||
client: agentClient,
|
||||
locationModel: locationModel,
|
||||
eventModel: eventModel,
|
||||
contactModel: contactModel,
|
||||
}
|
||||
|
||||
agentClient.ToolHandler.AddTool("listLocations",
|
||||
func(info client.ToolHandlerInfo, args string, call client.ToolCall) (any, error) {
|
||||
return agent.locationModel.List(context.Background(), info.UserId)
|
||||
},
|
||||
)
|
||||
|
||||
agentClient.ToolHandler.AddTool("createLocation",
|
||||
func(info client.ToolHandlerInfo, _args string, call client.ToolCall) (any, error) {
|
||||
args := CreateLocationArguments{}
|
||||
err := json.Unmarshal([]byte(_args), &args)
|
||||
if err != nil {
|
||||
return model.Locations{}, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
location, err := agent.locationModel.Save(ctx, info.UserId, model.Locations{
|
||||
Name: args.Name,
|
||||
Address: args.Address,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
_, err = agent.locationModel.SaveToImage(ctx, info.ImageId, location.ID)
|
||||
|
||||
return location, err
|
||||
},
|
||||
)
|
||||
|
||||
agentClient.ToolHandler.AddTool("createEvent",
|
||||
func(info client.ToolHandlerInfo, _args string, call client.ToolCall) (any, error) {
|
||||
args := CreateEventArguments{}
|
||||
err := json.Unmarshal([]byte(_args), &args)
|
||||
if err != nil {
|
||||
return model.Locations{}, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
layout := "2006-01-02T15:04:05Z"
|
||||
|
||||
startTime, err := time.Parse(layout, args.StartDateTime)
|
||||
if err != nil {
|
||||
return model.Events{}, err
|
||||
}
|
||||
|
||||
endTime, err := time.Parse(layout, args.EndDateTime)
|
||||
if err != nil {
|
||||
return model.Events{}, err
|
||||
}
|
||||
|
||||
event, err := agent.eventModel.Save(ctx, info.UserId, model.Events{
|
||||
Name: args.Name,
|
||||
StartDateTime: &startTime,
|
||||
EndDateTime: &endTime,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
organizer, err := agent.contactModel.Save(ctx, info.UserId, model.Contacts{
|
||||
Name: args.Name,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
_, err = agent.eventModel.SaveToImage(ctx, info.ImageId, event.ID)
|
||||
if err != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
_, err = agent.contactModel.SaveToImage(ctx, info.ImageId, organizer.ID)
|
||||
if err != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
locationId, err := uuid.Parse(args.LocationId)
|
||||
if err != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
event, err = agent.eventModel.UpdateLocation(ctx, event.ID, locationId)
|
||||
if err != nil {
|
||||
return event, err
|
||||
}
|
||||
|
||||
return agent.eventModel.UpdateOrganizer(ctx, event.ID, organizer.ID)
|
||||
},
|
||||
)
|
||||
|
||||
return agent, nil
|
||||
}
|
80
backend/agents/note_agent.go
Normal file
@ -0,0 +1,80 @@
|
||||
package agents
|
||||
|
||||
import (
|
||||
"context"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
"screenmark/screenmark/agents/client"
|
||||
"screenmark/screenmark/models"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const noteAgentPrompt = `
|
||||
You are a helpful agent, who's job is to extract notes from images.
|
||||
Not all images contain notes, in such cases there's not need to create them.
|
||||
|
||||
An image can have more than one note.
|
||||
|
||||
You must return markdown, and adapt the text to best fit markdown.
|
||||
Do not return anything except markdown.
|
||||
`
|
||||
|
||||
type NoteAgent struct {
|
||||
client client.AgentClient
|
||||
|
||||
noteModel models.NoteModel
|
||||
}
|
||||
|
||||
func (agent NoteAgent) GetNotes(userId uuid.UUID, imageId uuid.UUID, imageName string, imageData []byte) error {
|
||||
request := client.AgentRequestBody{
|
||||
Model: "pixtral-12b-2409",
|
||||
Temperature: 0.3,
|
||||
ResponseFormat: client.ResponseFormat{
|
||||
Type: "text",
|
||||
},
|
||||
Chat: &client.Chat{
|
||||
Messages: make([]client.ChatMessage, 0),
|
||||
},
|
||||
}
|
||||
|
||||
request.Chat.AddSystem(noteAgentPrompt)
|
||||
request.Chat.AddImage(imageName, imageData)
|
||||
|
||||
resp, err := agent.client.Request(&request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
markdown := resp.Choices[0].Message.Content
|
||||
|
||||
note, err := agent.noteModel.Save(ctx, userId, model.Notes{
|
||||
Name: "the note", // TODO: add some json schema
|
||||
Content: markdown,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = agent.noteModel.SaveToImage(ctx, imageId, note.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewNoteAgent(noteModel models.NoteModel) (NoteAgent, error) {
|
||||
client, err := client.CreateAgentClient()
|
||||
if err != nil {
|
||||
return NoteAgent{}, err
|
||||
}
|
||||
|
||||
agent := NoteAgent{
|
||||
client: client,
|
||||
noteModel: noteModel,
|
||||
}
|
||||
|
||||
return agent, nil
|
||||
}
|
167
backend/agents/orchestrator.go
Normal file
@ -0,0 +1,167 @@
|
||||
package agents
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"screenmark/screenmark/agents/client"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const orchestratorPrompt = `
|
||||
You are an Orchestrator for various AI agents.
|
||||
|
||||
The user will send you images and you have to determine which agents you have to call, in order to best help the user.
|
||||
|
||||
You might decide no agent needs to be called.
|
||||
|
||||
The agents are available as tool calls.
|
||||
|
||||
Agents available:
|
||||
|
||||
eventLocationAgent
|
||||
|
||||
Use it when you think the image contains an event or a location of any sort. This can be an event page, a map, an address or a date.
|
||||
|
||||
noteAgent
|
||||
|
||||
Use it when there is text on the screen. Any text, always use this. Use me!
|
||||
|
||||
defaultAgent
|
||||
|
||||
When none of the above apply.
|
||||
|
||||
Always call agents in parallel if you need to call more than 1.
|
||||
`
|
||||
|
||||
const MY_TOOLS = `
|
||||
[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "eventLocationAgent",
|
||||
"description": "Uses the event location agent",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "noteAgent",
|
||||
"description": "Uses the note agent",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "defaultAgent",
|
||||
"description": "Used when you dont think its a good idea to call other agents",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]`
|
||||
|
||||
type OrchestratorAgent struct {
|
||||
client client.AgentClient
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
Ok bool `json:"ok"`
|
||||
}
|
||||
|
||||
// TODO: the primary function of the agent could be extracted outwards.
|
||||
// This is basically the same function as we have in the `event_location_agent.go`
|
||||
func (agent OrchestratorAgent) Orchestrate(userId uuid.UUID, imageId uuid.UUID, imageName string, imageData []byte) error {
|
||||
toolChoice := "any"
|
||||
|
||||
var tools any
|
||||
err := json.Unmarshal([]byte(MY_TOOLS), &tools)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := client.AgentRequestBody{
|
||||
Model: "pixtral-12b-2409",
|
||||
Temperature: 0.3,
|
||||
ResponseFormat: client.ResponseFormat{
|
||||
Type: "text",
|
||||
},
|
||||
ToolChoice: &toolChoice,
|
||||
Tools: &tools,
|
||||
|
||||
EndToolCall: "defaultAgent",
|
||||
|
||||
Chat: &client.Chat{
|
||||
Messages: make([]client.ChatMessage, 0),
|
||||
},
|
||||
}
|
||||
|
||||
request.Chat.AddSystem(orchestratorPrompt)
|
||||
request.Chat.AddImage(imageName, imageData)
|
||||
|
||||
res, err := agent.client.Request(&request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(res)
|
||||
|
||||
toolHandlerInfo := client.ToolHandlerInfo{
|
||||
ImageId: imageId,
|
||||
UserId: userId,
|
||||
}
|
||||
|
||||
return agent.client.ToolLoop(toolHandlerInfo, &request)
|
||||
}
|
||||
|
||||
func NewOrchestratorAgent(eventLocationAgent EventLocationAgent, noteAgent NoteAgent, imageName string, imageData []byte) (OrchestratorAgent, error) {
|
||||
agent, err := client.CreateAgentClient()
|
||||
if err != nil {
|
||||
return OrchestratorAgent{}, err
|
||||
}
|
||||
|
||||
agent.ToolHandler.AddTool("eventLocationAgent", func(info client.ToolHandlerInfo, args string, call client.ToolCall) (any, error) {
|
||||
// We need a way to keep track of this async?
|
||||
// Probably just a DB, because we don't want to wait. The orchistrator shouldnt wait for this stuff to finish.
|
||||
|
||||
go eventLocationAgent.GetLocations(info.UserId, info.ImageId, imageName, imageData)
|
||||
|
||||
return Status{
|
||||
Ok: true,
|
||||
}, nil
|
||||
})
|
||||
|
||||
agent.ToolHandler.AddTool("noteAgent", func(info client.ToolHandlerInfo, args string, call client.ToolCall) (any, error) {
|
||||
go noteAgent.GetNotes(info.UserId, info.ImageId, imageName, imageData)
|
||||
|
||||
return Status{
|
||||
Ok: true,
|
||||
}, nil
|
||||
})
|
||||
|
||||
agent.ToolHandler.AddTool("defaultAgent", func(info client.ToolHandlerInfo, args string, call client.ToolCall) (any, error) {
|
||||
// To nothing
|
||||
|
||||
return Status{
|
||||
Ok: true,
|
||||
}, errors.New("Finished! Kinda bad return type but...")
|
||||
})
|
||||
|
||||
return OrchestratorAgent{
|
||||
client: agent,
|
||||
}, nil
|
||||
}
|
108
backend/ai_response.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"name": "image_info",
|
||||
"strict": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"title": "image",
|
||||
"required": ["tags", "text", "links"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"title": "tags",
|
||||
"description": "A list of tags you think the image is relevant to.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "array",
|
||||
"title": "text",
|
||||
"description": "A list of sentences the image contains.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"type": "array",
|
||||
"title": "links",
|
||||
"description": "A list of all the links you can find in the image.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"locations": {
|
||||
"title": "locations",
|
||||
"type": "array",
|
||||
"description": "A list of locations you can find on the image, if any",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"title": "name",
|
||||
"type": "string"
|
||||
},
|
||||
"coordinates": {
|
||||
"title": "coordinates",
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"title": "address",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"title": "description",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"title": "events",
|
||||
"type": "array",
|
||||
"description": "A list of events you find on the image, if any",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "name"
|
||||
},
|
||||
"locations": {
|
||||
"title": "locations",
|
||||
"type": "array",
|
||||
"description": "A list of locations on this event, if any",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"title": "name",
|
||||
"type": "string"
|
||||
},
|
||||
"coordinates": {
|
||||
"title": "coordinates",
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"title": "address",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"title": "description",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
71
backend/auth.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Code struct {
|
||||
Code string
|
||||
Valid time.Time
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
codes map[string]Code
|
||||
|
||||
mailer Mailer
|
||||
}
|
||||
|
||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
func randString(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (a *Auth) CreateCode(email string) error {
|
||||
code := randString(10)
|
||||
|
||||
if err := a.mailer.SendCode(email, code); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.codes[email] = Code{
|
||||
Code: code,
|
||||
Valid: time.Now().Add(time.Minute),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Auth) IsCodeValid(email string, code string) bool {
|
||||
fmt.Println(a.codes)
|
||||
existingCode, exists := a.codes[email]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
return existingCode.Valid.After(time.Now()) && existingCode.Code == code
|
||||
}
|
||||
|
||||
func (a *Auth) UseCode(email string, code string) error {
|
||||
if valid := a.IsCodeValid(email, code); !valid {
|
||||
fmt.Println("returning error?")
|
||||
return errors.New("This code is invalid.")
|
||||
}
|
||||
|
||||
delete(a.codes, email)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateAuth(mailer Mailer) Auth {
|
||||
return Auth{
|
||||
codes: make(map[string]Code),
|
||||
mailer: mailer,
|
||||
}
|
||||
}
|
30
backend/auth_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type TestMail struct{}
|
||||
|
||||
func (m TestMail) SendCode(to string, code string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var testMailer = TestMail{}
|
||||
|
||||
func TestCreateCode(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
auth := CreateAuth(testMailer)
|
||||
|
||||
err := auth.CreateCode("test")
|
||||
require.NoError(err)
|
||||
|
||||
code, exists := auth.codes["test"]
|
||||
require.True(exists)
|
||||
require.True(code.Valid.After(time.Now()))
|
||||
require.True(auth.IsCodeValid("test", code.Code))
|
||||
}
|
31
backend/docker-compose.yml
Normal file
@ -0,0 +1,31 @@
|
||||
services:
|
||||
database:
|
||||
image: postgres
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_DB: haystack
|
||||
volumes:
|
||||
- ./schema.sql:/docker-entrypoint-initdb.d/schema.sql
|
||||
- ./pgdata:/var/lib/postgresql/data
|
||||
ports:
|
||||
- 4321:5432
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d haystack"]
|
||||
interval: 10s
|
||||
retries: 5
|
||||
start_period: 5s
|
||||
timeout: 5s
|
||||
backend:
|
||||
build: .
|
||||
restart: always
|
||||
env_file: .env.docker
|
||||
ports:
|
||||
- 3040:3040
|
||||
volumes:
|
||||
- ./.env.docker:/app/.env
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
restart: true
|
71
backend/email.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wneessen/go-mail"
|
||||
)
|
||||
|
||||
type MailClient struct {
|
||||
client *mail.Client
|
||||
}
|
||||
|
||||
type TestMailClient struct{}
|
||||
|
||||
type Mailer interface {
|
||||
SendCode(to string, code string) error
|
||||
}
|
||||
|
||||
func (m MailClient) getMessage() (*mail.Msg, error) {
|
||||
message := mail.NewMsg()
|
||||
if err := message.From("auth@johncosta.tech"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
func (m MailClient) SendCode(to string, code string) error {
|
||||
msg, err := m.getMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := msg.To(to); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.Subject("Login to Haystack")
|
||||
msg.SetBodyString(mail.TypeTextPlain, code)
|
||||
|
||||
return m.client.DialAndSend(msg)
|
||||
}
|
||||
|
||||
func (m TestMailClient) SendCode(to string, code string) error {
|
||||
fmt.Printf("Email: %s | Code %s\n", to, code)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateMailClient() (Mailer, error) {
|
||||
mode := os.Getenv("MODE")
|
||||
if mode == "DEV" {
|
||||
return TestMailClient{}, nil
|
||||
}
|
||||
|
||||
client, err := mail.NewClient(
|
||||
"smtp.mailbox.org",
|
||||
mail.WithSMTPAuth(mail.SMTPAuthPlain),
|
||||
mail.WithUsername(os.Getenv("EMAIL_USERNAME")),
|
||||
mail.WithPassword(os.Getenv("EMAIL_PASSWORD")),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return MailClient{}, err
|
||||
}
|
||||
|
||||
return MailClient{
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
79
backend/events.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log"
|
||||
"os"
|
||||
"screenmark/screenmark/agents"
|
||||
"screenmark/screenmark/models"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
func ListenNewImageEvents(db *sql.DB) {
|
||||
listener := pq.NewListener(os.Getenv("DB_CONNECTION"), time.Second, time.Second, func(event pq.ListenerEventType, err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
defer listener.Close()
|
||||
|
||||
locationModel := models.NewLocationModel(db)
|
||||
eventModel := models.NewEventModel(db)
|
||||
noteModel := models.NewNoteModel(db)
|
||||
imageModel := models.NewImageModel(db)
|
||||
contactModel := models.NewContactModel(db)
|
||||
|
||||
err := listener.Listen("new_image")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case parameters := <-listener.Notify:
|
||||
imageId := uuid.MustParse(parameters.Extra)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
go func() {
|
||||
locationAgent, err := agents.NewLocationEventAgent(locationModel, eventModel, contactModel)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
noteAgent, err := agents.NewNoteAgent(noteModel)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
image, err := imageModel.GetToProcessWithData(ctx, imageId)
|
||||
if err != nil {
|
||||
log.Println("Failed to GetToProcessWithData")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = imageModel.FinishProcessing(ctx, image.ID)
|
||||
if err != nil {
|
||||
log.Println("Failed to FinishProcessing")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
orchestrator, err := agents.NewOrchestratorAgent(locationAgent, noteAgent, image.Image.ImageName, image.Image.Image)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = orchestrator.Orchestrate(image.UserID, image.ImageID, image.Image.ImageName, image.Image.Image)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
19
backend/go.mod
Normal file
@ -0,0 +1,19 @@
|
||||
module screenmark/screenmark
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-chi/chi/v5 v5.2.1 // indirect
|
||||
github.com/go-jet/jet/v2 v2.12.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // 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.10.0 // indirect
|
||||
github.com/wneessen/go-mail v0.6.2 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
91
backend/go.sum
Normal file
@ -0,0 +1,91 @@
|
||||
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-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
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/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
|
||||
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
96
backend/jwt.go
Normal file
@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type JwtType string
|
||||
|
||||
const (
|
||||
Access JwtType = "access"
|
||||
Refresh JwtType = "refresh"
|
||||
)
|
||||
|
||||
type JwtClaims struct {
|
||||
UserID string
|
||||
Type JwtType
|
||||
Expire time.Time
|
||||
}
|
||||
|
||||
// obviously this is very not secure. TODO: extract to env
|
||||
var JWT_SECRET = []byte("very secret")
|
||||
|
||||
func createToken(claims JwtClaims) *jwt.Token {
|
||||
return jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"UserID": claims.UserID,
|
||||
"Type": claims.Type,
|
||||
"Expire": claims.Expire,
|
||||
})
|
||||
}
|
||||
|
||||
func CreateRefreshToken(userId uuid.UUID) string {
|
||||
token := createToken(JwtClaims{
|
||||
UserID: userId.String(),
|
||||
Type: Refresh,
|
||||
Expire: time.Now().Add(time.Hour * 24 * 7),
|
||||
})
|
||||
|
||||
// TODO: bruh what is this
|
||||
tokenString, err := token.SignedString(JWT_SECRET)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return tokenString
|
||||
}
|
||||
|
||||
func CreateAccessToken(userId uuid.UUID) string {
|
||||
token := createToken(JwtClaims{
|
||||
UserID: userId.String(),
|
||||
Type: Access,
|
||||
Expire: time.Now().Add(time.Hour),
|
||||
})
|
||||
|
||||
// TODO: bruh what is this
|
||||
tokenString, err := token.SignedString(JWT_SECRET)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return tokenString
|
||||
}
|
||||
|
||||
var NotValidToken = errors.New("Not a valid token")
|
||||
|
||||
func GetUserIdFromAccess(accessToken string) (uuid.UUID, error) {
|
||||
token, err := jwt.Parse(accessToken, func(token *jwt.Token) (any, error) {
|
||||
return JWT_SECRET, nil
|
||||
}, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
|
||||
|
||||
if err != nil {
|
||||
return uuid.Nil, err
|
||||
}
|
||||
|
||||
// Blah blah, check expiry and stuff
|
||||
|
||||
// this function is stupid
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
tokenType, ok := claims["Type"]
|
||||
if !ok || tokenType.(string) != "access" {
|
||||
return uuid.Nil, NotValidToken
|
||||
}
|
||||
|
||||
userId, err := uuid.Parse(claims["UserID"].(string))
|
||||
if err != nil {
|
||||
return uuid.Nil, NotValidToken
|
||||
}
|
||||
|
||||
return userId, nil
|
||||
} else {
|
||||
return uuid.Nil, NotValidToken
|
||||
}
|
||||
}
|
324
backend/main.go
Normal file
@ -0,0 +1,324 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
"screenmark/screenmark/agents/client"
|
||||
"screenmark/screenmark/models"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/google/uuid"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type TestAiClient struct {
|
||||
ImageInfo client.ImageMessageContent
|
||||
}
|
||||
|
||||
func (client TestAiClient) GetImageInfo(imageName string, imageData []byte) (client.ImageMessageContent, error) {
|
||||
return client.ImageInfo, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
db, err := models.InitDatabase()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
imageModel := models.NewImageModel(db)
|
||||
userModel := models.NewUserModel(db)
|
||||
|
||||
mail, err := CreateMailClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
auth := CreateAuth(mail)
|
||||
|
||||
go ListenNewImageEvents(db)
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(CorsMiddleware)
|
||||
r.Use(func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
})
|
||||
|
||||
r.Options("/*", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(ProtectedRoute)
|
||||
|
||||
r.Get("/image", func(w http.ResponseWriter, r *http.Request) {
|
||||
userId := r.Context().Value(USER_ID).(uuid.UUID)
|
||||
|
||||
images, err := userModel.ListWithProperties(r.Context(), userId)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintf(w, "Something went wrong")
|
||||
return
|
||||
}
|
||||
|
||||
type DataType struct {
|
||||
Type string `json:"type"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
dataTypes := make([]DataType, 0)
|
||||
for _, image := range images {
|
||||
for _, location := range image.Locations {
|
||||
dataTypes = append(dataTypes, DataType{
|
||||
Type: "location",
|
||||
Data: location,
|
||||
})
|
||||
}
|
||||
|
||||
for _, event := range image.Events {
|
||||
dataTypes = append(dataTypes, DataType{
|
||||
Type: "event",
|
||||
Data: event,
|
||||
})
|
||||
}
|
||||
|
||||
for _, note := range image.Notes {
|
||||
dataTypes = append(dataTypes, DataType{
|
||||
Type: "note",
|
||||
Data: note,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
jsonImages, err := json.Marshal(dataTypes)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Could not create JSON response for this image")
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(jsonImages)
|
||||
})
|
||||
|
||||
r.Get("/image/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||
stringImageId := r.PathValue("id")
|
||||
userId := r.Context().Value(USER_ID).(uuid.UUID)
|
||||
|
||||
imageId, err := uuid.Parse(stringImageId)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprintf(w, "You cannot read this")
|
||||
return
|
||||
}
|
||||
|
||||
if authorized := imageModel.IsUserAuthorized(r.Context(), imageId, userId); !authorized {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
fmt.Fprintf(w, "You cannot read this")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: really need authorization here!
|
||||
image, err := imageModel.Get(r.Context(), imageId)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprintf(w, "Could not get image")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: this could be part of the db table
|
||||
extension := filepath.Ext(image.Image.ImageName)
|
||||
extension = extension[1:]
|
||||
|
||||
w.Header().Add("Content-Type", "image/"+extension)
|
||||
w.Write(image.Image.Image)
|
||||
})
|
||||
|
||||
r.Post("/image/{name}", func(w http.ResponseWriter, r *http.Request) {
|
||||
imageName := r.PathValue("name")
|
||||
userId := r.Context().Value(USER_ID).(uuid.UUID)
|
||||
|
||||
if len(imageName) == 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "You need to provide a name in the path")
|
||||
return
|
||||
}
|
||||
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
|
||||
// TODO: length checks on body
|
||||
// TODO: extract this shit out
|
||||
image := make([]byte, 0)
|
||||
if contentType == "application/base64" {
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, r.Body)
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
decodedIamge, err := io.Copy(buf, decoder)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "bruh, base64 aint decoding")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(image))
|
||||
fmt.Println(decodedIamge)
|
||||
|
||||
image = buf.Bytes()
|
||||
} else if contentType == "application/oclet-stream" {
|
||||
bodyData, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "bruh, binary aint binaring")
|
||||
return
|
||||
}
|
||||
// TODO: check headers
|
||||
|
||||
image = bodyData
|
||||
} else {
|
||||
log.Println("bad stuff?")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Bruh, you need oclet stream or base64")
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Println("First case")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Couldnt read the image from the request body")
|
||||
return
|
||||
}
|
||||
|
||||
userImage, err := imageModel.Process(r.Context(), uuid.MustParse(userId), model.Image{
|
||||
Image: image,
|
||||
ImageName: imageName,
|
||||
})
|
||||
if err != nil {
|
||||
log.Println("Second case")
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Could not save image to DB")
|
||||
return
|
||||
}
|
||||
|
||||
jsonUserImage, err := json.Marshal(userImage)
|
||||
if err != nil {
|
||||
log.Println("Third case")
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Could not create JSON response for this image")
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
fmt.Fprint(w, string(jsonUserImage))
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
r.Post("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
type LoginBody struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
loginBody := LoginBody{}
|
||||
err := json.NewDecoder(r.Body).Decode(&loginBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Request body was not correct")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: validate it's an email
|
||||
|
||||
auth.CreateCode(loginBody.Email)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
r.Post("/code", func(w http.ResponseWriter, r *http.Request) {
|
||||
type CodeBody struct {
|
||||
Email string `json:"email"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type CodeReturn struct {
|
||||
Access string `json:"access"`
|
||||
Refresh string `json:"refresh"`
|
||||
}
|
||||
|
||||
codeBody := CodeBody{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&codeBody); err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "Request body was not correct")
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth.UseCode(codeBody.Email, codeBody.Code); err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintf(w, "email or code are incorrect")
|
||||
return
|
||||
}
|
||||
|
||||
uuid, err := userModel.GetUserIdFromEmail(r.Context(), codeBody.Email)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Something went wrong.")
|
||||
return
|
||||
}
|
||||
|
||||
refresh := CreateRefreshToken(uuid)
|
||||
access := CreateAccessToken(uuid)
|
||||
|
||||
codeReturn := CodeReturn{
|
||||
Access: access,
|
||||
Refresh: refresh,
|
||||
}
|
||||
|
||||
json, err := json.Marshal(codeReturn)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Something went wrong.")
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Fprint(w, string(json))
|
||||
})
|
||||
|
||||
log.Println("Listening and serving on port 3040.")
|
||||
if err := http.ListenAndServe(":3040", r); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
41
backend/middleware.go
Normal file
@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func CorsMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(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", "*")
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
const USER_ID = "UserID"
|
||||
|
||||
func ProtectedRoute(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
token := r.Header.Get("Authorization")
|
||||
if len(token) < len("Bearer ") {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(token[len("Bearer "):])
|
||||
userId, err := GetUserIdFromAccess(token[len("Bearer "):])
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
contextWithUserId := context.WithValue(r.Context(), USER_ID, userId)
|
||||
|
||||
newR := r.WithContext(contextWithUserId)
|
||||
next.ServeHTTP(w, newR)
|
||||
})
|
||||
}
|
70
backend/models/contacts.go
Normal file
@ -0,0 +1,70 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ContactModel struct {
|
||||
dbPool *sql.DB
|
||||
}
|
||||
|
||||
func (m ContactModel) List(ctx context.Context, userId uuid.UUID) ([]model.Contacts, error) {
|
||||
listContactsStmt := SELECT(Contacts.AllColumns).
|
||||
FROM(
|
||||
Contacts.
|
||||
INNER_JOIN(UserContacts, UserContacts.ContactID.EQ(Contacts.ID)),
|
||||
).
|
||||
WHERE(UserContacts.UserID.EQ(UUID(userId)))
|
||||
|
||||
locations := []model.Contacts{}
|
||||
|
||||
err := listContactsStmt.QueryContext(ctx, m.dbPool, &locations)
|
||||
return locations, err
|
||||
}
|
||||
|
||||
func (m ContactModel) Save(ctx context.Context, userId uuid.UUID, contact model.Contacts) (model.Contacts, error) {
|
||||
// TODO: make this a transaction
|
||||
|
||||
insertContactStmt := Contacts.
|
||||
INSERT(Contacts.Name, Contacts.Description, Contacts.PhoneNumber, Contacts.Email).
|
||||
VALUES(contact.Name, contact.Description, contact.PhoneNumber, contact.Email).
|
||||
RETURNING(Contacts.AllColumns)
|
||||
|
||||
insertedContact := model.Contacts{}
|
||||
err := insertContactStmt.QueryContext(ctx, m.dbPool, &insertedContact)
|
||||
|
||||
if err != nil {
|
||||
return insertedContact, err
|
||||
}
|
||||
|
||||
insertUserContactStmt := UserContacts.
|
||||
INSERT(UserContacts.UserID, UserContacts.ContactID).
|
||||
VALUES(userId, insertedContact.ID)
|
||||
|
||||
_, err = insertUserContactStmt.ExecContext(ctx, m.dbPool)
|
||||
|
||||
return insertedContact, err
|
||||
}
|
||||
|
||||
func (m ContactModel) SaveToImage(ctx context.Context, imageId uuid.UUID, contactId uuid.UUID) (model.ImageContacts, error) {
|
||||
insertImageContactStmt := ImageContacts.
|
||||
INSERT(ImageContacts.ImageID, ImageContacts.ContactID).
|
||||
VALUES(imageId, contactId).
|
||||
RETURNING(ImageContacts.AllColumns)
|
||||
|
||||
imageContact := model.ImageContacts{}
|
||||
err := insertImageContactStmt.QueryContext(ctx, m.dbPool, &imageContact)
|
||||
|
||||
return imageContact, err
|
||||
}
|
||||
|
||||
func NewContactModel(db *sql.DB) ContactModel {
|
||||
return ContactModel{dbPool: db}
|
||||
}
|
19
backend/models/database.go
Normal file
@ -0,0 +1,19 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func InitDatabase() (*sql.DB, error) {
|
||||
connection := os.Getenv("DB_CONNECTION")
|
||||
|
||||
if len(connection) == 0 {
|
||||
return nil, errors.New("DB_CONNECTION env was not found.")
|
||||
}
|
||||
|
||||
return sql.Open("postgres", connection)
|
||||
}
|
80
backend/models/events.go
Normal file
@ -0,0 +1,80 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type EventModel struct {
|
||||
dbPool *sql.DB
|
||||
}
|
||||
|
||||
func (m EventModel) Save(ctx context.Context, userId uuid.UUID, event model.Events) (model.Events, error) {
|
||||
// TODO tx here
|
||||
insertEventStmt := Events.
|
||||
INSERT(Events.Name, Events.Description, Events.StartDateTime, Events.EndDateTime).
|
||||
VALUES(event.Name, event.Description, event.StartDateTime, event.EndDateTime).
|
||||
RETURNING(Events.AllColumns)
|
||||
|
||||
insertedEvent := model.Events{}
|
||||
err := insertEventStmt.QueryContext(ctx, m.dbPool, &insertedEvent)
|
||||
|
||||
if err != nil {
|
||||
return insertedEvent, err
|
||||
}
|
||||
|
||||
insertUserEventStmt := UserEvents.
|
||||
INSERT(UserEvents.UserID, UserEvents.EventID).
|
||||
VALUES(userId, insertedEvent.ID)
|
||||
|
||||
_, err = insertUserEventStmt.ExecContext(ctx, m.dbPool)
|
||||
|
||||
return insertedEvent, err
|
||||
}
|
||||
|
||||
func (m EventModel) SaveToImage(ctx context.Context, imageId uuid.UUID, eventId uuid.UUID) (model.ImageEvents, error) {
|
||||
insertImageEventStmt := ImageEvents.
|
||||
INSERT(ImageEvents.ImageID, ImageEvents.EventID).
|
||||
VALUES(imageId, eventId).
|
||||
RETURNING(ImageEvents.AllColumns)
|
||||
|
||||
imageEvent := model.ImageEvents{}
|
||||
err := insertImageEventStmt.QueryContext(ctx, m.dbPool, &imageEvent)
|
||||
|
||||
return imageEvent, err
|
||||
}
|
||||
|
||||
func (m EventModel) UpdateLocation(ctx context.Context, eventId uuid.UUID, locationId uuid.UUID) (model.Events, error) {
|
||||
updateEventLocationStmt := Events.
|
||||
UPDATE(Events.LocationID).
|
||||
SET(locationId).
|
||||
WHERE(Events.ID.EQ(UUID(eventId))).
|
||||
RETURNING(Events.AllColumns)
|
||||
|
||||
updatedEvent := model.Events{}
|
||||
err := updateEventLocationStmt.QueryContext(ctx, m.dbPool, &updatedEvent)
|
||||
|
||||
return updatedEvent, err
|
||||
}
|
||||
|
||||
func (m EventModel) UpdateOrganizer(ctx context.Context, eventId uuid.UUID, organizerId uuid.UUID) (model.Events, error) {
|
||||
updateEventContactStmt := Events.
|
||||
UPDATE(Events.OrganizerID).
|
||||
SET(organizerId).
|
||||
WHERE(Events.ID.EQ(UUID(eventId))).
|
||||
RETURNING(Events.AllColumns)
|
||||
|
||||
updatedEvent := model.Events{}
|
||||
err := updateEventContactStmt.QueryContext(ctx, m.dbPool, &updatedEvent)
|
||||
|
||||
return updatedEvent, err
|
||||
}
|
||||
|
||||
func NewEventModel(db *sql.DB) EventModel {
|
||||
return EventModel{dbPool: db}
|
||||
}
|
161
backend/models/image.go
Normal file
@ -0,0 +1,161 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ImageModel struct {
|
||||
dbPool *sql.DB
|
||||
}
|
||||
|
||||
type ImageData struct {
|
||||
model.UserImages
|
||||
|
||||
Image model.Image
|
||||
}
|
||||
|
||||
type ProcessingImageData struct {
|
||||
model.UserImagesToProcess
|
||||
|
||||
Image model.Image
|
||||
}
|
||||
|
||||
func (m ImageModel) Process(ctx context.Context, userId uuid.UUID, image model.Image) (model.UserImagesToProcess, error) {
|
||||
tx, err := m.dbPool.BeginTx(ctx, nil)
|
||||
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{}
|
||||
err := getToProcessStmt.QueryContext(ctx, m.dbPool, &images)
|
||||
|
||||
if len(images) != 1 {
|
||||
return model.UserImagesToProcess{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
||||
}
|
||||
|
||||
return images[0], err
|
||||
}
|
||||
|
||||
func (m ImageModel) GetToProcessWithData(ctx context.Context, imageId uuid.UUID) (ProcessingImageData, error) {
|
||||
stmt := SELECT(UserImagesToProcess.AllColumns, Image.AllColumns).
|
||||
FROM(
|
||||
UserImagesToProcess.INNER_JOIN(
|
||||
Image, Image.ID.EQ(UserImagesToProcess.ImageID),
|
||||
),
|
||||
).WHERE(UserImagesToProcess.ID.EQ(UUID(imageId)))
|
||||
|
||||
images := []ProcessingImageData{}
|
||||
err := stmt.QueryContext(ctx, m.dbPool, &images)
|
||||
|
||||
if len(images) != 1 {
|
||||
return ProcessingImageData{}, errors.New(fmt.Sprintf("Expected 1, got %d\n", len(images)))
|
||||
}
|
||||
|
||||
return images[0], err
|
||||
}
|
||||
|
||||
func (m ImageModel) FinishProcessing(ctx context.Context, imageId uuid.UUID) (model.UserImages, error) {
|
||||
imageToProcess, err := m.GetToProcess(ctx, imageId)
|
||||
if err != nil {
|
||||
return model.UserImages{}, err
|
||||
}
|
||||
|
||||
tx, err := m.dbPool.Begin()
|
||||
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)
|
||||
if err != nil {
|
||||
return model.UserImages{}, err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
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
|
||||
}
|
||||
|
||||
func (m ImageModel) IsUserAuthorized(ctx context.Context, imageId uuid.UUID, userId uuid.UUID) bool {
|
||||
getImageUserId := UserImages.SELECT(UserImages.UserID).WHERE(UserImages.ImageID.EQ(UUID(imageId)))
|
||||
|
||||
userImage := model.UserImages{}
|
||||
err := getImageUserId.QueryContext(ctx, m.dbPool, &userImage)
|
||||
|
||||
return err != nil && userImage.UserID.String() == userId.String()
|
||||
}
|
||||
|
||||
func NewImageModel(db *sql.DB) ImageModel {
|
||||
return ImageModel{dbPool: db}
|
||||
}
|
33
backend/models/links.go
Normal file
@ -0,0 +1,33 @@
|
||||
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 {
|
||||
if len(links) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
67
backend/models/locations.go
Normal file
@ -0,0 +1,67 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type LocationModel struct {
|
||||
dbPool *sql.DB
|
||||
}
|
||||
|
||||
func (m LocationModel) List(ctx context.Context, userId uuid.UUID) ([]model.Locations, error) {
|
||||
listLocationsStmt := SELECT(Locations.AllColumns).
|
||||
FROM(
|
||||
Locations.
|
||||
INNER_JOIN(UserLocations, UserLocations.LocationID.EQ(Locations.ID)),
|
||||
).
|
||||
WHERE(UserLocations.UserID.EQ(UUID(userId)))
|
||||
|
||||
locations := []model.Locations{}
|
||||
|
||||
err := listLocationsStmt.QueryContext(ctx, m.dbPool, &locations)
|
||||
return locations, err
|
||||
}
|
||||
|
||||
func (m LocationModel) Save(ctx context.Context, userId uuid.UUID, location model.Locations) (model.Locations, error) {
|
||||
insertLocationStmt := Locations.
|
||||
INSERT(Locations.Name, Locations.Address, Locations.Description).
|
||||
VALUES(location.Name, location.Address, location.Description).
|
||||
RETURNING(Locations.AllColumns)
|
||||
|
||||
insertedLocation := model.Locations{}
|
||||
err := insertLocationStmt.QueryContext(ctx, m.dbPool, &insertedLocation)
|
||||
if err != nil {
|
||||
return model.Locations{}, err
|
||||
}
|
||||
|
||||
insertUserLocationStmt := UserLocations.
|
||||
INSERT(UserLocations.UserID, UserLocations.LocationID).
|
||||
VALUES(userId, insertedLocation.ID)
|
||||
|
||||
_, err = insertUserLocationStmt.ExecContext(ctx, m.dbPool)
|
||||
|
||||
return insertedLocation, err
|
||||
}
|
||||
|
||||
func (m LocationModel) SaveToImage(ctx context.Context, imageId uuid.UUID, locationId uuid.UUID) (model.ImageLocations, error) {
|
||||
insertImageLocationStmt := ImageLocations.
|
||||
INSERT(ImageLocations.ImageID, ImageLocations.LocationID).
|
||||
VALUES(imageId, locationId).
|
||||
RETURNING(ImageLocations.AllColumns)
|
||||
|
||||
imageLocation := model.ImageLocations{}
|
||||
err := insertImageLocationStmt.QueryContext(ctx, m.dbPool, &imageLocation)
|
||||
|
||||
return imageLocation, err
|
||||
}
|
||||
|
||||
func NewLocationModel(db *sql.DB) LocationModel {
|
||||
return LocationModel{dbPool: db}
|
||||
}
|
67
backend/models/notes.go
Normal file
@ -0,0 +1,67 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type NoteModel struct {
|
||||
dbPool *sql.DB
|
||||
}
|
||||
|
||||
func (m NoteModel) List(ctx context.Context, userId uuid.UUID) ([]model.Notes, error) {
|
||||
listNotesStmt := SELECT(Notes.AllColumns).
|
||||
FROM(
|
||||
Notes.
|
||||
INNER_JOIN(UserNotes, UserNotes.NoteID.EQ(Notes.ID)),
|
||||
).
|
||||
WHERE(UserNotes.UserID.EQ(UUID(userId)))
|
||||
|
||||
locations := []model.Notes{}
|
||||
|
||||
err := listNotesStmt.QueryContext(ctx, m.dbPool, &locations)
|
||||
return locations, err
|
||||
}
|
||||
|
||||
func (m NoteModel) Save(ctx context.Context, userId uuid.UUID, note model.Notes) (model.Notes, error) {
|
||||
insertNoteStmt := Notes.
|
||||
INSERT(Notes.Name, Notes.Description, Notes.Content).
|
||||
VALUES(note.Name, note.Description, note.Content).
|
||||
RETURNING(Notes.AllColumns)
|
||||
|
||||
insertedNote := model.Notes{}
|
||||
err := insertNoteStmt.QueryContext(ctx, m.dbPool, &insertedNote)
|
||||
if err != nil {
|
||||
return model.Notes{}, err
|
||||
}
|
||||
|
||||
insertUserNoteStmt := UserNotes.
|
||||
INSERT(UserNotes.UserID, UserNotes.NoteID).
|
||||
VALUES(userId, insertedNote.ID)
|
||||
|
||||
_, err = insertUserNoteStmt.ExecContext(ctx, m.dbPool)
|
||||
|
||||
return insertedNote, err
|
||||
}
|
||||
|
||||
func (m NoteModel) SaveToImage(ctx context.Context, imageId uuid.UUID, noteId uuid.UUID) (model.ImageNotes, error) {
|
||||
insertImageNoteStmt := ImageNotes.
|
||||
INSERT(ImageNotes.ImageID, ImageNotes.NoteID).
|
||||
VALUES(imageId, noteId).
|
||||
RETURNING(ImageNotes.AllColumns)
|
||||
|
||||
imageNote := model.ImageNotes{}
|
||||
err := insertImageNoteStmt.QueryContext(ctx, m.dbPool, &imageNote)
|
||||
|
||||
return imageNote, err
|
||||
}
|
||||
|
||||
func NewNoteModel(db *sql.DB) NoteModel {
|
||||
return NoteModel{dbPool: db}
|
||||
}
|
156
backend/models/tags.go
Normal file
@ -0,0 +1,156 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type TagModel struct {
|
||||
dbPool *sql.DB
|
||||
}
|
||||
|
||||
// Raw dogging SQL is kinda based though?
|
||||
//
|
||||
// | nO, usE OrM!!
|
||||
//
|
||||
// | RAW - RAW
|
||||
// | SQL | \ SQL
|
||||
// | GOOD | \ GOOD
|
||||
// | - -
|
||||
// | -- --
|
||||
// | -- --
|
||||
// | ---- IQ ----
|
||||
func (m TagModel) getNonExistantTags(ctx context.Context, userId uuid.UUID, tags []string) ([]string, error) {
|
||||
if len(tags) == 0 {
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
values := ""
|
||||
counter := 1
|
||||
// big big SQL injection problem here?
|
||||
for counter = 1; counter <= len(tags); counter++ {
|
||||
values += fmt.Sprintf("($%d),", counter)
|
||||
}
|
||||
values = values[0 : len(values)-1]
|
||||
|
||||
getNonExistingTags := fmt.Sprintf(`WITH given_tags
|
||||
AS (SELECT given_tags.tag FROM (VALUES `+values+`) AS given_tags (tag)),
|
||||
this_user_tags AS
|
||||
(SELECT id, tag FROM haystack.user_tags WHERE user_tags.user_id = $%d)
|
||||
SELECT given_tags.tag
|
||||
FROM given_tags
|
||||
LEFT OUTER JOIN haystack.user_tags ON haystack.user_tags.tag = given_tags.tag
|
||||
where user_tags.tag is null`, counter)
|
||||
|
||||
getNonExistingTagsStmt, err := m.dbPool.PrepareContext(ctx, getNonExistingTags)
|
||||
defer getNonExistingTagsStmt.Close()
|
||||
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
args := make([]any, counter)
|
||||
for i, v := range tags {
|
||||
args[i] = v
|
||||
}
|
||||
args[counter-1] = userId.String()
|
||||
|
||||
rows, err := getNonExistingTagsStmt.QueryContext(ctx, args...)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
nonExistantTags := make([]string, 0)
|
||||
|
||||
for rows.Next() {
|
||||
var tag string
|
||||
rows.Scan(&tag)
|
||||
|
||||
nonExistantTags = append(nonExistantTags, tag)
|
||||
}
|
||||
|
||||
return nonExistantTags, nil
|
||||
}
|
||||
|
||||
func (m TagModel) Save(ctx context.Context, userId uuid.UUID, tags []string) error {
|
||||
tagsToInsert, err := m.getNonExistantTags(ctx, userId, tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(tagsToInsert) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stmt := UserTags.INSERT(UserTags.UserID, UserTags.Tag)
|
||||
|
||||
for _, tag := range tagsToInsert {
|
||||
stmt = stmt.VALUES(UUID(userId), tag)
|
||||
}
|
||||
|
||||
_, err = stmt.ExecContext(ctx, m.dbPool)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m TagModel) List(ctx context.Context, userId uuid.UUID) ([]model.UserTags, error) {
|
||||
listTagsStmt := UserTags.SELECT(UserTags.AllColumns).WHERE(UserTags.UserID.EQ(UUID(userId)))
|
||||
|
||||
userTags := []model.UserTags{}
|
||||
|
||||
err := listTagsStmt.QueryContext(ctx, m.dbPool, &userTags)
|
||||
|
||||
return userTags, err
|
||||
}
|
||||
|
||||
func (m TagModel) SaveToImage(ctx context.Context, imageId uuid.UUID, tags []string) error {
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
35
backend/models/text.go
Normal file
@ -0,0 +1,35 @@
|
||||
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 {
|
||||
if len(texts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
118
backend/models/user.go
Normal file
@ -0,0 +1,118 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"screenmark/screenmark/.gen/haystack/haystack/model"
|
||||
. "screenmark/screenmark/.gen/haystack/haystack/table"
|
||||
|
||||
. "github.com/go-jet/jet/v2/postgres"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UserModel struct {
|
||||
dbPool *sql.DB
|
||||
}
|
||||
|
||||
type ImageWithProperties struct {
|
||||
ID uuid.UUID
|
||||
|
||||
Image model.Image
|
||||
|
||||
Tags []struct {
|
||||
model.ImageTags
|
||||
Tag model.UserTags
|
||||
}
|
||||
Links []model.ImageLinks
|
||||
Text []model.ImageText
|
||||
|
||||
Locations []model.Locations
|
||||
|
||||
Events []struct {
|
||||
model.Events
|
||||
|
||||
Location *model.Locations
|
||||
Organizer *model.Contacts
|
||||
}
|
||||
|
||||
Notes []model.Notes
|
||||
}
|
||||
|
||||
func getUserIdFromImage(ctx context.Context, dbPool *sql.DB, imageId uuid.UUID) (uuid.UUID, error) {
|
||||
getUserIdStmt := UserImages.SELECT(UserImages.UserID).WHERE(UserImages.ImageID.EQ(UUID(imageId)))
|
||||
|
||||
log.Println(getUserIdStmt.DebugSql())
|
||||
|
||||
userImages := []model.UserImages{}
|
||||
err := getUserIdStmt.QueryContext(ctx, dbPool, &userImages)
|
||||
if err != nil {
|
||||
return uuid.Nil, err
|
||||
}
|
||||
|
||||
if len(userImages) != 1 {
|
||||
return uuid.Nil, errors.New("Expected exactly one choice.")
|
||||
}
|
||||
|
||||
return userImages[0].UserID, nil
|
||||
}
|
||||
|
||||
func (m UserModel) ListWithProperties(ctx context.Context, userId uuid.UUID) ([]ImageWithProperties, error) {
|
||||
listWithPropertiesStmt := SELECT(
|
||||
UserImages.ID.AS("ImageWithProperties.ID"),
|
||||
Image.ID,
|
||||
Image.ImageName,
|
||||
ImageTags.AllColumns,
|
||||
UserTags.AllColumns,
|
||||
ImageText.AllColumns,
|
||||
ImageLinks.AllColumns,
|
||||
ImageLocations.AllColumns,
|
||||
Locations.AllColumns,
|
||||
ImageEvents.AllColumns,
|
||||
Events.AllColumns,
|
||||
ImageContacts.AllColumns,
|
||||
Contacts.AllColumns,
|
||||
ImageNotes.AllColumns,
|
||||
Notes.AllColumns,
|
||||
).
|
||||
FROM(
|
||||
UserImages.INNER_JOIN(Image, Image.ID.EQ(UserImages.ImageID)).
|
||||
LEFT_JOIN(ImageTags, ImageTags.ImageID.EQ(Image.ID)).
|
||||
LEFT_JOIN(UserTags, UserTags.ID.EQ(ImageTags.TagID)).
|
||||
LEFT_JOIN(ImageText, ImageText.ImageID.EQ(Image.ID)).
|
||||
LEFT_JOIN(ImageLinks, ImageLinks.ImageID.EQ(Image.ID)).
|
||||
LEFT_JOIN(ImageLocations, ImageLocations.ImageID.EQ(UserImages.ImageID)).
|
||||
LEFT_JOIN(Locations, Locations.ID.EQ(ImageLocations.LocationID)).
|
||||
LEFT_JOIN(ImageEvents, ImageEvents.ImageID.EQ(UserImages.ImageID)).
|
||||
LEFT_JOIN(Events, Events.ID.EQ(ImageEvents.EventID)).
|
||||
LEFT_JOIN(ImageContacts, ImageContacts.ImageID.EQ(UserImages.ImageID)).
|
||||
LEFT_JOIN(Contacts, Contacts.ID.EQ(ImageContacts.ContactID)).
|
||||
LEFT_JOIN(ImageNotes, ImageNotes.ImageID.EQ(UserImages.ImageID)).
|
||||
LEFT_JOIN(Notes, Notes.ID.EQ(ImageNotes.NoteID))).
|
||||
WHERE(UserImages.UserID.EQ(UUID(userId)))
|
||||
|
||||
fmt.Println(listWithPropertiesStmt.DebugSql())
|
||||
|
||||
images := []ImageWithProperties{}
|
||||
|
||||
err := listWithPropertiesStmt.QueryContext(ctx, m.dbPool, &images)
|
||||
if err != nil {
|
||||
return images, err
|
||||
}
|
||||
return images, err
|
||||
}
|
||||
|
||||
func (m UserModel) GetUserIdFromEmail(ctx context.Context, email string) (uuid.UUID, error) {
|
||||
getUserIdStmt := Users.SELECT(Users.ID).WHERE(Users.Email.EQ(String(email)))
|
||||
|
||||
user := model.Users{}
|
||||
err := getUserIdStmt.QueryContext(ctx, m.dbPool, &user)
|
||||
|
||||
return user.ID, err
|
||||
}
|
||||
|
||||
func NewUserModel(db *sql.DB) UserModel {
|
||||
return UserModel{dbPool: db}
|
||||
}
|
263
backend/schema.sql
Normal file
@ -0,0 +1,263 @@
|
||||
DROP SCHEMA IF EXISTS haystack CASCADE;
|
||||
|
||||
CREATE SCHEMA haystack;
|
||||
|
||||
/* -----| Schema tables |----- */
|
||||
|
||||
CREATE TABLE haystack.users (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
image_name TEXT NOT NULL,
|
||||
image BYTEA NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_images_to_process (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
image_id uuid NOT NULL UNIQUE REFERENCES haystack.image (id),
|
||||
user_id uuid NOT NULL REFERENCES haystack.users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_images (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
image_id uuid NOT NULL UNIQUE REFERENCES haystack.image (id),
|
||||
user_id uuid NOT NULL REFERENCES haystack.users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_tags (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tag VARCHAR(32) UNIQUE NOT NULL,
|
||||
user_id uuid NOT NULL REFERENCES haystack.users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_tags (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tag_id UUID NOT NULL REFERENCES haystack.user_tags (id),
|
||||
image_id UUID NOT NULL REFERENCES haystack.image (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.image (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.image (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.locations (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
address TEXT,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_locations (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
location_id UUID NOT NULL REFERENCES haystack.locations (id),
|
||||
image_id UUID NOT NULL REFERENCES haystack.image (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_locations (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
location_id UUID NOT NULL REFERENCES haystack.locations (id),
|
||||
user_id UUID NOT NULL REFERENCES haystack.users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.contacts (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- It seems name and description are frequent. We could use table inheritance.
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
phone_number TEXT,
|
||||
email TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_contacts (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES haystack.users (id),
|
||||
contact_id UUID NOT NULL REFERENCES haystack.contacts (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_contacts (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
image_id UUID NOT NULL REFERENCES haystack.image (id),
|
||||
contact_id UUID NOT NULL REFERENCES haystack.contacts (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.events (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- It seems name and description are frequent. We could use table inheritance.
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
start_date_time TIMESTAMP,
|
||||
end_date_time TIMESTAMP,
|
||||
|
||||
location_id UUID REFERENCES haystack.locations (id),
|
||||
organizer_id UUID REFERENCES haystack.contacts (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_events (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
event_id UUID NOT NULL REFERENCES haystack.events (id),
|
||||
image_id UUID NOT NULL REFERENCES haystack.image (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_events (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
event_id UUID NOT NULL REFERENCES haystack.events (id),
|
||||
user_id UUID NOT NULL REFERENCES haystack.users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.notes (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- It seems name and description are frequent. We could use table inheritance.
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
content TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.image_notes (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
image_id UUID NOT NULL REFERENCES haystack.image (id),
|
||||
note_id UUID NOT NULL REFERENCES haystack.notes (id)
|
||||
);
|
||||
|
||||
CREATE TABLE haystack.user_notes (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES haystack.users (id),
|
||||
note_id UUID NOT NULL REFERENCES haystack.notes (id)
|
||||
);
|
||||
|
||||
/* -----| Indexes |----- */
|
||||
|
||||
CREATE INDEX user_tags_index ON haystack.user_tags(tag);
|
||||
|
||||
/* -----| 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_to_process
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE notify_new_image();
|
||||
|
||||
/* -----| Test Data |----- */
|
||||
|
||||
-- Insert a user
|
||||
INSERT INTO haystack.users (id, email) VALUES ('1db09f34-b155-4bf2-b606-dda25365fc89', 'me@email.com');
|
||||
|
||||
-- Insert images
|
||||
INSERT INTO haystack.image (id, image_name, image) VALUES
|
||||
('3bd3fa04-e4b4-4ffb-b282-d573a092eb71', 'Sample Image 1', 'sample_image_1_bytes'),
|
||||
('f4560a78-d5d3-433e-8d90-b75c66e25423', 'Sample Image 2', 'sample_image_2_bytes');
|
||||
|
||||
-- Insert user images to process
|
||||
INSERT INTO haystack.user_images_to_process (id, image_id, user_id) VALUES
|
||||
('abe3679c-e787-4670-b5da-570453938f18', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71', '1db09f34-b155-4bf2-b606-dda25365fc89'),
|
||||
('8f3727e8-03fa-49bf-b0fe-ba8762df0902', 'f4560a78-d5d3-433e-8d90-b75c66e25423', '1db09f34-b155-4bf2-b606-dda25365fc89');
|
||||
|
||||
-- Insert user images
|
||||
INSERT INTO haystack.user_images (id, image_id, user_id) VALUES
|
||||
('28ade3a5-30c0-4f0a-93ff-5d062ba5c253', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71', '1db09f34-b155-4bf2-b606-dda25365fc89'),
|
||||
('c9425f01-a496-4c0a-919e-54b58c8ba600', 'f4560a78-d5d3-433e-8d90-b75c66e25423', '1db09f34-b155-4bf2-b606-dda25365fc89');
|
||||
|
||||
-- Insert user tags
|
||||
INSERT INTO haystack.user_tags (id, tag, user_id) VALUES
|
||||
('118c9491-a1ea-4930-88ee-33edfbc61cd3', 'vacation', '1db09f34-b155-4bf2-b606-dda25365fc89'),
|
||||
('c3e8c00a-4af6-45c6-acc3-53aa7ce2024a', 'family', '1db09f34-b155-4bf2-b606-dda25365fc89');
|
||||
|
||||
-- Insert image tags
|
||||
INSERT INTO haystack.image_tags (id, tag_id, image_id) VALUES
|
||||
('38ec5481-7b09-4e50-98b8-a85bbd5f6c6e', '118c9491-a1ea-4930-88ee-33edfbc61cd3', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71'),
|
||||
('9d64f58e-1d61-4c97-ae8b-a38bc3519fe1', 'c3e8c00a-4af6-45c6-acc3-53aa7ce2024a', 'f4560a78-d5d3-433e-8d90-b75c66e25423');
|
||||
|
||||
-- Insert image text
|
||||
INSERT INTO haystack.image_text (id, image_text, image_id) VALUES
|
||||
('fdd7a9f4-2a9a-494e-89d2-a63df8e45d62', 'Sample text for image 1', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71'),
|
||||
('95516f15-575c-485b-92ab-22eb18a306c1', 'Sample text for image 2', 'f4560a78-d5d3-433e-8d90-b75c66e25423');
|
||||
|
||||
-- Insert image links
|
||||
INSERT INTO haystack.image_links (id, link, image_id) VALUES
|
||||
('bbcc284f-c1f6-47ac-8d54-65b7729f03be', 'http://example.com/image1', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71'),
|
||||
('7391b2d1-6141-4195-8a4c-9c8ba4491b5a', 'http://example.com/image2', 'f4560a78-d5d3-433e-8d90-b75c66e25423');
|
||||
|
||||
-- Insert locations
|
||||
INSERT INTO haystack.locations (id, name, address, description) VALUES
|
||||
('5ac6f116-c21a-408b-9d2b-e8227a9a8503', 'Sample Location 1', '123 Sample St', 'A sample location'),
|
||||
('cd4b1815-5019-406d-9f1d-e9e5ac34c5f1', 'Sample Location 2', '456 Sample Ave', 'Another sample location');
|
||||
|
||||
-- Insert image locations
|
||||
INSERT INTO haystack.image_locations (id, location_id, image_id) VALUES
|
||||
('0e0c5cc2-b5b3-4b26-9d9c-2517b9358eb3', '5ac6f116-c21a-408b-9d2b-e8227a9a8503', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71'),
|
||||
('98facc74-cfc0-41cd-87e1-5e3822ae3407', 'cd4b1815-5019-406d-9f1d-e9e5ac34c5f1', 'f4560a78-d5d3-433e-8d90-b75c66e25423');
|
||||
|
||||
-- Insert user locations
|
||||
INSERT INTO haystack.user_locations (id, location_id, user_id) VALUES
|
||||
('1427ca1c-293f-4fab-b813-2acf145715f5', '5ac6f116-c21a-408b-9d2b-e8227a9a8503', '1db09f34-b155-4bf2-b606-dda25365fc89'),
|
||||
('343f9321-f63d-4248-aaab-3a1264d9cb5e', 'cd4b1815-5019-406d-9f1d-e9e5ac34c5f1', '1db09f34-b155-4bf2-b606-dda25365fc89');
|
||||
|
||||
-- Insert contacts
|
||||
INSERT INTO haystack.contacts (id, name, description, phone_number, email) VALUES
|
||||
('943be2ab-4db4-4e4e-bd1c-b78ad96df0d1', 'Contact 1', 'Sample contact description', '123-456-7890', 'contact1@example.com'),
|
||||
('09e2bf18-09b7-4553-971e-45136bd5b12f', 'Contact 2', 'Another sample contact description', '098-765-4321', 'contact2@example.com');
|
||||
|
||||
-- Insert user contacts
|
||||
INSERT INTO haystack.user_contacts (id, user_id, contact_id) VALUES
|
||||
('d74125e4-cbe4-4b83-8432-e0a3206af91c', '1db09f34-b155-4bf2-b606-dda25365fc89', '943be2ab-4db4-4e4e-bd1c-b78ad96df0d1'),
|
||||
('46e8cbd4-46a6-4499-9575-d3aad003fd1c', '1db09f34-b155-4bf2-b606-dda25365fc89', '09e2bf18-09b7-4553-971e-45136bd5b12f');
|
||||
|
||||
-- Insert image contacts
|
||||
INSERT INTO haystack.image_contacts (id, image_id, contact_id) VALUES
|
||||
('db075381-e89b-4582-800e-07561f9139e8', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71', '943be2ab-4db4-4e4e-bd1c-b78ad96df0d1'),
|
||||
('7384970d-3d3c-4e29-b158-edf200c53169', 'f4560a78-d5d3-433e-8d90-b75c66e25423', '09e2bf18-09b7-4553-971e-45136bd5b12f');
|
||||
|
||||
-- Insert events
|
||||
INSERT INTO haystack.events (id, name, description, start_date_time, end_date_time, location_id, organizer_id) VALUES
|
||||
('24a9dcbc-f8dc-4fca-835b-7ea57850d0b7', 'Sample Event 1', 'A sample event description', '2023-01-01 10:00:00', '2023-01-01 12:00:00', '5ac6f116-c21a-408b-9d2b-e8227a9a8503', '943be2ab-4db4-4e4e-bd1c-b78ad96df0d1'),
|
||||
('9cb6b0ae-3b02-4343-9858-5a07dd248562', 'Sample Event 2', 'Another sample event description', '2023-02-01 14:00:00', '2023-02-01 16:00:00', 'cd4b1815-5019-406d-9f1d-e9e5ac34c5f1', '09e2bf18-09b7-4553-971e-45136bd5b12f');
|
||||
|
||||
-- Insert image events
|
||||
INSERT INTO haystack.image_events (id, event_id, image_id) VALUES
|
||||
('5268a005-b3eb-4a30-8823-c8e9666507bb', '24a9dcbc-f8dc-4fca-835b-7ea57850d0b7', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71'),
|
||||
('9d6d4d26-c2a2-427f-92ed-34dc8c2d3e5f', '9cb6b0ae-3b02-4343-9858-5a07dd248562', 'f4560a78-d5d3-433e-8d90-b75c66e25423');
|
||||
|
||||
-- Insert user events
|
||||
INSERT INTO haystack.user_events (id, event_id, user_id) VALUES
|
||||
('16d815e4-6387-4fe9-b31d-5baff0567345', '24a9dcbc-f8dc-4fca-835b-7ea57850d0b7', '1db09f34-b155-4bf2-b606-dda25365fc89'),
|
||||
('43078366-d265-4ff9-9210-e11680bd6bcd', '9cb6b0ae-3b02-4343-9858-5a07dd248562', '1db09f34-b155-4bf2-b606-dda25365fc89');
|
||||
|
||||
-- Insert notes
|
||||
INSERT INTO haystack.notes (id, name, description, content) VALUES
|
||||
('6524f6b9-c659-409e-b2a0-abd3c3f5b5bb', 'Sample Note 1', 'A sample note description', 'This is the content of the sample note 1'),
|
||||
('a274b9b3-024f-457d-b4a0-d4535c2cca54', 'Sample Note 2', 'Another sample note description', 'This is the content of the sample note 2');
|
||||
|
||||
-- Insert image notes
|
||||
INSERT INTO haystack.image_notes (id, image_id, note_id) VALUES
|
||||
('6062fceb-7b3f-41fb-8509-489218968204', '3bd3fa04-e4b4-4ffb-b282-d573a092eb71', '6524f6b9-c659-409e-b2a0-abd3c3f5b5bb'),
|
||||
('956dd3f6-4513-4cbc-9a5e-03dbec769402', 'f4560a78-d5d3-433e-8d90-b75c66e25423', 'a274b9b3-024f-457d-b4a0-d4535c2cca54');
|
||||
|
||||
-- Insert user notes
|
||||
INSERT INTO haystack.user_notes (id, user_id, note_id) VALUES
|
||||
('e3fa7a74-acbf-4aa9-930b-f10bd8a6ced5', '1db09f34-b155-4bf2-b606-dda25365fc89', '6524f6b9-c659-409e-b2a0-abd3c3f5b5bb'),
|
||||
('ebaef76b-3b78-491c-93f7-19510080284d', '1db09f34-b155-4bf2-b606-dda25365fc89', 'a274b9b3-024f-457d-b4a0-d4535c2cca54');
|
75
backend/tools.json
Normal file
@ -0,0 +1,75 @@
|
||||
[
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "createLocation",
|
||||
"description": "Creates a location. No not use if you think an existing location is suitable!",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "listLocations",
|
||||
"description": "Lists the locations available",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "createEvent",
|
||||
"description": "Creates a new event",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"startDateTime": {
|
||||
"type": "string",
|
||||
"description": "The start time as an ISO string"
|
||||
},
|
||||
"endDateTime": {
|
||||
"type": "string",
|
||||
"description": "The end time as an ISO string"
|
||||
},
|
||||
"locationId": {
|
||||
"type": "string",
|
||||
"description": "The ID of the location, available by listLocations"
|
||||
},
|
||||
"organizerName": {
|
||||
"type": "string",
|
||||
"description": "The name of the organizer"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "finish",
|
||||
"description": "Nothing else to do, call this function.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
17
biome.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.5.3/schema.json",
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 4
|
||||
}
|
||||
}
|
2
.gitignore → frontend/.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
.env
|
||||
db
|
||||
screenmark
|
||||
node_modules
|
||||
dist
|
BIN
frontend/bun.lockb
Executable file
17
frontend/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/assets/logo.svg" />
|
||||
<title>Tauri + Solid + Typescript App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="/src/index.tsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
43
frontend/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "haystack",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"tauri": "tauri",
|
||||
"lint": "bunx @biomejs/biome lint .",
|
||||
"format": "bunx @biomejs/biome format . --write"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "^0.13.9",
|
||||
"@kobalte/tailwindcss": "^0.9.0",
|
||||
"@solidjs/router": "^0.15.3",
|
||||
"@tabler/icons-solidjs": "^3.30.0",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-dialog": "~2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"clsx": "^2.1.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"solid-js": "^1.9.3",
|
||||
"solid-motionone": "^1.0.3",
|
||||
"tailwind-scrollbar-hide": "^2.0.0",
|
||||
"valibot": "^1.0.0-rc.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.9.4",
|
||||
"@tauri-apps/cli": "^2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-cli": "^11.0.0",
|
||||
"tailwindcss": "3.4.0",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.0.3",
|
||||
"vite-plugin-solid": "^2.11.0"
|
||||
}
|
||||
}
|
6
frontend/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
6
frontend/public/tauri.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="206" height="231" viewBox="0 0 206 231" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M143.143 84C143.143 96.1503 133.293 106 121.143 106C108.992 106 99.1426 96.1503 99.1426 84C99.1426 71.8497 108.992 62 121.143 62C133.293 62 143.143 71.8497 143.143 84Z" fill="#FFC131"/>
|
||||
<ellipse cx="84.1426" cy="147" rx="22" ry="22" transform="rotate(180 84.1426 147)" fill="#24C8DB"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M166.738 154.548C157.86 160.286 148.023 164.269 137.757 166.341C139.858 160.282 141 153.774 141 147C141 144.543 140.85 142.121 140.558 139.743C144.975 138.204 149.215 136.139 153.183 133.575C162.73 127.404 170.292 118.608 174.961 108.244C179.63 97.8797 181.207 86.3876 179.502 75.1487C177.798 63.9098 172.884 53.4021 165.352 44.8883C157.82 36.3744 147.99 30.2165 137.042 27.1546C126.095 24.0926 114.496 24.2568 103.64 27.6274C92.7839 30.998 83.1319 37.4317 75.8437 46.1553C74.9102 47.2727 74.0206 48.4216 73.176 49.5993C61.9292 50.8488 51.0363 54.0318 40.9629 58.9556C44.2417 48.4586 49.5653 38.6591 56.679 30.1442C67.0505 17.7298 80.7861 8.57426 96.2354 3.77762C111.685 -1.01901 128.19 -1.25267 143.769 3.10474C159.348 7.46215 173.337 16.2252 184.056 28.3411C194.775 40.457 201.767 55.4101 204.193 71.404C206.619 87.3978 204.374 103.752 197.73 118.501C191.086 133.25 180.324 145.767 166.738 154.548ZM41.9631 74.275L62.5557 76.8042C63.0459 72.813 63.9401 68.9018 65.2138 65.1274C57.0465 67.0016 49.2088 70.087 41.9631 74.275Z" fill="#FFC131"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4045 76.4519C47.3493 70.6709 57.2677 66.6712 67.6171 64.6132C65.2774 70.9669 64 77.8343 64 85.0001C64 87.1434 64.1143 89.26 64.3371 91.3442C60.0093 92.8732 55.8533 94.9092 51.9599 97.4256C42.4128 103.596 34.8505 112.392 30.1816 122.756C25.5126 133.12 23.9357 144.612 25.6403 155.851C27.3449 167.09 32.2584 177.598 39.7906 186.112C47.3227 194.626 57.153 200.784 68.1003 203.846C79.0476 206.907 90.6462 206.743 101.502 203.373C112.359 200.002 122.011 193.568 129.299 184.845C130.237 183.722 131.131 182.567 131.979 181.383C143.235 180.114 154.132 176.91 164.205 171.962C160.929 182.49 155.596 192.319 148.464 200.856C138.092 213.27 124.357 222.426 108.907 227.222C93.458 232.019 76.9524 232.253 61.3736 227.895C45.7948 223.538 31.8055 214.775 21.0867 202.659C10.3679 190.543 3.37557 175.59 0.949823 159.596C-1.47592 143.602 0.768139 127.248 7.41237 112.499C14.0566 97.7497 24.8183 85.2327 38.4045 76.4519ZM163.062 156.711L163.062 156.711C162.954 156.773 162.846 156.835 162.738 156.897C162.846 156.835 162.954 156.773 163.062 156.711Z" fill="#24C8DB"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
1
frontend/public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
7
frontend/src-tauri/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Generated by Tauri
|
||||
# will have schema files for capabilities auto-completion
|
||||
/gen/schemas
|
5715
frontend/src-tauri/Cargo.lock
generated
Normal file
31
frontend/src-tauri/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "haystack"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
# The `_lib` suffix may seem redundant but it is necessary
|
||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||
name = "haystack_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = ["macos-private-api"] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tauri-plugin-dialog = "2"
|
||||
notify = "6.1.1"
|
||||
base64 = "0.21.7"
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
cocoa = "0.26"
|
3
frontend/src-tauri/build.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
12
frontend/src-tauri/capabilities/default.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default",
|
||||
"dialog:default",
|
||||
"core:window:allow-start-dragging"
|
||||
]
|
||||
}
|
BIN
frontend/src-tauri/icons/128x128.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
frontend/src-tauri/icons/128x128@2x.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
frontend/src-tauri/icons/32x32.png
Normal file
After Width: | Height: | Size: 974 B |
BIN
frontend/src-tauri/icons/Square107x107Logo.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
frontend/src-tauri/icons/Square142x142Logo.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
frontend/src-tauri/icons/Square150x150Logo.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
frontend/src-tauri/icons/Square284x284Logo.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
frontend/src-tauri/icons/Square30x30Logo.png
Normal file
After Width: | Height: | Size: 903 B |
BIN
frontend/src-tauri/icons/Square310x310Logo.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
frontend/src-tauri/icons/Square44x44Logo.png
Normal file
After Width: | Height: | Size: 1.3 KiB |