feat: add posts

This commit is contained in:
Rogee
2025-01-15 14:47:10 +08:00
parent ab827715fb
commit 9002862415
13 changed files with 374 additions and 104 deletions

View File

@@ -70,8 +70,8 @@ func (ctl *Controller) Upload(ctx fiber.Ctx, claim *jwt.Claims, tenantSlug strin
TenantID: tenant.ID, TenantID: tenant.ID,
UserID: claim.UserID, UserID: claim.UserID,
StorageID: defaultStorage.ID, StorageID: defaultStorage.ID,
Hash: uploadedFile.Hash,
Name: uploadedFile.Name, Name: uploadedFile.Name,
UUID: uploadedFile.Hash,
MimeType: uploadedFile.MimeType, MimeType: uploadedFile.MimeType,
Size: uploadedFile.Size, Size: uploadedFile.Size,
Path: uploadedFile.Path, Path: uploadedFile.Path,

View File

@@ -9,6 +9,7 @@ import (
"backend/providers/otel" "backend/providers/otel"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
"github.com/samber/lo"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0" semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
) )
@@ -40,3 +41,34 @@ func (svc *Service) Create(ctx context.Context, m *model.Medias) (*model.Medias,
} }
return &ret, nil return &ret, nil
} }
// GetMediasByHash
func (svc *Service) GetMediasByHash(ctx context.Context, tenantID, userID int64, hashes []string) ([]*model.Medias, error) {
_, span := otel.Start(ctx, "medias.service.GetMediasByHash")
defer span.End()
hashExpr := lo.Map(hashes, func(item string, index int) Expression { return String(item) })
tbl := table.Medias
stmt := tbl.
SELECT(tbl.AllColumns).
WHERE(
tbl.TenantID.
EQ(Int64(tenantID)).
AND(
tbl.UserID.EQ(Int64(userID)),
).
AND(
tbl.Hash.IN(hashExpr...),
),
)
span.SetAttributes(semconv.DBStatementKey.String(stmt.DebugSql()))
var ret []model.Medias
if err := stmt.QueryContext(ctx, svc.db, &ret); err != nil {
return nil, err
}
return lo.Map(ret, func(item model.Medias, _ int) *model.Medias {
return &item
}), nil
}

View File

@@ -4,6 +4,7 @@ import (
"time" "time"
"backend/app/errorx" "backend/app/errorx"
"backend/app/http/medias"
"backend/app/http/tenants" "backend/app/http/tenants"
"backend/app/http/users" "backend/app/http/users"
"backend/app/requests" "backend/app/requests"
@@ -24,6 +25,7 @@ type Controller struct {
hashIds *hashids.HashID hashIds *hashids.HashID
userSvc *users.Service userSvc *users.Service
tenantSvc *tenants.Service tenantSvc *tenants.Service
mediaSvc *medias.Service
log *log.Entry `inject:"false"` log *log.Entry `inject:"false"`
} }
@@ -153,24 +155,37 @@ func (ctl *Controller) Create(ctx fiber.Ctx, claim *jwt.Claims, tenantSlug strin
return err return err
} }
// check media assets exists
hashes := lo.Map(body.Assets.Data, func(item fields.MediaAsset, _ int) string { return item.Hash })
medias, err := ctl.mediaSvc.GetMediasByHash(ctx.Context(), tenant.ID, user.ID, hashes)
if err != nil {
return err
}
if len(medias) != len(lo.Uniq(hashes)) {
return errorx.BadRequest
}
post := &model.Posts{ post := &model.Posts{
CreatedAt: time.Now(), CreatedAt: time.Now(),
UpdatedAt: time.Now(), UpdatedAt: time.Now(),
TenantID: tenant.ID, TenantID: tenant.ID,
UserID: user.ID, UserID: user.ID,
Title: body.Title, Title: body.Title,
Description: body.Description, Description: body.Description,
Content: body.Content, Content: body.Content,
PosterAssetID: 0, Stage: fields.PostStagePending,
Stage: fields.PostStagePending, Status: fields.PostStatusPending,
Status: fields.PostStatusPending, Price: body.Price,
Price: body.Price, Discount: body.Discount,
Discount: body.Discount, Assets: body.Assets,
Tags: body.Tags,
} }
if err := ctl.svc.Create(ctx.Context(), tenant, user, post); err != nil { if err := ctl.svc.Create(ctx.Context(), tenant, user, post); err != nil {
return err return err
} }
// TODO: trigger event && jobs
return nil return nil
} }

View File

@@ -2,6 +2,8 @@ package posts
import ( import (
"time" "time"
"backend/database/fields"
) )
type UserPost struct { type UserPost struct {
@@ -32,9 +34,11 @@ type UserPostFilter struct {
} }
type PostBody struct { type PostBody struct {
Title string Title string `json:"title,omitempty"`
Description string Tags fields.Json[[]string] `json:"tags,omitempty"`
Content string Description string `json:"description,omitempty"`
Price int64 Content string `json:"content,omitempty"`
Discount int16 Price int64 `json:"price,omitempty"`
Discount int16 `json:"discount,omitempty"`
Assets fields.Json[[]fields.MediaAsset] `json:"assets,omitempty"`
} }

View File

@@ -0,0 +1,199 @@
// Code generated by go-enum DO NOT EDIT.
// Version: -
// Revision: -
// Build Date: -
// Built By: -
package fields
import (
"database/sql/driver"
"errors"
"fmt"
"strings"
)
const (
// MediaAssetTypeUnknown is a MediaAssetType of type Unknown.
MediaAssetTypeUnknown MediaAssetType = "unknown"
// MediaAssetTypePoster is a MediaAssetType of type Poster.
MediaAssetTypePoster MediaAssetType = "poster"
// MediaAssetTypeImage is a MediaAssetType of type Image.
MediaAssetTypeImage MediaAssetType = "image"
// MediaAssetTypeVideo is a MediaAssetType of type Video.
MediaAssetTypeVideo MediaAssetType = "video"
// MediaAssetTypeAudio is a MediaAssetType of type Audio.
MediaAssetTypeAudio MediaAssetType = "audio"
// MediaAssetTypeDocument is a MediaAssetType of type Document.
MediaAssetTypeDocument MediaAssetType = "document"
// MediaAssetTypeOther is a MediaAssetType of type Other.
MediaAssetTypeOther MediaAssetType = "other"
)
var ErrInvalidMediaAssetType = fmt.Errorf("not a valid MediaAssetType, try [%s]", strings.Join(_MediaAssetTypeNames, ", "))
var _MediaAssetTypeNames = []string{
string(MediaAssetTypeUnknown),
string(MediaAssetTypePoster),
string(MediaAssetTypeImage),
string(MediaAssetTypeVideo),
string(MediaAssetTypeAudio),
string(MediaAssetTypeDocument),
string(MediaAssetTypeOther),
}
// MediaAssetTypeNames returns a list of possible string values of MediaAssetType.
func MediaAssetTypeNames() []string {
tmp := make([]string, len(_MediaAssetTypeNames))
copy(tmp, _MediaAssetTypeNames)
return tmp
}
// MediaAssetTypeValues returns a list of the values for MediaAssetType
func MediaAssetTypeValues() []MediaAssetType {
return []MediaAssetType{
MediaAssetTypeUnknown,
MediaAssetTypePoster,
MediaAssetTypeImage,
MediaAssetTypeVideo,
MediaAssetTypeAudio,
MediaAssetTypeDocument,
MediaAssetTypeOther,
}
}
// String implements the Stringer interface.
func (x MediaAssetType) String() string {
return string(x)
}
// IsValid provides a quick way to determine if the typed value is
// part of the allowed enumerated values
func (x MediaAssetType) IsValid() bool {
_, err := ParseMediaAssetType(string(x))
return err == nil
}
var _MediaAssetTypeValue = map[string]MediaAssetType{
"unknown": MediaAssetTypeUnknown,
"poster": MediaAssetTypePoster,
"image": MediaAssetTypeImage,
"video": MediaAssetTypeVideo,
"audio": MediaAssetTypeAudio,
"document": MediaAssetTypeDocument,
"other": MediaAssetTypeOther,
}
// ParseMediaAssetType attempts to convert a string to a MediaAssetType.
func ParseMediaAssetType(name string) (MediaAssetType, error) {
if x, ok := _MediaAssetTypeValue[name]; ok {
return x, nil
}
return MediaAssetType(""), fmt.Errorf("%s is %w", name, ErrInvalidMediaAssetType)
}
var errMediaAssetTypeNilPtr = errors.New("value pointer is nil") // one per type for package clashes
// Scan implements the Scanner interface.
func (x *MediaAssetType) Scan(value interface{}) (err error) {
if value == nil {
*x = MediaAssetType("")
return
}
// A wider range of scannable types.
// driver.Value values at the top of the list for expediency
switch v := value.(type) {
case string:
*x, err = ParseMediaAssetType(v)
case []byte:
*x, err = ParseMediaAssetType(string(v))
case MediaAssetType:
*x = v
case *MediaAssetType:
if v == nil {
return errMediaAssetTypeNilPtr
}
*x = *v
case *string:
if v == nil {
return errMediaAssetTypeNilPtr
}
*x, err = ParseMediaAssetType(*v)
default:
return errors.New("invalid type for MediaAssetType")
}
return
}
// Value implements the driver Valuer interface.
func (x MediaAssetType) Value() (driver.Value, error) {
return x.String(), nil
}
// Set implements the Golang flag.Value interface func.
func (x *MediaAssetType) Set(val string) error {
v, err := ParseMediaAssetType(val)
*x = v
return err
}
// Get implements the Golang flag.Getter interface func.
func (x *MediaAssetType) Get() interface{} {
return *x
}
// Type implements the github.com/spf13/pFlag Value interface.
func (x *MediaAssetType) Type() string {
return "MediaAssetType"
}
type NullMediaAssetType struct {
MediaAssetType MediaAssetType
Valid bool
}
func NewNullMediaAssetType(val interface{}) (x NullMediaAssetType) {
err := x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
_ = err // make any errcheck linters happy
return
}
// Scan implements the Scanner interface.
func (x *NullMediaAssetType) Scan(value interface{}) (err error) {
if value == nil {
x.MediaAssetType, x.Valid = MediaAssetType(""), false
return
}
err = x.MediaAssetType.Scan(value)
x.Valid = (err == nil)
return
}
// Value implements the driver Valuer interface.
func (x NullMediaAssetType) Value() (driver.Value, error) {
if !x.Valid {
return nil, nil
}
// driver.Value accepts int64 for int values.
return string(x.MediaAssetType), nil
}
type NullMediaAssetTypeStr struct {
NullMediaAssetType
}
func NewNullMediaAssetTypeStr(val interface{}) (x NullMediaAssetTypeStr) {
x.Scan(val) // yes, we ignore this error, it will just be an invalid value.
return
}
// Value implements the driver Valuer interface.
func (x NullMediaAssetTypeStr) Value() (driver.Value, error) {
if !x.Valid {
return nil, nil
}
return x.MediaAssetType.String(), nil
}

View File

@@ -0,0 +1,18 @@
package fields
type MediaAsset struct {
Type MediaAssetType `json:"type"`
Hash string `json:"hash"`
}
// swagger:enum MediaAssetType
// ENUM(
// Unknown = "unknown",
// Poster = "poster",
// Image = "image",
// Video = "video",
// Audio = "audio",
// Document = "document",
// Other = "other"
// )
type MediaAssetType string

View File

@@ -17,13 +17,13 @@ CREATE TABLE
title VARCHAR(128) NOT NULL, title VARCHAR(128) NOT NULL,
description VARCHAR(256) NOT NULL, description VARCHAR(256) NOT NULL,
poster_asset_id INT8 NOT NULL,
content TEXT NOT NULL, content TEXT NOT NULL,
price INT8 NOT NULL default 0, price INT8 NOT NULL default 0,
discount INT2 NOT NULL default 100, discount INT2 NOT NULL default 100,
views INT8 NOT NULL default 0, views INT8 NOT NULL default 0,
likes INT8 NOT NULL default 0, likes INT8 NOT NULL default 0,
meta jsonb default '{}'::jsonb, meta jsonb default '{}'::jsonb,
tags jsonb default '{}'::jsonb,
assets jsonb default '{}'::jsonb assets jsonb default '{}'::jsonb
); );
-- create indexes -- create indexes

View File

@@ -10,8 +10,8 @@ CREATE TABLE medias (
user_id INT8 NOT NULL, user_id INT8 NOT NULL,
post_id INT8 NOT NULL, post_id INT8 NOT NULL,
storage_id INT8 NOT NULL, storage_id INT8 NOT NULL,
hash VARCHAR(32) NOT NULL,
name VARCHAR(255) NOT NULL default '', name VARCHAR(255) NOT NULL default '',
uuid VARCHAR(128) NOT NULL,
mime_type VARCHAR(128) NOT NULL default '', mime_type VARCHAR(128) NOT NULL default '',
size INT8 NOT NULL default 0, size INT8 NOT NULL default 0,
path VARCHAR(255) NOT NULL default '' path VARCHAR(255) NOT NULL default ''

View File

@@ -19,8 +19,8 @@ type Medias struct {
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
PostID int64 `json:"post_id"` PostID int64 `json:"post_id"`
StorageID int64 `json:"storage_id"` StorageID int64 `json:"storage_id"`
Hash string `json:"hash"`
Name string `json:"name"` Name string `json:"name"`
UUID string `json:"uuid"`
MimeType string `json:"mime_type"` MimeType string `json:"mime_type"`
Size int64 `json:"size"` Size int64 `json:"size"`
Path string `json:"path"` Path string `json:"path"`

View File

@@ -13,23 +13,23 @@ import (
) )
type Posts struct { type Posts struct {
ID int64 `sql:"primary_key" json:"id"` ID int64 `sql:"primary_key" json:"id"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at"` DeletedAt *time.Time `json:"deleted_at"`
Type fields.PostType `json:"type"` Type fields.PostType `json:"type"`
Stage fields.PostStage `json:"stage"` Stage fields.PostStage `json:"stage"`
Status fields.PostStatus `json:"status"` Status fields.PostStatus `json:"status"`
TenantID int64 `json:"tenant_id"` TenantID int64 `json:"tenant_id"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
PosterAssetID int64 `json:"poster_asset_id"` Content string `json:"content"`
Content string `json:"content"` Price int64 `json:"price"`
Price int64 `json:"price"` Discount int16 `json:"discount"`
Discount int16 `json:"discount"` Views int64 `json:"views"`
Views int64 `json:"views"` Likes int64 `json:"likes"`
Likes int64 `json:"likes"` Meta *string `json:"meta"`
Meta *string `json:"meta"` Tags fields.Json[[]string] `json:"tags"`
Assets *string `json:"assets"` Assets fields.Json[[]fields.MediaAsset] `json:"assets"`
} }

View File

@@ -24,8 +24,8 @@ type mediasTable struct {
UserID postgres.ColumnInteger UserID postgres.ColumnInteger
PostID postgres.ColumnInteger PostID postgres.ColumnInteger
StorageID postgres.ColumnInteger StorageID postgres.ColumnInteger
Hash postgres.ColumnString
Name postgres.ColumnString Name postgres.ColumnString
UUID postgres.ColumnString
MimeType postgres.ColumnString MimeType postgres.ColumnString
Size postgres.ColumnInteger Size postgres.ColumnInteger
Path postgres.ColumnString Path postgres.ColumnString
@@ -76,13 +76,13 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable {
UserIDColumn = postgres.IntegerColumn("user_id") UserIDColumn = postgres.IntegerColumn("user_id")
PostIDColumn = postgres.IntegerColumn("post_id") PostIDColumn = postgres.IntegerColumn("post_id")
StorageIDColumn = postgres.IntegerColumn("storage_id") StorageIDColumn = postgres.IntegerColumn("storage_id")
HashColumn = postgres.StringColumn("hash")
NameColumn = postgres.StringColumn("name") NameColumn = postgres.StringColumn("name")
UUIDColumn = postgres.StringColumn("uuid")
MimeTypeColumn = postgres.StringColumn("mime_type") MimeTypeColumn = postgres.StringColumn("mime_type")
SizeColumn = postgres.IntegerColumn("size") SizeColumn = postgres.IntegerColumn("size")
PathColumn = postgres.StringColumn("path") PathColumn = postgres.StringColumn("path")
allColumns = postgres.ColumnList{IDColumn, CreatedAtColumn, UpdatedAtColumn, TenantIDColumn, UserIDColumn, PostIDColumn, StorageIDColumn, NameColumn, UUIDColumn, MimeTypeColumn, SizeColumn, PathColumn} allColumns = postgres.ColumnList{IDColumn, CreatedAtColumn, UpdatedAtColumn, TenantIDColumn, UserIDColumn, PostIDColumn, StorageIDColumn, HashColumn, NameColumn, MimeTypeColumn, SizeColumn, PathColumn}
mutableColumns = postgres.ColumnList{CreatedAtColumn, UpdatedAtColumn, TenantIDColumn, UserIDColumn, PostIDColumn, StorageIDColumn, NameColumn, UUIDColumn, MimeTypeColumn, SizeColumn, PathColumn} mutableColumns = postgres.ColumnList{CreatedAtColumn, UpdatedAtColumn, TenantIDColumn, UserIDColumn, PostIDColumn, StorageIDColumn, HashColumn, NameColumn, MimeTypeColumn, SizeColumn, PathColumn}
) )
return mediasTable{ return mediasTable{
@@ -96,8 +96,8 @@ func newMediasTableImpl(schemaName, tableName, alias string) mediasTable {
UserID: UserIDColumn, UserID: UserIDColumn,
PostID: PostIDColumn, PostID: PostIDColumn,
StorageID: StorageIDColumn, StorageID: StorageIDColumn,
Hash: HashColumn,
Name: NameColumn, Name: NameColumn,
UUID: UUIDColumn,
MimeType: MimeTypeColumn, MimeType: MimeTypeColumn,
Size: SizeColumn, Size: SizeColumn,
Path: PathColumn, Path: PathColumn,

View File

@@ -17,25 +17,25 @@ type postsTable struct {
postgres.Table postgres.Table
// Columns // Columns
ID postgres.ColumnInteger ID postgres.ColumnInteger
CreatedAt postgres.ColumnTimestamp CreatedAt postgres.ColumnTimestamp
UpdatedAt postgres.ColumnTimestamp UpdatedAt postgres.ColumnTimestamp
DeletedAt postgres.ColumnTimestamp DeletedAt postgres.ColumnTimestamp
Type postgres.ColumnInteger Type postgres.ColumnInteger
Stage postgres.ColumnInteger Stage postgres.ColumnInteger
Status postgres.ColumnInteger Status postgres.ColumnInteger
TenantID postgres.ColumnInteger TenantID postgres.ColumnInteger
UserID postgres.ColumnInteger UserID postgres.ColumnInteger
Title postgres.ColumnString Title postgres.ColumnString
Description postgres.ColumnString Description postgres.ColumnString
PosterAssetID postgres.ColumnInteger Content postgres.ColumnString
Content postgres.ColumnString Price postgres.ColumnInteger
Price postgres.ColumnInteger Discount postgres.ColumnInteger
Discount postgres.ColumnInteger Views postgres.ColumnInteger
Views postgres.ColumnInteger Likes postgres.ColumnInteger
Likes postgres.ColumnInteger Meta postgres.ColumnString
Meta postgres.ColumnString Tags postgres.ColumnString
Assets postgres.ColumnString Assets postgres.ColumnString
AllColumns postgres.ColumnList AllColumns postgres.ColumnList
MutableColumns postgres.ColumnList MutableColumns postgres.ColumnList
@@ -76,52 +76,52 @@ func newPostsTable(schemaName, tableName, alias string) *PostsTable {
func newPostsTableImpl(schemaName, tableName, alias string) postsTable { func newPostsTableImpl(schemaName, tableName, alias string) postsTable {
var ( var (
IDColumn = postgres.IntegerColumn("id") IDColumn = postgres.IntegerColumn("id")
CreatedAtColumn = postgres.TimestampColumn("created_at") CreatedAtColumn = postgres.TimestampColumn("created_at")
UpdatedAtColumn = postgres.TimestampColumn("updated_at") UpdatedAtColumn = postgres.TimestampColumn("updated_at")
DeletedAtColumn = postgres.TimestampColumn("deleted_at") DeletedAtColumn = postgres.TimestampColumn("deleted_at")
TypeColumn = postgres.IntegerColumn("type") TypeColumn = postgres.IntegerColumn("type")
StageColumn = postgres.IntegerColumn("stage") StageColumn = postgres.IntegerColumn("stage")
StatusColumn = postgres.IntegerColumn("status") StatusColumn = postgres.IntegerColumn("status")
TenantIDColumn = postgres.IntegerColumn("tenant_id") TenantIDColumn = postgres.IntegerColumn("tenant_id")
UserIDColumn = postgres.IntegerColumn("user_id") UserIDColumn = postgres.IntegerColumn("user_id")
TitleColumn = postgres.StringColumn("title") TitleColumn = postgres.StringColumn("title")
DescriptionColumn = postgres.StringColumn("description") DescriptionColumn = postgres.StringColumn("description")
PosterAssetIDColumn = postgres.IntegerColumn("poster_asset_id") ContentColumn = postgres.StringColumn("content")
ContentColumn = postgres.StringColumn("content") PriceColumn = postgres.IntegerColumn("price")
PriceColumn = postgres.IntegerColumn("price") DiscountColumn = postgres.IntegerColumn("discount")
DiscountColumn = postgres.IntegerColumn("discount") ViewsColumn = postgres.IntegerColumn("views")
ViewsColumn = postgres.IntegerColumn("views") LikesColumn = postgres.IntegerColumn("likes")
LikesColumn = postgres.IntegerColumn("likes") MetaColumn = postgres.StringColumn("meta")
MetaColumn = postgres.StringColumn("meta") TagsColumn = postgres.StringColumn("tags")
AssetsColumn = postgres.StringColumn("assets") AssetsColumn = postgres.StringColumn("assets")
allColumns = postgres.ColumnList{IDColumn, CreatedAtColumn, UpdatedAtColumn, DeletedAtColumn, TypeColumn, StageColumn, StatusColumn, TenantIDColumn, UserIDColumn, TitleColumn, DescriptionColumn, PosterAssetIDColumn, ContentColumn, PriceColumn, DiscountColumn, ViewsColumn, LikesColumn, MetaColumn, AssetsColumn} allColumns = postgres.ColumnList{IDColumn, CreatedAtColumn, UpdatedAtColumn, DeletedAtColumn, TypeColumn, StageColumn, StatusColumn, TenantIDColumn, UserIDColumn, TitleColumn, DescriptionColumn, ContentColumn, PriceColumn, DiscountColumn, ViewsColumn, LikesColumn, MetaColumn, TagsColumn, AssetsColumn}
mutableColumns = postgres.ColumnList{CreatedAtColumn, UpdatedAtColumn, DeletedAtColumn, TypeColumn, StageColumn, StatusColumn, TenantIDColumn, UserIDColumn, TitleColumn, DescriptionColumn, PosterAssetIDColumn, ContentColumn, PriceColumn, DiscountColumn, ViewsColumn, LikesColumn, MetaColumn, AssetsColumn} mutableColumns = postgres.ColumnList{CreatedAtColumn, UpdatedAtColumn, DeletedAtColumn, TypeColumn, StageColumn, StatusColumn, TenantIDColumn, UserIDColumn, TitleColumn, DescriptionColumn, ContentColumn, PriceColumn, DiscountColumn, ViewsColumn, LikesColumn, MetaColumn, TagsColumn, AssetsColumn}
) )
return postsTable{ return postsTable{
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ID: IDColumn, ID: IDColumn,
CreatedAt: CreatedAtColumn, CreatedAt: CreatedAtColumn,
UpdatedAt: UpdatedAtColumn, UpdatedAt: UpdatedAtColumn,
DeletedAt: DeletedAtColumn, DeletedAt: DeletedAtColumn,
Type: TypeColumn, Type: TypeColumn,
Stage: StageColumn, Stage: StageColumn,
Status: StatusColumn, Status: StatusColumn,
TenantID: TenantIDColumn, TenantID: TenantIDColumn,
UserID: UserIDColumn, UserID: UserIDColumn,
Title: TitleColumn, Title: TitleColumn,
Description: DescriptionColumn, Description: DescriptionColumn,
PosterAssetID: PosterAssetIDColumn, Content: ContentColumn,
Content: ContentColumn, Price: PriceColumn,
Price: PriceColumn, Discount: DiscountColumn,
Discount: DiscountColumn, Views: ViewsColumn,
Views: ViewsColumn, Likes: LikesColumn,
Likes: LikesColumn, Meta: MetaColumn,
Meta: MetaColumn, Tags: TagsColumn,
Assets: AssetsColumn, Assets: AssetsColumn,
AllColumns: allColumns, AllColumns: allColumns,
MutableColumns: mutableColumns, MutableColumns: mutableColumns,

View File

@@ -20,6 +20,8 @@ types:
stage: PostStage stage: PostStage
status: PostStatus status: PostStatus
type: PostType type: PostType
assets: Json[[]MediaAsset]
tags: Json[[]string]
orders: orders:
type: OrderType type: OrderType