Lots of progress on public AP interface
Some checks failed
/ docker (push) Failing after 2m57s

- Read handler for create activities (notes only so far)
- Read handler for note objects
- Structure laid out for other objects, activities and collections
- DB structure for activities created
- Update access logging

TODO: Create collections type in DB to describe a collection group
This commit is contained in:
Melody Becker 2025-05-04 22:08:06 +02:00
parent cfa0566c6d
commit 12c9e17c4b
Signed by: mstar
SSH key fingerprint: SHA256:vkXfS9FG2pVNVfvDrzd1VW9n8VJzqqdKQGljxxX8uK8
24 changed files with 1327 additions and 206 deletions

7
go.mod
View file

@ -1,12 +1,10 @@
module git.mstar.dev/mstar/linstrom
go 1.23.0
toolchain go1.23.7
go 1.24.2
require (
git.mstar.dev/mstar/canvas v0.13.1
git.mstar.dev/mstar/goutils v1.13.0
git.mstar.dev/mstar/goutils v1.14.0
github.com/BurntSushi/toml v1.5.0
github.com/dgraph-io/ristretto v0.2.0
github.com/eko/gocache/lib/v4 v4.2.0
@ -38,6 +36,7 @@ require (
require (
filippo.io/edwards25519 v1.1.0 // indirect
git.mstar.dev/mstar/treeificator v0.0.0-20250423153311-dc24f39ca3c9 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect

4
go.sum
View file

@ -39,6 +39,10 @@ git.mstar.dev/mstar/goutils v1.12.3 h1:Wx7i8/a99Cp+Y/XcXgqQr0r9cSsJu7QkWBlKyprTH
git.mstar.dev/mstar/goutils v1.12.3/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
git.mstar.dev/mstar/goutils v1.13.0 h1:j2AA3izqTumZyUgC2wi/JdIZAtnDQLve2iexI5kWMgM=
git.mstar.dev/mstar/goutils v1.13.0/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
git.mstar.dev/mstar/goutils v1.14.0 h1:XxhP528dwyaDnnVX3Si3UF3bQgNGZQOQxNTGD86mlEo=
git.mstar.dev/mstar/goutils v1.14.0/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA=
git.mstar.dev/mstar/treeificator v0.0.0-20250423153311-dc24f39ca3c9 h1:XXfafmvV6B0Yl6ucec0Gy+VgwWRI+tzLMSyuOgRpXDw=
git.mstar.dev/mstar/treeificator v0.0.0-20250423153311-dc24f39ca3c9/go.mod h1:7wp7WyRRR5gUSur0Ur7RheG1io2QdVoxJPRIrN3pxqo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=

View file

@ -0,0 +1,394 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dbgen
import (
"context"
"database/sql"
"git.mstar.dev/mstar/linstrom/storage-new/models"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
)
func newActivitystreamsActivity(db *gorm.DB, opts ...gen.DOOption) activitystreamsActivity {
_activitystreamsActivity := activitystreamsActivity{}
_activitystreamsActivity.activitystreamsActivityDo.UseDB(db, opts...)
_activitystreamsActivity.activitystreamsActivityDo.UseModel(&models.ActivitystreamsActivity{})
tableName := _activitystreamsActivity.activitystreamsActivityDo.TableName()
_activitystreamsActivity.ALL = field.NewAsterisk(tableName)
_activitystreamsActivity.Id = field.NewString(tableName, "id")
_activitystreamsActivity.Type = field.NewString(tableName, "type")
_activitystreamsActivity.ObjectId = field.NewString(tableName, "object_id")
_activitystreamsActivity.ObjectType = field.NewUint32(tableName, "object_type")
_activitystreamsActivity.fillFieldMap()
return _activitystreamsActivity
}
type activitystreamsActivity struct {
activitystreamsActivityDo
ALL field.Asterisk
Id field.String
Type field.String
ObjectId field.String
ObjectType field.Uint32
fieldMap map[string]field.Expr
}
func (a activitystreamsActivity) Table(newTableName string) *activitystreamsActivity {
a.activitystreamsActivityDo.UseTable(newTableName)
return a.updateTableName(newTableName)
}
func (a activitystreamsActivity) As(alias string) *activitystreamsActivity {
a.activitystreamsActivityDo.DO = *(a.activitystreamsActivityDo.As(alias).(*gen.DO))
return a.updateTableName(alias)
}
func (a *activitystreamsActivity) updateTableName(table string) *activitystreamsActivity {
a.ALL = field.NewAsterisk(table)
a.Id = field.NewString(table, "id")
a.Type = field.NewString(table, "type")
a.ObjectId = field.NewString(table, "object_id")
a.ObjectType = field.NewUint32(table, "object_type")
a.fillFieldMap()
return a
}
func (a *activitystreamsActivity) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := a.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (a *activitystreamsActivity) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 4)
a.fieldMap["id"] = a.Id
a.fieldMap["type"] = a.Type
a.fieldMap["object_id"] = a.ObjectId
a.fieldMap["object_type"] = a.ObjectType
}
func (a activitystreamsActivity) clone(db *gorm.DB) activitystreamsActivity {
a.activitystreamsActivityDo.ReplaceConnPool(db.Statement.ConnPool)
return a
}
func (a activitystreamsActivity) replaceDB(db *gorm.DB) activitystreamsActivity {
a.activitystreamsActivityDo.ReplaceDB(db)
return a
}
type activitystreamsActivityDo struct{ gen.DO }
type IActivitystreamsActivityDo interface {
gen.SubQuery
Debug() IActivitystreamsActivityDo
WithContext(ctx context.Context) IActivitystreamsActivityDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() IActivitystreamsActivityDo
WriteDB() IActivitystreamsActivityDo
As(alias string) gen.Dao
Session(config *gorm.Session) IActivitystreamsActivityDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) IActivitystreamsActivityDo
Not(conds ...gen.Condition) IActivitystreamsActivityDo
Or(conds ...gen.Condition) IActivitystreamsActivityDo
Select(conds ...field.Expr) IActivitystreamsActivityDo
Where(conds ...gen.Condition) IActivitystreamsActivityDo
Order(conds ...field.Expr) IActivitystreamsActivityDo
Distinct(cols ...field.Expr) IActivitystreamsActivityDo
Omit(cols ...field.Expr) IActivitystreamsActivityDo
Join(table schema.Tabler, on ...field.Expr) IActivitystreamsActivityDo
LeftJoin(table schema.Tabler, on ...field.Expr) IActivitystreamsActivityDo
RightJoin(table schema.Tabler, on ...field.Expr) IActivitystreamsActivityDo
Group(cols ...field.Expr) IActivitystreamsActivityDo
Having(conds ...gen.Condition) IActivitystreamsActivityDo
Limit(limit int) IActivitystreamsActivityDo
Offset(offset int) IActivitystreamsActivityDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) IActivitystreamsActivityDo
Unscoped() IActivitystreamsActivityDo
Create(values ...*models.ActivitystreamsActivity) error
CreateInBatches(values []*models.ActivitystreamsActivity, batchSize int) error
Save(values ...*models.ActivitystreamsActivity) error
First() (*models.ActivitystreamsActivity, error)
Take() (*models.ActivitystreamsActivity, error)
Last() (*models.ActivitystreamsActivity, error)
Find() ([]*models.ActivitystreamsActivity, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.ActivitystreamsActivity, err error)
FindInBatches(result *[]*models.ActivitystreamsActivity, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*models.ActivitystreamsActivity) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) IActivitystreamsActivityDo
Assign(attrs ...field.AssignExpr) IActivitystreamsActivityDo
Joins(fields ...field.RelationField) IActivitystreamsActivityDo
Preload(fields ...field.RelationField) IActivitystreamsActivityDo
FirstOrInit() (*models.ActivitystreamsActivity, error)
FirstOrCreate() (*models.ActivitystreamsActivity, error)
FindByPage(offset int, limit int) (result []*models.ActivitystreamsActivity, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Rows() (*sql.Rows, error)
Row() *sql.Row
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) IActivitystreamsActivityDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (a activitystreamsActivityDo) Debug() IActivitystreamsActivityDo {
return a.withDO(a.DO.Debug())
}
func (a activitystreamsActivityDo) WithContext(ctx context.Context) IActivitystreamsActivityDo {
return a.withDO(a.DO.WithContext(ctx))
}
func (a activitystreamsActivityDo) ReadDB() IActivitystreamsActivityDo {
return a.Clauses(dbresolver.Read)
}
func (a activitystreamsActivityDo) WriteDB() IActivitystreamsActivityDo {
return a.Clauses(dbresolver.Write)
}
func (a activitystreamsActivityDo) Session(config *gorm.Session) IActivitystreamsActivityDo {
return a.withDO(a.DO.Session(config))
}
func (a activitystreamsActivityDo) Clauses(conds ...clause.Expression) IActivitystreamsActivityDo {
return a.withDO(a.DO.Clauses(conds...))
}
func (a activitystreamsActivityDo) Returning(value interface{}, columns ...string) IActivitystreamsActivityDo {
return a.withDO(a.DO.Returning(value, columns...))
}
func (a activitystreamsActivityDo) Not(conds ...gen.Condition) IActivitystreamsActivityDo {
return a.withDO(a.DO.Not(conds...))
}
func (a activitystreamsActivityDo) Or(conds ...gen.Condition) IActivitystreamsActivityDo {
return a.withDO(a.DO.Or(conds...))
}
func (a activitystreamsActivityDo) Select(conds ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Select(conds...))
}
func (a activitystreamsActivityDo) Where(conds ...gen.Condition) IActivitystreamsActivityDo {
return a.withDO(a.DO.Where(conds...))
}
func (a activitystreamsActivityDo) Order(conds ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Order(conds...))
}
func (a activitystreamsActivityDo) Distinct(cols ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Distinct(cols...))
}
func (a activitystreamsActivityDo) Omit(cols ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Omit(cols...))
}
func (a activitystreamsActivityDo) Join(table schema.Tabler, on ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Join(table, on...))
}
func (a activitystreamsActivityDo) LeftJoin(table schema.Tabler, on ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.LeftJoin(table, on...))
}
func (a activitystreamsActivityDo) RightJoin(table schema.Tabler, on ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.RightJoin(table, on...))
}
func (a activitystreamsActivityDo) Group(cols ...field.Expr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Group(cols...))
}
func (a activitystreamsActivityDo) Having(conds ...gen.Condition) IActivitystreamsActivityDo {
return a.withDO(a.DO.Having(conds...))
}
func (a activitystreamsActivityDo) Limit(limit int) IActivitystreamsActivityDo {
return a.withDO(a.DO.Limit(limit))
}
func (a activitystreamsActivityDo) Offset(offset int) IActivitystreamsActivityDo {
return a.withDO(a.DO.Offset(offset))
}
func (a activitystreamsActivityDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IActivitystreamsActivityDo {
return a.withDO(a.DO.Scopes(funcs...))
}
func (a activitystreamsActivityDo) Unscoped() IActivitystreamsActivityDo {
return a.withDO(a.DO.Unscoped())
}
func (a activitystreamsActivityDo) Create(values ...*models.ActivitystreamsActivity) error {
if len(values) == 0 {
return nil
}
return a.DO.Create(values)
}
func (a activitystreamsActivityDo) CreateInBatches(values []*models.ActivitystreamsActivity, batchSize int) error {
return a.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (a activitystreamsActivityDo) Save(values ...*models.ActivitystreamsActivity) error {
if len(values) == 0 {
return nil
}
return a.DO.Save(values)
}
func (a activitystreamsActivityDo) First() (*models.ActivitystreamsActivity, error) {
if result, err := a.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.ActivitystreamsActivity), nil
}
}
func (a activitystreamsActivityDo) Take() (*models.ActivitystreamsActivity, error) {
if result, err := a.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.ActivitystreamsActivity), nil
}
}
func (a activitystreamsActivityDo) Last() (*models.ActivitystreamsActivity, error) {
if result, err := a.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.ActivitystreamsActivity), nil
}
}
func (a activitystreamsActivityDo) Find() ([]*models.ActivitystreamsActivity, error) {
result, err := a.DO.Find()
return result.([]*models.ActivitystreamsActivity), err
}
func (a activitystreamsActivityDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.ActivitystreamsActivity, err error) {
buf := make([]*models.ActivitystreamsActivity, 0, batchSize)
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (a activitystreamsActivityDo) FindInBatches(result *[]*models.ActivitystreamsActivity, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return a.DO.FindInBatches(result, batchSize, fc)
}
func (a activitystreamsActivityDo) Attrs(attrs ...field.AssignExpr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Attrs(attrs...))
}
func (a activitystreamsActivityDo) Assign(attrs ...field.AssignExpr) IActivitystreamsActivityDo {
return a.withDO(a.DO.Assign(attrs...))
}
func (a activitystreamsActivityDo) Joins(fields ...field.RelationField) IActivitystreamsActivityDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Joins(_f))
}
return &a
}
func (a activitystreamsActivityDo) Preload(fields ...field.RelationField) IActivitystreamsActivityDo {
for _, _f := range fields {
a = *a.withDO(a.DO.Preload(_f))
}
return &a
}
func (a activitystreamsActivityDo) FirstOrInit() (*models.ActivitystreamsActivity, error) {
if result, err := a.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.ActivitystreamsActivity), nil
}
}
func (a activitystreamsActivityDo) FirstOrCreate() (*models.ActivitystreamsActivity, error) {
if result, err := a.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.ActivitystreamsActivity), nil
}
}
func (a activitystreamsActivityDo) FindByPage(offset int, limit int) (result []*models.ActivitystreamsActivity, count int64, err error) {
result, err = a.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = a.Offset(-1).Limit(-1).Count()
return
}
func (a activitystreamsActivityDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = a.Count()
if err != nil {
return
}
err = a.Offset(offset).Limit(limit).Scan(result)
return
}
func (a activitystreamsActivityDo) Scan(result interface{}) (err error) {
return a.DO.Scan(result)
}
func (a activitystreamsActivityDo) Delete(models ...*models.ActivitystreamsActivity) (result gen.ResultInfo, err error) {
return a.DO.Delete(models)
}
func (a *activitystreamsActivityDo) withDO(do gen.Dao) *activitystreamsActivityDo {
a.DO = *do.(*gen.DO)
return a
}

View file

@ -0,0 +1,390 @@
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
package dbgen
import (
"context"
"database/sql"
"git.mstar.dev/mstar/linstrom/storage-new/models"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"gorm.io/gorm/schema"
"gorm.io/gen"
"gorm.io/gen/field"
"gorm.io/plugin/dbresolver"
)
func newCollection(db *gorm.DB, opts ...gen.DOOption) collection {
_collection := collection{}
_collection.collectionDo.UseDB(db, opts...)
_collection.collectionDo.UseModel(&models.Collection{})
tableName := _collection.collectionDo.TableName()
_collection.ALL = field.NewAsterisk(tableName)
_collection.Id = field.NewString(tableName, "id")
_collection.TargetId = field.NewString(tableName, "target_id")
_collection.TargetType = field.NewUint32(tableName, "target_type")
_collection.fillFieldMap()
return _collection
}
type collection struct {
collectionDo
ALL field.Asterisk
Id field.String
TargetId field.String
TargetType field.Uint32
fieldMap map[string]field.Expr
}
func (c collection) Table(newTableName string) *collection {
c.collectionDo.UseTable(newTableName)
return c.updateTableName(newTableName)
}
func (c collection) As(alias string) *collection {
c.collectionDo.DO = *(c.collectionDo.As(alias).(*gen.DO))
return c.updateTableName(alias)
}
func (c *collection) updateTableName(table string) *collection {
c.ALL = field.NewAsterisk(table)
c.Id = field.NewString(table, "id")
c.TargetId = field.NewString(table, "target_id")
c.TargetType = field.NewUint32(table, "target_type")
c.fillFieldMap()
return c
}
func (c *collection) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
_f, ok := c.fieldMap[fieldName]
if !ok || _f == nil {
return nil, false
}
_oe, ok := _f.(field.OrderExpr)
return _oe, ok
}
func (c *collection) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 3)
c.fieldMap["id"] = c.Id
c.fieldMap["target_id"] = c.TargetId
c.fieldMap["target_type"] = c.TargetType
}
func (c collection) clone(db *gorm.DB) collection {
c.collectionDo.ReplaceConnPool(db.Statement.ConnPool)
return c
}
func (c collection) replaceDB(db *gorm.DB) collection {
c.collectionDo.ReplaceDB(db)
return c
}
type collectionDo struct{ gen.DO }
type ICollectionDo interface {
gen.SubQuery
Debug() ICollectionDo
WithContext(ctx context.Context) ICollectionDo
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
ReplaceDB(db *gorm.DB)
ReadDB() ICollectionDo
WriteDB() ICollectionDo
As(alias string) gen.Dao
Session(config *gorm.Session) ICollectionDo
Columns(cols ...field.Expr) gen.Columns
Clauses(conds ...clause.Expression) ICollectionDo
Not(conds ...gen.Condition) ICollectionDo
Or(conds ...gen.Condition) ICollectionDo
Select(conds ...field.Expr) ICollectionDo
Where(conds ...gen.Condition) ICollectionDo
Order(conds ...field.Expr) ICollectionDo
Distinct(cols ...field.Expr) ICollectionDo
Omit(cols ...field.Expr) ICollectionDo
Join(table schema.Tabler, on ...field.Expr) ICollectionDo
LeftJoin(table schema.Tabler, on ...field.Expr) ICollectionDo
RightJoin(table schema.Tabler, on ...field.Expr) ICollectionDo
Group(cols ...field.Expr) ICollectionDo
Having(conds ...gen.Condition) ICollectionDo
Limit(limit int) ICollectionDo
Offset(offset int) ICollectionDo
Count() (count int64, err error)
Scopes(funcs ...func(gen.Dao) gen.Dao) ICollectionDo
Unscoped() ICollectionDo
Create(values ...*models.Collection) error
CreateInBatches(values []*models.Collection, batchSize int) error
Save(values ...*models.Collection) error
First() (*models.Collection, error)
Take() (*models.Collection, error)
Last() (*models.Collection, error)
Find() ([]*models.Collection, error)
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Collection, err error)
FindInBatches(result *[]*models.Collection, batchSize int, fc func(tx gen.Dao, batch int) error) error
Pluck(column field.Expr, dest interface{}) error
Delete(...*models.Collection) (info gen.ResultInfo, err error)
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
Updates(value interface{}) (info gen.ResultInfo, err error)
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
UpdateFrom(q gen.SubQuery) gen.Dao
Attrs(attrs ...field.AssignExpr) ICollectionDo
Assign(attrs ...field.AssignExpr) ICollectionDo
Joins(fields ...field.RelationField) ICollectionDo
Preload(fields ...field.RelationField) ICollectionDo
FirstOrInit() (*models.Collection, error)
FirstOrCreate() (*models.Collection, error)
FindByPage(offset int, limit int) (result []*models.Collection, count int64, err error)
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
Rows() (*sql.Rows, error)
Row() *sql.Row
Scan(result interface{}) (err error)
Returning(value interface{}, columns ...string) ICollectionDo
UnderlyingDB() *gorm.DB
schema.Tabler
}
func (c collectionDo) Debug() ICollectionDo {
return c.withDO(c.DO.Debug())
}
func (c collectionDo) WithContext(ctx context.Context) ICollectionDo {
return c.withDO(c.DO.WithContext(ctx))
}
func (c collectionDo) ReadDB() ICollectionDo {
return c.Clauses(dbresolver.Read)
}
func (c collectionDo) WriteDB() ICollectionDo {
return c.Clauses(dbresolver.Write)
}
func (c collectionDo) Session(config *gorm.Session) ICollectionDo {
return c.withDO(c.DO.Session(config))
}
func (c collectionDo) Clauses(conds ...clause.Expression) ICollectionDo {
return c.withDO(c.DO.Clauses(conds...))
}
func (c collectionDo) Returning(value interface{}, columns ...string) ICollectionDo {
return c.withDO(c.DO.Returning(value, columns...))
}
func (c collectionDo) Not(conds ...gen.Condition) ICollectionDo {
return c.withDO(c.DO.Not(conds...))
}
func (c collectionDo) Or(conds ...gen.Condition) ICollectionDo {
return c.withDO(c.DO.Or(conds...))
}
func (c collectionDo) Select(conds ...field.Expr) ICollectionDo {
return c.withDO(c.DO.Select(conds...))
}
func (c collectionDo) Where(conds ...gen.Condition) ICollectionDo {
return c.withDO(c.DO.Where(conds...))
}
func (c collectionDo) Order(conds ...field.Expr) ICollectionDo {
return c.withDO(c.DO.Order(conds...))
}
func (c collectionDo) Distinct(cols ...field.Expr) ICollectionDo {
return c.withDO(c.DO.Distinct(cols...))
}
func (c collectionDo) Omit(cols ...field.Expr) ICollectionDo {
return c.withDO(c.DO.Omit(cols...))
}
func (c collectionDo) Join(table schema.Tabler, on ...field.Expr) ICollectionDo {
return c.withDO(c.DO.Join(table, on...))
}
func (c collectionDo) LeftJoin(table schema.Tabler, on ...field.Expr) ICollectionDo {
return c.withDO(c.DO.LeftJoin(table, on...))
}
func (c collectionDo) RightJoin(table schema.Tabler, on ...field.Expr) ICollectionDo {
return c.withDO(c.DO.RightJoin(table, on...))
}
func (c collectionDo) Group(cols ...field.Expr) ICollectionDo {
return c.withDO(c.DO.Group(cols...))
}
func (c collectionDo) Having(conds ...gen.Condition) ICollectionDo {
return c.withDO(c.DO.Having(conds...))
}
func (c collectionDo) Limit(limit int) ICollectionDo {
return c.withDO(c.DO.Limit(limit))
}
func (c collectionDo) Offset(offset int) ICollectionDo {
return c.withDO(c.DO.Offset(offset))
}
func (c collectionDo) Scopes(funcs ...func(gen.Dao) gen.Dao) ICollectionDo {
return c.withDO(c.DO.Scopes(funcs...))
}
func (c collectionDo) Unscoped() ICollectionDo {
return c.withDO(c.DO.Unscoped())
}
func (c collectionDo) Create(values ...*models.Collection) error {
if len(values) == 0 {
return nil
}
return c.DO.Create(values)
}
func (c collectionDo) CreateInBatches(values []*models.Collection, batchSize int) error {
return c.DO.CreateInBatches(values, batchSize)
}
// Save : !!! underlying implementation is different with GORM
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
func (c collectionDo) Save(values ...*models.Collection) error {
if len(values) == 0 {
return nil
}
return c.DO.Save(values)
}
func (c collectionDo) First() (*models.Collection, error) {
if result, err := c.DO.First(); err != nil {
return nil, err
} else {
return result.(*models.Collection), nil
}
}
func (c collectionDo) Take() (*models.Collection, error) {
if result, err := c.DO.Take(); err != nil {
return nil, err
} else {
return result.(*models.Collection), nil
}
}
func (c collectionDo) Last() (*models.Collection, error) {
if result, err := c.DO.Last(); err != nil {
return nil, err
} else {
return result.(*models.Collection), nil
}
}
func (c collectionDo) Find() ([]*models.Collection, error) {
result, err := c.DO.Find()
return result.([]*models.Collection), err
}
func (c collectionDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*models.Collection, err error) {
buf := make([]*models.Collection, 0, batchSize)
err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
defer func() { results = append(results, buf...) }()
return fc(tx, batch)
})
return results, err
}
func (c collectionDo) FindInBatches(result *[]*models.Collection, batchSize int, fc func(tx gen.Dao, batch int) error) error {
return c.DO.FindInBatches(result, batchSize, fc)
}
func (c collectionDo) Attrs(attrs ...field.AssignExpr) ICollectionDo {
return c.withDO(c.DO.Attrs(attrs...))
}
func (c collectionDo) Assign(attrs ...field.AssignExpr) ICollectionDo {
return c.withDO(c.DO.Assign(attrs...))
}
func (c collectionDo) Joins(fields ...field.RelationField) ICollectionDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Joins(_f))
}
return &c
}
func (c collectionDo) Preload(fields ...field.RelationField) ICollectionDo {
for _, _f := range fields {
c = *c.withDO(c.DO.Preload(_f))
}
return &c
}
func (c collectionDo) FirstOrInit() (*models.Collection, error) {
if result, err := c.DO.FirstOrInit(); err != nil {
return nil, err
} else {
return result.(*models.Collection), nil
}
}
func (c collectionDo) FirstOrCreate() (*models.Collection, error) {
if result, err := c.DO.FirstOrCreate(); err != nil {
return nil, err
} else {
return result.(*models.Collection), nil
}
}
func (c collectionDo) FindByPage(offset int, limit int) (result []*models.Collection, count int64, err error) {
result, err = c.Offset(offset).Limit(limit).Find()
if err != nil {
return
}
if size := len(result); 0 < limit && 0 < size && size < limit {
count = int64(size + offset)
return
}
count, err = c.Offset(-1).Limit(-1).Count()
return
}
func (c collectionDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
count, err = c.Count()
if err != nil {
return
}
err = c.Offset(offset).Limit(limit).Scan(result)
return
}
func (c collectionDo) Scan(result interface{}) (err error) {
return c.DO.Scan(result)
}
func (c collectionDo) Delete(models ...*models.Collection) (result gen.ResultInfo, err error) {
return c.DO.Delete(models)
}
func (c *collectionDo) withDO(do gen.Dao) *collectionDo {
c.DO = *do.(*gen.DO)
return c
}

View file

@ -16,39 +16,43 @@ import (
)
var (
Q = new(Query)
AccessToken *accessToken
Emote *emote
Feed *feed
LoginProcessToken *loginProcessToken
MediaMetadata *mediaMetadata
Note *note
NoteEdit *noteEdit
NoteTag *noteTag
NoteToAttachment *noteToAttachment
NoteToBoost *noteToBoost
NoteToEmote *noteToEmote
NoteToFeed *noteToFeed
NoteToPing *noteToPing
Notification *notification
Reaction *reaction
RemoteServer *remoteServer
RemoteServerMetadata *remoteServerMetadata
Role *role
User *user
UserAuthMethod *userAuthMethod
UserInfoField *userInfoField
UserRemoteLinks *userRemoteLinks
UserToBeing *userToBeing
UserToPronoun *userToPronoun
UserToRole *userToRole
UserToTag *userToTag
UserToUserRelation *userToUserRelation
Q = new(Query)
AccessToken *accessToken
ActivitystreamsActivity *activitystreamsActivity
Collection *collection
Emote *emote
Feed *feed
LoginProcessToken *loginProcessToken
MediaMetadata *mediaMetadata
Note *note
NoteEdit *noteEdit
NoteTag *noteTag
NoteToAttachment *noteToAttachment
NoteToBoost *noteToBoost
NoteToEmote *noteToEmote
NoteToFeed *noteToFeed
NoteToPing *noteToPing
Notification *notification
Reaction *reaction
RemoteServer *remoteServer
RemoteServerMetadata *remoteServerMetadata
Role *role
User *user
UserAuthMethod *userAuthMethod
UserInfoField *userInfoField
UserRemoteLinks *userRemoteLinks
UserToBeing *userToBeing
UserToPronoun *userToPronoun
UserToRole *userToRole
UserToTag *userToTag
UserToUserRelation *userToUserRelation
)
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
*Q = *Use(db, opts...)
AccessToken = &Q.AccessToken
ActivitystreamsActivity = &Q.ActivitystreamsActivity
Collection = &Q.Collection
Emote = &Q.Emote
Feed = &Q.Feed
LoginProcessToken = &Q.LoginProcessToken
@ -79,101 +83,107 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
return &Query{
db: db,
AccessToken: newAccessToken(db, opts...),
Emote: newEmote(db, opts...),
Feed: newFeed(db, opts...),
LoginProcessToken: newLoginProcessToken(db, opts...),
MediaMetadata: newMediaMetadata(db, opts...),
Note: newNote(db, opts...),
NoteEdit: newNoteEdit(db, opts...),
NoteTag: newNoteTag(db, opts...),
NoteToAttachment: newNoteToAttachment(db, opts...),
NoteToBoost: newNoteToBoost(db, opts...),
NoteToEmote: newNoteToEmote(db, opts...),
NoteToFeed: newNoteToFeed(db, opts...),
NoteToPing: newNoteToPing(db, opts...),
Notification: newNotification(db, opts...),
Reaction: newReaction(db, opts...),
RemoteServer: newRemoteServer(db, opts...),
RemoteServerMetadata: newRemoteServerMetadata(db, opts...),
Role: newRole(db, opts...),
User: newUser(db, opts...),
UserAuthMethod: newUserAuthMethod(db, opts...),
UserInfoField: newUserInfoField(db, opts...),
UserRemoteLinks: newUserRemoteLinks(db, opts...),
UserToBeing: newUserToBeing(db, opts...),
UserToPronoun: newUserToPronoun(db, opts...),
UserToRole: newUserToRole(db, opts...),
UserToTag: newUserToTag(db, opts...),
UserToUserRelation: newUserToUserRelation(db, opts...),
db: db,
AccessToken: newAccessToken(db, opts...),
ActivitystreamsActivity: newActivitystreamsActivity(db, opts...),
Collection: newCollection(db, opts...),
Emote: newEmote(db, opts...),
Feed: newFeed(db, opts...),
LoginProcessToken: newLoginProcessToken(db, opts...),
MediaMetadata: newMediaMetadata(db, opts...),
Note: newNote(db, opts...),
NoteEdit: newNoteEdit(db, opts...),
NoteTag: newNoteTag(db, opts...),
NoteToAttachment: newNoteToAttachment(db, opts...),
NoteToBoost: newNoteToBoost(db, opts...),
NoteToEmote: newNoteToEmote(db, opts...),
NoteToFeed: newNoteToFeed(db, opts...),
NoteToPing: newNoteToPing(db, opts...),
Notification: newNotification(db, opts...),
Reaction: newReaction(db, opts...),
RemoteServer: newRemoteServer(db, opts...),
RemoteServerMetadata: newRemoteServerMetadata(db, opts...),
Role: newRole(db, opts...),
User: newUser(db, opts...),
UserAuthMethod: newUserAuthMethod(db, opts...),
UserInfoField: newUserInfoField(db, opts...),
UserRemoteLinks: newUserRemoteLinks(db, opts...),
UserToBeing: newUserToBeing(db, opts...),
UserToPronoun: newUserToPronoun(db, opts...),
UserToRole: newUserToRole(db, opts...),
UserToTag: newUserToTag(db, opts...),
UserToUserRelation: newUserToUserRelation(db, opts...),
}
}
type Query struct {
db *gorm.DB
AccessToken accessToken
Emote emote
Feed feed
LoginProcessToken loginProcessToken
MediaMetadata mediaMetadata
Note note
NoteEdit noteEdit
NoteTag noteTag
NoteToAttachment noteToAttachment
NoteToBoost noteToBoost
NoteToEmote noteToEmote
NoteToFeed noteToFeed
NoteToPing noteToPing
Notification notification
Reaction reaction
RemoteServer remoteServer
RemoteServerMetadata remoteServerMetadata
Role role
User user
UserAuthMethod userAuthMethod
UserInfoField userInfoField
UserRemoteLinks userRemoteLinks
UserToBeing userToBeing
UserToPronoun userToPronoun
UserToRole userToRole
UserToTag userToTag
UserToUserRelation userToUserRelation
AccessToken accessToken
ActivitystreamsActivity activitystreamsActivity
Collection collection
Emote emote
Feed feed
LoginProcessToken loginProcessToken
MediaMetadata mediaMetadata
Note note
NoteEdit noteEdit
NoteTag noteTag
NoteToAttachment noteToAttachment
NoteToBoost noteToBoost
NoteToEmote noteToEmote
NoteToFeed noteToFeed
NoteToPing noteToPing
Notification notification
Reaction reaction
RemoteServer remoteServer
RemoteServerMetadata remoteServerMetadata
Role role
User user
UserAuthMethod userAuthMethod
UserInfoField userInfoField
UserRemoteLinks userRemoteLinks
UserToBeing userToBeing
UserToPronoun userToPronoun
UserToRole userToRole
UserToTag userToTag
UserToUserRelation userToUserRelation
}
func (q *Query) Available() bool { return q.db != nil }
func (q *Query) clone(db *gorm.DB) *Query {
return &Query{
db: db,
AccessToken: q.AccessToken.clone(db),
Emote: q.Emote.clone(db),
Feed: q.Feed.clone(db),
LoginProcessToken: q.LoginProcessToken.clone(db),
MediaMetadata: q.MediaMetadata.clone(db),
Note: q.Note.clone(db),
NoteEdit: q.NoteEdit.clone(db),
NoteTag: q.NoteTag.clone(db),
NoteToAttachment: q.NoteToAttachment.clone(db),
NoteToBoost: q.NoteToBoost.clone(db),
NoteToEmote: q.NoteToEmote.clone(db),
NoteToFeed: q.NoteToFeed.clone(db),
NoteToPing: q.NoteToPing.clone(db),
Notification: q.Notification.clone(db),
Reaction: q.Reaction.clone(db),
RemoteServer: q.RemoteServer.clone(db),
RemoteServerMetadata: q.RemoteServerMetadata.clone(db),
Role: q.Role.clone(db),
User: q.User.clone(db),
UserAuthMethod: q.UserAuthMethod.clone(db),
UserInfoField: q.UserInfoField.clone(db),
UserRemoteLinks: q.UserRemoteLinks.clone(db),
UserToBeing: q.UserToBeing.clone(db),
UserToPronoun: q.UserToPronoun.clone(db),
UserToRole: q.UserToRole.clone(db),
UserToTag: q.UserToTag.clone(db),
UserToUserRelation: q.UserToUserRelation.clone(db),
db: db,
AccessToken: q.AccessToken.clone(db),
ActivitystreamsActivity: q.ActivitystreamsActivity.clone(db),
Collection: q.Collection.clone(db),
Emote: q.Emote.clone(db),
Feed: q.Feed.clone(db),
LoginProcessToken: q.LoginProcessToken.clone(db),
MediaMetadata: q.MediaMetadata.clone(db),
Note: q.Note.clone(db),
NoteEdit: q.NoteEdit.clone(db),
NoteTag: q.NoteTag.clone(db),
NoteToAttachment: q.NoteToAttachment.clone(db),
NoteToBoost: q.NoteToBoost.clone(db),
NoteToEmote: q.NoteToEmote.clone(db),
NoteToFeed: q.NoteToFeed.clone(db),
NoteToPing: q.NoteToPing.clone(db),
Notification: q.Notification.clone(db),
Reaction: q.Reaction.clone(db),
RemoteServer: q.RemoteServer.clone(db),
RemoteServerMetadata: q.RemoteServerMetadata.clone(db),
Role: q.Role.clone(db),
User: q.User.clone(db),
UserAuthMethod: q.UserAuthMethod.clone(db),
UserInfoField: q.UserInfoField.clone(db),
UserRemoteLinks: q.UserRemoteLinks.clone(db),
UserToBeing: q.UserToBeing.clone(db),
UserToPronoun: q.UserToPronoun.clone(db),
UserToRole: q.UserToRole.clone(db),
UserToTag: q.UserToTag.clone(db),
UserToUserRelation: q.UserToUserRelation.clone(db),
}
}
@ -187,96 +197,102 @@ func (q *Query) WriteDB() *Query {
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
return &Query{
db: db,
AccessToken: q.AccessToken.replaceDB(db),
Emote: q.Emote.replaceDB(db),
Feed: q.Feed.replaceDB(db),
LoginProcessToken: q.LoginProcessToken.replaceDB(db),
MediaMetadata: q.MediaMetadata.replaceDB(db),
Note: q.Note.replaceDB(db),
NoteEdit: q.NoteEdit.replaceDB(db),
NoteTag: q.NoteTag.replaceDB(db),
NoteToAttachment: q.NoteToAttachment.replaceDB(db),
NoteToBoost: q.NoteToBoost.replaceDB(db),
NoteToEmote: q.NoteToEmote.replaceDB(db),
NoteToFeed: q.NoteToFeed.replaceDB(db),
NoteToPing: q.NoteToPing.replaceDB(db),
Notification: q.Notification.replaceDB(db),
Reaction: q.Reaction.replaceDB(db),
RemoteServer: q.RemoteServer.replaceDB(db),
RemoteServerMetadata: q.RemoteServerMetadata.replaceDB(db),
Role: q.Role.replaceDB(db),
User: q.User.replaceDB(db),
UserAuthMethod: q.UserAuthMethod.replaceDB(db),
UserInfoField: q.UserInfoField.replaceDB(db),
UserRemoteLinks: q.UserRemoteLinks.replaceDB(db),
UserToBeing: q.UserToBeing.replaceDB(db),
UserToPronoun: q.UserToPronoun.replaceDB(db),
UserToRole: q.UserToRole.replaceDB(db),
UserToTag: q.UserToTag.replaceDB(db),
UserToUserRelation: q.UserToUserRelation.replaceDB(db),
db: db,
AccessToken: q.AccessToken.replaceDB(db),
ActivitystreamsActivity: q.ActivitystreamsActivity.replaceDB(db),
Collection: q.Collection.replaceDB(db),
Emote: q.Emote.replaceDB(db),
Feed: q.Feed.replaceDB(db),
LoginProcessToken: q.LoginProcessToken.replaceDB(db),
MediaMetadata: q.MediaMetadata.replaceDB(db),
Note: q.Note.replaceDB(db),
NoteEdit: q.NoteEdit.replaceDB(db),
NoteTag: q.NoteTag.replaceDB(db),
NoteToAttachment: q.NoteToAttachment.replaceDB(db),
NoteToBoost: q.NoteToBoost.replaceDB(db),
NoteToEmote: q.NoteToEmote.replaceDB(db),
NoteToFeed: q.NoteToFeed.replaceDB(db),
NoteToPing: q.NoteToPing.replaceDB(db),
Notification: q.Notification.replaceDB(db),
Reaction: q.Reaction.replaceDB(db),
RemoteServer: q.RemoteServer.replaceDB(db),
RemoteServerMetadata: q.RemoteServerMetadata.replaceDB(db),
Role: q.Role.replaceDB(db),
User: q.User.replaceDB(db),
UserAuthMethod: q.UserAuthMethod.replaceDB(db),
UserInfoField: q.UserInfoField.replaceDB(db),
UserRemoteLinks: q.UserRemoteLinks.replaceDB(db),
UserToBeing: q.UserToBeing.replaceDB(db),
UserToPronoun: q.UserToPronoun.replaceDB(db),
UserToRole: q.UserToRole.replaceDB(db),
UserToTag: q.UserToTag.replaceDB(db),
UserToUserRelation: q.UserToUserRelation.replaceDB(db),
}
}
type queryCtx struct {
AccessToken IAccessTokenDo
Emote IEmoteDo
Feed IFeedDo
LoginProcessToken ILoginProcessTokenDo
MediaMetadata IMediaMetadataDo
Note INoteDo
NoteEdit INoteEditDo
NoteTag INoteTagDo
NoteToAttachment INoteToAttachmentDo
NoteToBoost INoteToBoostDo
NoteToEmote INoteToEmoteDo
NoteToFeed INoteToFeedDo
NoteToPing INoteToPingDo
Notification INotificationDo
Reaction IReactionDo
RemoteServer IRemoteServerDo
RemoteServerMetadata IRemoteServerMetadataDo
Role IRoleDo
User IUserDo
UserAuthMethod IUserAuthMethodDo
UserInfoField IUserInfoFieldDo
UserRemoteLinks IUserRemoteLinksDo
UserToBeing IUserToBeingDo
UserToPronoun IUserToPronounDo
UserToRole IUserToRoleDo
UserToTag IUserToTagDo
UserToUserRelation IUserToUserRelationDo
AccessToken IAccessTokenDo
ActivitystreamsActivity IActivitystreamsActivityDo
Collection ICollectionDo
Emote IEmoteDo
Feed IFeedDo
LoginProcessToken ILoginProcessTokenDo
MediaMetadata IMediaMetadataDo
Note INoteDo
NoteEdit INoteEditDo
NoteTag INoteTagDo
NoteToAttachment INoteToAttachmentDo
NoteToBoost INoteToBoostDo
NoteToEmote INoteToEmoteDo
NoteToFeed INoteToFeedDo
NoteToPing INoteToPingDo
Notification INotificationDo
Reaction IReactionDo
RemoteServer IRemoteServerDo
RemoteServerMetadata IRemoteServerMetadataDo
Role IRoleDo
User IUserDo
UserAuthMethod IUserAuthMethodDo
UserInfoField IUserInfoFieldDo
UserRemoteLinks IUserRemoteLinksDo
UserToBeing IUserToBeingDo
UserToPronoun IUserToPronounDo
UserToRole IUserToRoleDo
UserToTag IUserToTagDo
UserToUserRelation IUserToUserRelationDo
}
func (q *Query) WithContext(ctx context.Context) *queryCtx {
return &queryCtx{
AccessToken: q.AccessToken.WithContext(ctx),
Emote: q.Emote.WithContext(ctx),
Feed: q.Feed.WithContext(ctx),
LoginProcessToken: q.LoginProcessToken.WithContext(ctx),
MediaMetadata: q.MediaMetadata.WithContext(ctx),
Note: q.Note.WithContext(ctx),
NoteEdit: q.NoteEdit.WithContext(ctx),
NoteTag: q.NoteTag.WithContext(ctx),
NoteToAttachment: q.NoteToAttachment.WithContext(ctx),
NoteToBoost: q.NoteToBoost.WithContext(ctx),
NoteToEmote: q.NoteToEmote.WithContext(ctx),
NoteToFeed: q.NoteToFeed.WithContext(ctx),
NoteToPing: q.NoteToPing.WithContext(ctx),
Notification: q.Notification.WithContext(ctx),
Reaction: q.Reaction.WithContext(ctx),
RemoteServer: q.RemoteServer.WithContext(ctx),
RemoteServerMetadata: q.RemoteServerMetadata.WithContext(ctx),
Role: q.Role.WithContext(ctx),
User: q.User.WithContext(ctx),
UserAuthMethod: q.UserAuthMethod.WithContext(ctx),
UserInfoField: q.UserInfoField.WithContext(ctx),
UserRemoteLinks: q.UserRemoteLinks.WithContext(ctx),
UserToBeing: q.UserToBeing.WithContext(ctx),
UserToPronoun: q.UserToPronoun.WithContext(ctx),
UserToRole: q.UserToRole.WithContext(ctx),
UserToTag: q.UserToTag.WithContext(ctx),
UserToUserRelation: q.UserToUserRelation.WithContext(ctx),
AccessToken: q.AccessToken.WithContext(ctx),
ActivitystreamsActivity: q.ActivitystreamsActivity.WithContext(ctx),
Collection: q.Collection.WithContext(ctx),
Emote: q.Emote.WithContext(ctx),
Feed: q.Feed.WithContext(ctx),
LoginProcessToken: q.LoginProcessToken.WithContext(ctx),
MediaMetadata: q.MediaMetadata.WithContext(ctx),
Note: q.Note.WithContext(ctx),
NoteEdit: q.NoteEdit.WithContext(ctx),
NoteTag: q.NoteTag.WithContext(ctx),
NoteToAttachment: q.NoteToAttachment.WithContext(ctx),
NoteToBoost: q.NoteToBoost.WithContext(ctx),
NoteToEmote: q.NoteToEmote.WithContext(ctx),
NoteToFeed: q.NoteToFeed.WithContext(ctx),
NoteToPing: q.NoteToPing.WithContext(ctx),
Notification: q.Notification.WithContext(ctx),
Reaction: q.Reaction.WithContext(ctx),
RemoteServer: q.RemoteServer.WithContext(ctx),
RemoteServerMetadata: q.RemoteServerMetadata.WithContext(ctx),
Role: q.Role.WithContext(ctx),
User: q.User.WithContext(ctx),
UserAuthMethod: q.UserAuthMethod.WithContext(ctx),
UserInfoField: q.UserInfoField.WithContext(ctx),
UserRemoteLinks: q.UserRemoteLinks.WithContext(ctx),
UserToBeing: q.UserToBeing.WithContext(ctx),
UserToPronoun: q.UserToPronoun.WithContext(ctx),
UserToRole: q.UserToRole.WithContext(ctx),
UserToTag: q.UserToTag.WithContext(ctx),
UserToUserRelation: q.UserToUserRelation.WithContext(ctx),
}
}

View file

@ -2,6 +2,7 @@ package models
// A list of all models stored in the database
var AllTypes = []any{
&Collection{},
&ActivitystreamsActivity{},
&Emote{},
&Feed{},

View file

@ -0,0 +1,7 @@
package models
type Collection struct {
Id string `gorm:"primarykey"`
TargetId string
TargetType uint32
}

View file

@ -1,6 +1,7 @@
package models
type ActivitystreamsActivity struct {
Id string `gorm:"primarykey"`
Type string `gorm:"type:activitystreams_activity_type"`
ObjectId string
ObjectType uint32 // Target type: ActivitystreamsActivityTargetType

View file

@ -68,6 +68,7 @@ func AttemptReconnect() {
// HandleReconnectError checks if the given error requires
// a reconnect attempt. If it does, it also starts
// a reconnection attempt.
// Returns true if a reconnect has been started
func HandleReconnectError(errToCheck error) bool {
if _, ok := errToCheck.(*pgconn.ConnectError); ok {
go AttemptReconnect()

View file

@ -1,7 +1,7 @@
[general]
protocol = "https"
domain = "serveo.net"
subdomain = "e8f7573481d6b2311fb076f4190ac852"
domain = "lhr.life"
subdomain = "61f6e346a99d58"
private_port = 8080
public_port = 443

View file

@ -14,6 +14,8 @@ import (
"net/http"
webutils "git.mstar.dev/mstar/goutils/http"
webmiddleware "git.mstar.dev/mstar/linstrom/web/public/middleware"
)
type Server struct {
@ -36,7 +38,13 @@ func New(addr string) *Server {
Addr: addr,
Handler: webutils.ChainMiddlewares(
handler,
webutils.BuildLoggingMiddleware(map[string]string{"server": "debug"}),
webutils.BuildLoggingMiddleware(
true,
[]string{"/assets"},
map[string]string{"server": "debug"},
),
webmiddleware.AppendFullPathMiddleware,
webmiddleware.TraceRequestInfoMiddleware,
),
}
return &Server{&web}

View file

@ -9,8 +9,14 @@ func BuildActivitypubRouter() http.Handler {
router := http.NewServeMux()
router.HandleFunc("/user/{id}", users)
router.HandleFunc("/user/{id}/inbox", userInbox)
router.HandleFunc("/activity/accept/{id}", activityAccept)
router.HandleFunc("/activity/create/{id}", activityCreate)
router.HandleFunc("/activity/delete/{id}", activityDelete)
router.HandleFunc("/activity/reject/{id}", activityReject)
router.HandleFunc("/activity/update/{id}", activityUpdate)
router.HandleFunc("/note/{id}", objectNote)
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "in ap")
_, _ = fmt.Fprint(w, "in ap")
})
return router
}

View file

@ -54,7 +54,6 @@ func users(w http.ResponseWriter, r *http.Request) {
}
log := hlog.FromRequest(r)
userId := r.PathValue("id")
log.Debug().Any("headers", r.Header).Msg("request headers")
user, err := dbgen.User.Where(dbgen.User.ID.Eq(userId)).
Preload(dbgen.User.Icon).Preload(dbgen.User.Banner).
Preload(dbgen.User.BeingTypes).

View file

@ -1 +1,5 @@
package activitypub
import "net/http"
func activityAccept(w http.ResponseWriter, r *http.Request) {}

View file

@ -1 +1,23 @@
package activitypub
import (
"context"
"time"
)
// Announce is boost
type activityAnnounceOut struct {
Context any `json:"@context,omitempty"`
Id string
Type string // Always "Announce"
Actor string // The one doing the boost
Published time.Time
To []string
CC []string
Object string // Link to object being boosted
}
func announceFromStorage(ctx context.Context, id string) (*activityAnnounceOut, error) {
panic("not implemented")
}

View file

@ -1 +1,94 @@
package activitypub
import (
"context"
"encoding/json"
"fmt"
"net/http"
webutils "git.mstar.dev/mstar/goutils/http"
"github.com/rs/zerolog/hlog"
"gorm.io/gorm"
"git.mstar.dev/mstar/linstrom/activitypub"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/storage-new"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
"git.mstar.dev/mstar/linstrom/storage-new/models"
)
type activityCreateOut struct {
Context any `json:"@context,omitempty"`
Id string `json:"id"`
Type string `json:"type"`
Actor string `json:"actor"`
Object any `json:"object"`
}
func activityCreate(w http.ResponseWriter, r *http.Request) {
log := hlog.FromRequest(r)
id := r.PathValue("id")
activity, err := createFromStorage(r.Context(), id)
switch err {
case gorm.ErrRecordNotFound:
webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
case nil:
activity.Context = activitypub.BaseLdContext
data, err := json.Marshal(activity)
if err != nil {
log.Error().Err(err).Any("activity", activity).Msg("Failed to marshal create activity")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/activity+json")
fmt.Fprint(w, string(data))
default:
if storage.HandleReconnectError(err) {
log.Error().Err(err).Msg("Connection failed, restart attempt started")
} else {
log.Error().Err(err).Msg("Failed to get create activity from db")
}
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
}
}
// Find a create activity from the db and format it for activitypub reads
// Does not set the context for the activity, in case the activity is embedded
// in another activity or object. That's the responsibility of the handler
// getting the final result
func createFromStorage(ctx context.Context, id string) (*activityCreateOut, error) {
// log := log.Ctx(ctx)
asa := dbgen.ActivitystreamsActivity
activity, err := asa.Where(asa.Type.Eq(string(models.ActivityCreate))).
Where(asa.Id.Eq(id)).
First()
if err != nil {
return nil, err
}
switch models.ActivitystreamsActivityTargetType(activity.ObjectType) {
case models.ActivitystreamsActivityTargetNote:
note, err := noteFromStorage(ctx, activity.ObjectId)
if err != nil {
return nil, err
}
out := activityCreateOut{
Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/create/" + id,
Type: "Create",
Actor: note.AttributedTo,
Object: note,
}
return &out, nil
case models.ActivitystreamsActivityTargetBoost:
panic("Not implemented")
case models.ActivitystreamsActivityTargetReaction:
panic("Not implemented")
case models.ActivitystreamsActivityTargetActivity:
panic("Not implemented")
case models.ActivitystreamsActivityTargetUser:
panic("Not implemented")
case models.ActivitystreamsActivityTargetUnknown:
panic("Not implemented")
default:
panic("Not implemented")
}
}

View file

@ -1 +1,5 @@
package activitypub
import "net/http"
func activityDelete(w http.ResponseWriter, r *http.Request) {}

View file

@ -1 +1,5 @@
package activitypub
import "net/http"
func activityReject(w http.ResponseWriter, r *http.Request) {}

View file

@ -1 +1,5 @@
package activitypub
import "net/http"
func activityUpdate(w http.ResponseWriter, r *http.Request) {}

View file

@ -0,0 +1,30 @@
package activitypub
import "net/http"
// Used for both unordered and ordered
type collectionOut struct {
Context any
Summary string
Type string
Items []any
Id string
TotalItems int
First *collectionPageOut
}
// Used for both unordered and ordered
type collectionPageOut struct {
Context any
Type string
Id string
PartOf string
Next string
Items []any
}
// Unordered collections handler
func collections(w http.ResponseWriter, r *http.Request) {}
// Ordered collections handler
func orderedCollections(w http.ResponseWriter, r *http.Request) {}

View file

@ -1 +1,102 @@
package activitypub
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
webutils "git.mstar.dev/mstar/goutils/http"
"github.com/rs/zerolog/hlog"
"gorm.io/gorm"
"git.mstar.dev/mstar/linstrom/activitypub"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/storage-new"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
)
type objectNoteOut struct {
// Context should be set, if needed, by the endpoint handler
Context any `json:"@context,omitempty"`
// Attributes below set from storage
Id string `json:"id"`
Type string `json:"type"`
Summary *string `json:"summary"`
InReplyTo *string `json:"inReplyTo"`
Published time.Time `json:"published"`
Url string `json:"url"`
AttributedTo string `json:"attributedTo"`
To []string `json:"to"`
// CC []string `json:"cc"` // FIXME: Uncomment once followers collection implemented
Sensitive bool `json:"sensitive"`
AtomUri string `json:"atomUri"`
InReplyToAtomUri *string `json:"inReplyToAtomUri"`
// Conversation string `json:"conversation"` // FIXME: Uncomment once understood what this field wants
Content string `json:"content"`
// ContentMap map[string]string `json:"content_map"` // TODO: Uncomment once/if support for multiple languages available
// Attachments []string `json:"attachments"` // FIXME: Change this to document type
// Tags []string `json:"tags"` // FIXME: Change this to hashtag type
// Replies any `json:"replies"` // FIXME: Change this to collection type embedding first page
// Likes any `json:"likes"` // FIXME: Change this to collection
// Shares any `json:"shares"` // FIXME: Change this to collection, is boosts
}
func objectNote(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
log := hlog.FromRequest(r)
note, err := noteFromStorage(r.Context(), id)
switch err {
case gorm.ErrRecordNotFound:
webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound)
return
case nil:
note.Context = activitypub.BaseLdContext
data, err := json.Marshal(note)
if err != nil {
log.Error().Err(err).Any("activity", note).Msg("Failed to marshal create activity")
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/activity+json")
fmt.Fprint(w, string(data))
default:
if storage.HandleReconnectError(err) {
log.Error().Err(err).Msg("Connection failed, restart attempt started")
} else {
log.Error().Err(err).Msg("Failed to get create activity from db")
}
webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError)
}
}
func noteFromStorage(ctx context.Context, id string) (*objectNoteOut, error) {
note, err := dbgen.Note.Where(dbgen.Note.ID.Eq(id)).Preload(dbgen.Note.Creator).First()
if err != nil {
return nil, err
}
data := &objectNoteOut{
Id: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/note/" + id,
Type: "Note",
Published: note.CreatedAt,
AttributedTo: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/user/" + note.CreatorId,
Content: note.RawContent, // FIXME: Escape content
Url: config.GlobalConfig.General.GetFullPublicUrl() + "/@" + note.Creator.Username + "/" + id,
To: []string{
"https://www.w3.org/ns/activitystreams#Public",
}, // FIXME: Replace with proper targets, not always public
AtomUri: config.GlobalConfig.General.GetFullPublicUrl() + "/api/activitypub/object/" + id,
}
if note.RepliesTo.Valid {
data.InReplyTo = &note.RepliesTo.String
data.InReplyToAtomUri = &note.RepliesTo.String
}
if note.ContentWarning.Valid {
data.Summary = &note.ContentWarning.String
data.Sensitive = true
}
return data, nil
}

View file

@ -64,13 +64,13 @@ func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuil
if !config.GlobalConfig.Experimental.AuthFetchForServerActor &&
strings.Contains(path, storage.ServerActorId) {
log.Info().Msg("Server actor requested, no auth")
log.Debug().Msg("Server actor requested, no auth")
h.ServeHTTP(w, r)
return
}
// Not an always open path, check methods
if r.Method == "GET" && !forGet {
log.Info().Msg("Get request to AP resources don't need signature")
log.Debug().Msg("Get request to AP resources don't need signature")
h.ServeHTTP(w, r)
return
} else if !forGet && !forNonGet {
@ -78,7 +78,7 @@ func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuil
h.ServeHTTP(w, r)
return
}
log.Info().Msg("Need signature for AP request")
log.Debug().Msg("Need signature for AP request")
rawDate := r.Header.Get("Date")
date, err := http.ParseTime(rawDate)
if err != nil {

View file

@ -0,0 +1,28 @@
package webmiddleware
import (
"bytes"
"io"
"net/http"
"time"
webutils "git.mstar.dev/mstar/goutils/http"
"github.com/rs/zerolog/hlog"
)
func TraceRequestInfoMiddleware(h http.Handler) http.Handler {
return webutils.ChainMiddlewares(
h,
hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
body, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewReader(body))
hlog.FromRequest(r).Trace().Any("headers", r.Header).
Bytes("body", body).
Str("method", r.Method).
Stringer("url", r.URL).
Int("status", status).
Int("size", size).
Dur("duration", duration)
}),
)
}

View file

@ -59,8 +59,13 @@ func New(addr string, duckImg *string, duckFs fs.FS) *Server {
server := http.Server{
Handler: webutils.ChainMiddlewares(
handler,
webutils.BuildLoggingMiddleware(map[string]string{"server": "public"}),
webutils.BuildLoggingMiddleware(
true,
[]string{"/assets"},
map[string]string{"server": "public"},
),
webmiddleware.AppendFullPathMiddleware,
webmiddleware.TraceRequestInfoMiddleware,
),
Addr: addr,
}