Merge branch 'main' of git.mstar.dev:mstar/linstrom
Some checks failed
/ test (push) Has been cancelled
Some checks failed
/ test (push) Has been cancelled
This commit is contained in:
commit
2c57f668e0
33 changed files with 1186 additions and 51 deletions
|
@ -1,28 +1,50 @@
|
|||
# Contribution Guide
|
||||
|
||||
Thank you for your interest in contributing to Linstrom! All contributors are welcome, regardless of their level of experience.
|
||||
Thank you for your interest in contributing to Linstrom!
|
||||
All contributors are welcome, regardless of their level of experience.
|
||||
|
||||
Unless it's primarely AI generated. Then you're going to be blocked immediately
|
||||
and publicly shamed.
|
||||
|
||||
## Bug Reports
|
||||
|
||||
Use the [bug report issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=bug-report.md) to file a bug report. Please include a detailed description of the events leading up to the problem, your system configuration, and the program logs. If you're able to reproduce the bug reliably, attaching a debugger to the program, triggering it, and uploading the results would be very helpful.
|
||||
Use the [bug report issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=bug-report.md)
|
||||
to file a bug report. Please include a detailed description of the events
|
||||
leading up to the problem, your system configuration, and the program logs.
|
||||
If you're able to reproduce the bug reliably, attaching a debugger to the program,
|
||||
triggering it, and uploading the results would be very helpful.
|
||||
|
||||
This section *should* tell you how to find your logs, attach the debugger, and do whatever else you need for a detailed bug report. But nobody filled it out. Attach a picture of Goatse to your bug reports until we fix this.
|
||||
This section _should_ tell you how to find your logs, attach the debugger,
|
||||
and do whatever else you need for a detailed bug report. But nobody filled it out.
|
||||
Attach a picture of Goatse to your bug reports until we fix this.
|
||||
|
||||
## Feature Requests
|
||||
|
||||
Use the [feature request issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=suggestion.md) to suggest new features. Please note that we haven't replaced this placeholder text with the actual criteria we're looking for, which means you should spam us with utterly nonsensical ideas.
|
||||
Use the [feature request issue template](https://git.mstar.dev/mstar/linstrom/issues/new?template=suggestion.md)
|
||||
to suggest new features. Please note that we haven't replaced this placeholder
|
||||
text with the actual criteria we're looking for, which means
|
||||
you should spam us with utterly nonsensical ideas.
|
||||
|
||||
## Submitting Translations
|
||||
|
||||
Translation files are part of the project codebase, so you'll have to fork the repository and file a pull request (see [Contributing Code](CONTRIBUTING.md#contributing-code) below). You don't need any programming knowledge to edit the translation files, though.
|
||||
Translation files are part of the project codebase, so you'll have to
|
||||
fork the repository and file a pull request
|
||||
(see [Contributing Code](CONTRIBUTING.md#contributing-code) below).
|
||||
You don't need any programming knowledge to edit the translation files, though.
|
||||
|
||||
This should have been removed and replaced with a quick overview of where the files are and what translators need to do in order to edit them. Nobody did that, so think of this as a free pass to scream profanities into the issue tracker in your native language.
|
||||
This should have been removed and replaced with a quick overview
|
||||
of where the files are and what translators need to do in order to edit them.
|
||||
Nobody did that, so think of this as a free pass to scream profanities
|
||||
into the issue tracker in your native language.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
### Forking
|
||||
|
||||
If you'd like to have a go at writing some code for Linstrom, fork the repository, then create a new branch with a name that describes the changes you're making. If there's a [relevant issue](https://git.mstar.dev/mstar/linstrom/issues), include the issue number in the branch name:
|
||||
If you'd like to have a go at writing some code for Linstrom,
|
||||
fork the repository, then create a new branch with a name that
|
||||
describes the changes you're making. If there's a [relevant issue](https://git.mstar.dev/mstar/linstrom/issues),
|
||||
include the issue number in the branch name:
|
||||
|
||||
```sh
|
||||
git checkout -b 1337-prevent-computer-from-exploding
|
||||
|
@ -30,10 +52,14 @@ git checkout -b 1337-prevent-computer-from-exploding
|
|||
|
||||
### Development Environment
|
||||
|
||||
The project utilises Go (version 1.23+) for almost everything and node/npm (version 18+) for building the frontend.
|
||||
The go side also makes use of `go generate` for multiple things, primarely in the storage module
|
||||
The project utilises Go (version 1.23+) for almost everything and
|
||||
node/npm (version 18+) for building the frontend.
|
||||
The go side also makes use of `go generate` for multiple things,
|
||||
primarely in the storage module
|
||||
|
||||
We don't have a development environment, because nobody bothered to fill this out. Please add a new build system to the project specifically for your modifications. Bonus points if it's entirely nonsensical, like `npm` in a C project.
|
||||
We don't have a development environment, because nobody bothered to fill this out.
|
||||
Please add a new build system to the project specifically for your modifications.
|
||||
Bonus points if it's entirely nonsensical, like `npm` in a C project.
|
||||
|
||||
### Code Style
|
||||
|
||||
|
@ -43,15 +69,18 @@ For anything node: uhhh, yolo, idk yet
|
|||
|
||||
### Pull Requests
|
||||
|
||||
Once your modifications are complete, you'll want to fetch the latest changes from this repository, rebase your branch, and publish your changes:
|
||||
Once your modifications are complete, you'll want to fetch the latest changes
|
||||
from this repository, rebase your branch, and publish your changes:
|
||||
|
||||
```sh
|
||||
git remote add upstream https://git.mstar.dev/mstar/linstrom.git
|
||||
git checkout main
|
||||
git pull upstream main
|
||||
git checkout 1337-prevent-computer-from-exploding
|
||||
git rebase master
|
||||
git rebase main
|
||||
git push --set-upstream origin 1337-prevent-computer-from-exploding
|
||||
```
|
||||
|
||||
Finally, you can [create a pull request](https://git.mstar.dev/mstar/linstrom/pulls). It might not get approved, or you might have to make some additional changes to your code - but don't give up!
|
||||
Finally, you can [create a pull request](https://git.mstar.dev/mstar/linstrom/pulls).
|
||||
It might not get approved, or you might have to make some additional changes
|
||||
to your code - but don't give up!
|
||||
|
|
22
README.md
22
README.md
|
@ -1,5 +1,8 @@
|
|||
Linstrom is a new social media server with the focus on providing users, moderators and admins alike
|
||||
simple, yet powerful features to moderate the experience.
|
||||
# Linstrom
|
||||
|
||||
Linstrom is a new social media server with the focus on providing
|
||||
users, moderators and admins alike simple, yet powerful features
|
||||
to moderate the experience.
|
||||
|
||||
## Federation
|
||||
|
||||
|
@ -20,3 +23,18 @@ And they all have different woes with their software.
|
|||
I want to try and make a server that combines the strengths of various
|
||||
project's tooling while also hopefully solving
|
||||
some of the weaknesses.
|
||||
|
||||
## Permission system
|
||||
|
||||
All permissions operate on a role based system, similar to what Discord offers.
|
||||
Accounts in power (admins and moderators) are only able to manipulate other accounts
|
||||
via this role system. This prevents moderators from directly manipulating accounts,
|
||||
such as changing their username, info fields or otherwise, while still providing
|
||||
ample control over the activities these accounts can perform.
|
||||
|
||||
These same roles, while most powerful for local accounts, also apply to remote accounts
|
||||
as much as realisticly possible. Preventing a login on a remote server for example
|
||||
might not be possible, but blocking all inbound traffic from that account is.
|
||||
|
||||
However, roles are not only for moderators and admins to use.
|
||||
Normal accounts can also make use of them, all be it in a more limited way.
|
||||
|
|
17
auth/auth.go
Normal file
17
auth/auth.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"git.mstar.dev/mstar/linstrom/storage"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Authentication struct {
|
||||
// For when in-depth access is needed
|
||||
db *gorm.DB
|
||||
// Primary method to acquire account data
|
||||
store *storage.Storage
|
||||
}
|
||||
|
||||
func NewAuth(db *gorm.DB, store *storage.Storage) *Authentication {
|
||||
return &Authentication{db, store}
|
||||
}
|
118
auth/checks.go
Normal file
118
auth/checks.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||
"git.mstar.dev/mstar/linstrom/storage"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Can actorId read the account with targetId?
|
||||
func (a *Authentication) CanReadAccount(actorId *string, targetId string) bool {
|
||||
targetAccount, err := a.store.FindAccountById(targetId)
|
||||
if err != nil {
|
||||
if err == storage.ErrEntryNotFound {
|
||||
return true
|
||||
}
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("account-id", targetId).
|
||||
Msg("Failed to receive account for permission check")
|
||||
return false
|
||||
}
|
||||
if actorId == nil {
|
||||
// TODO: Decide if roles should have a field to declare an account as follow only/hidden
|
||||
// and then check for that flag here
|
||||
return true
|
||||
}
|
||||
roles, err := a.store.FindRolesByNames(targetAccount.Roles)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Strs("role-names", targetAccount.Roles).
|
||||
Msg("Failed to get roles for target account")
|
||||
return false
|
||||
}
|
||||
combined := storage.CollapseRolesIntoOne(roles...)
|
||||
if sliceutils.Contains(combined.BlockedUsers, *actorId) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Can actorId edit the account with targetId?
|
||||
// If actorId is nil, it is assumed to be an anonymous user trying to edit the target account
|
||||
// if targetId is nil, it is assumed that the actor is editing themselves
|
||||
func (a *Authentication) CanEditAccount(actorId *string, targetId *string) bool {
|
||||
// FIXME: This entire function feels wrong, idk
|
||||
// Only the owner of an account should be able to edit said account's data
|
||||
// But how do moderation actions play with this? Do they count as edit or as something separate?
|
||||
if actorId == nil {
|
||||
return false
|
||||
}
|
||||
if targetId == nil {
|
||||
targetId = actorId
|
||||
}
|
||||
targetAccount, err := a.store.FindAccountById(*targetId)
|
||||
if err != nil {
|
||||
if err != storage.ErrEntryNotFound {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Str("target-id", *targetId).
|
||||
Msg("Failed to receive account for permission checks")
|
||||
}
|
||||
return false
|
||||
}
|
||||
if targetId == actorId {
|
||||
targetRoles, err := a.store.FindRolesByNames(targetAccount.Roles)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Strs("role-names", targetAccount.Roles).
|
||||
Msg("Failed to get roles from storage")
|
||||
return false
|
||||
}
|
||||
combined := storage.CollapseRolesIntoOne(targetRoles...)
|
||||
return *combined.CanLogin
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Can actorId delete the account with targetId?
|
||||
// If actorId is nil, it is assumed to be an anonymous user trying to delete the target account
|
||||
// if targetId is nil, it is assumed that the actor is deleting themselves
|
||||
func (a *Authentication) CanDeleteAccount(actorId *string, targetId *string) bool {
|
||||
if actorId == nil {
|
||||
return false
|
||||
}
|
||||
acc, err := a.store.FindAccountById(*actorId)
|
||||
if err != nil {
|
||||
// TODO: Logging
|
||||
return false
|
||||
}
|
||||
roles, err := a.store.FindRolesByNames(acc.Roles)
|
||||
if err != nil {
|
||||
// TODO: Logging
|
||||
return false
|
||||
}
|
||||
collapsed := storage.CollapseRolesIntoOne(roles...)
|
||||
if targetId == nil {
|
||||
return *collapsed.CanLogin
|
||||
} else {
|
||||
return *collapsed.CanDeleteAccounts
|
||||
}
|
||||
}
|
||||
|
||||
// Can actorId create a new post at all?
|
||||
// Specific restrictions regarding the content are not checked
|
||||
func (a *Authentication) CanCreatePost(actorId string) bool { return true }
|
||||
|
||||
// Ensures that a given post conforms with all roles attached to the author account.
|
||||
// Returns the conforming note (or nil of it can't be changed to conform)
|
||||
// and whether the note was changed
|
||||
func (a *Authentication) EnsureNoteConformsWithRoles(note *storage.Note) (*storage.Note, bool) {
|
||||
return note, false
|
||||
}
|
||||
|
||||
// Does the given note conform with the roles attached to the author account?
|
||||
func (a *Authentication) DoesNoteConform(note *storage.Note) bool { return true }
|
64
storage-new/gormLogger.go
Normal file
64
storage-new/gormLogger.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
type gormLogger struct {
|
||||
logger zerolog.Logger
|
||||
}
|
||||
|
||||
func newGormLogger(zerologger zerolog.Logger) *gormLogger {
|
||||
return &gormLogger{zerologger}
|
||||
}
|
||||
|
||||
func (g *gormLogger) LogMode(newLevel logger.LogLevel) logger.Interface {
|
||||
switch newLevel {
|
||||
case logger.Error:
|
||||
g.logger = g.logger.Level(zerolog.ErrorLevel)
|
||||
case logger.Warn:
|
||||
g.logger = g.logger.Level(zerolog.WarnLevel)
|
||||
case logger.Info:
|
||||
g.logger = g.logger.Level(zerolog.InfoLevel)
|
||||
case logger.Silent:
|
||||
g.logger = g.logger.Level(zerolog.Disabled)
|
||||
}
|
||||
return g
|
||||
}
|
||||
func (g *gormLogger) Info(ctx context.Context, format string, args ...interface{}) {
|
||||
g.logger.Info().Ctx(ctx).Msgf(format, args...)
|
||||
}
|
||||
func (g *gormLogger) Warn(ctx context.Context, format string, args ...interface{}) {
|
||||
g.logger.Warn().Ctx(ctx).Msgf(format, args...)
|
||||
}
|
||||
func (g *gormLogger) Error(ctx context.Context, format string, args ...interface{}) {
|
||||
g.logger.Error().Ctx(ctx).Msgf(format, args...)
|
||||
}
|
||||
|
||||
func (g *gormLogger) Trace(
|
||||
ctx context.Context,
|
||||
begin time.Time,
|
||||
fc func() (sql string, rowsAffected int64),
|
||||
err error,
|
||||
) {
|
||||
sql, rowsAffected := fc()
|
||||
g.logger.Trace().
|
||||
Ctx(ctx).
|
||||
Time("gorm-begin", begin).
|
||||
Err(err).
|
||||
Str("gorm-query", sql).
|
||||
Int64("gorm-rows-affected", rowsAffected).
|
||||
Send()
|
||||
}
|
||||
|
||||
func (g *gormLogger) OverwriteLoggingLevel(new zerolog.Level) {
|
||||
g.logger = g.logger.Level(new)
|
||||
}
|
||||
|
||||
func (g *gormLogger) OverwriteLogger(new zerolog.Logger) {
|
||||
g.logger = new
|
||||
}
|
102
storage-new/migrations.go
Normal file
102
storage-new/migrations.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
||||
)
|
||||
|
||||
func migrateTypes(db *gorm.DB) error {
|
||||
if err := db.AutoMigrate(
|
||||
&models.Emote{},
|
||||
&models.MediaMetadata{},
|
||||
&models.Note{},
|
||||
&models.NoteToAttachment{},
|
||||
&models.NoteToEmote{},
|
||||
&models.NoteToPing{},
|
||||
&models.NoteTag{},
|
||||
&models.Reaction{},
|
||||
&models.RemoteServer{},
|
||||
&models.Role{},
|
||||
&models.User{},
|
||||
&models.UserAuthMethod{},
|
||||
&models.UserBeings{},
|
||||
&models.UserInfoField{},
|
||||
&models.UserRelation{},
|
||||
&models.UserRemoteLinks{},
|
||||
&models.UserRole{},
|
||||
&models.UserTag{},
|
||||
); err != nil {
|
||||
return fmt.Errorf("storage: automigrate structs: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure the being enum exists for the user
|
||||
func createBeingType(db *gorm.DB) error {
|
||||
return migrateEnum(
|
||||
db,
|
||||
"being_type",
|
||||
sliceutils.Map(models.AllBeings, func(t models.BeingType) string { return string(t) }),
|
||||
)
|
||||
}
|
||||
|
||||
func createAccountRelationType(db *gorm.DB) error {
|
||||
return migrateEnum(
|
||||
db,
|
||||
"relation_type",
|
||||
sliceutils.Map(
|
||||
models.AllRelations,
|
||||
func(t models.RelationType) string { return string(t) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func createAccountAuthMethodType(db *gorm.DB) error {
|
||||
return migrateEnum(
|
||||
db,
|
||||
"auth_method_type",
|
||||
sliceutils.Map(
|
||||
models.AllAuthMethods,
|
||||
func(t models.AuthenticationMethodType) string { return string(t) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func createRemoteServerSoftwareType(db *gorm.DB) error {
|
||||
return migrateEnum(
|
||||
db,
|
||||
"server_software_type",
|
||||
sliceutils.Map(
|
||||
models.AllServerSoftwareTypes,
|
||||
func(t models.ServerSoftwareType) string { return string(t) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function for ensuring the existence of an enum with the given values
|
||||
func migrateEnum(db *gorm.DB, name string, values []string) error {
|
||||
if err := db.Exec("DROP TYPE IF EXISTS " + name).Error; err != nil {
|
||||
return fmt.Errorf("storage: migrate %s: %w", name, err)
|
||||
}
|
||||
queryBuilder := strings.Builder{}
|
||||
queryBuilder.WriteString("CREATE TYPE")
|
||||
queryBuilder.WriteString(name)
|
||||
queryBuilder.WriteString("AS ENUM (")
|
||||
blen := len(values)
|
||||
for i, btype := range values {
|
||||
queryBuilder.WriteString("'" + string(btype) + "'")
|
||||
// Append comma everywhere except last entry
|
||||
if i+1 < blen {
|
||||
queryBuilder.WriteString(",")
|
||||
}
|
||||
}
|
||||
if err := db.Exec(queryBuilder.String()).Error; err != nil {
|
||||
return fmt.Errorf("storage: migrate %s: %w", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
12
storage-new/models/Emote.go
Normal file
12
storage-new/models/Emote.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Emote struct {
|
||||
gorm.Model
|
||||
// Metadata MediaMetadata // `gorm:"foreignKey:MetadataId"`
|
||||
MetadataId string
|
||||
Name string
|
||||
// Server RemoteServer // `gorm:"foreignKey:ServerId;references:ID"`
|
||||
ServerId uint
|
||||
}
|
29
storage-new/models/MediaMetadata.go
Normal file
29
storage-new/models/MediaMetadata.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MediaMetadata struct {
|
||||
ID string `gorm:"primarykey"` // The unique ID of this media file
|
||||
CreatedAt time.Time // When this entry was created
|
||||
UpdatedAt time.Time // When this entry was last updated
|
||||
// When this entry was deleted (for soft deletions)
|
||||
// Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to
|
||||
// If not null, this entry is marked as deleted
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
OwnedBy string // Account id this media belongs to
|
||||
Remote bool // whether the attachment is a remote one
|
||||
// Where the media is stored. Url
|
||||
Location string
|
||||
Type string // What media type this is following mime types, eg image/png
|
||||
// Name of the file
|
||||
// Could be <emote-name>.png, <server-name>.webp for example. Or the name the file was uploaded with
|
||||
Name string
|
||||
// Alternative description of the media file's content
|
||||
AltText string
|
||||
// Whether the media is to be blurred by default
|
||||
Blurred bool
|
||||
}
|
33
storage-new/models/Note.go
Normal file
33
storage-new/models/Note.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Data defined in extra structs:
|
||||
// - Attachments: models.NoteToAttachment
|
||||
// - Emotes: models.NoteToEmote
|
||||
// - Pings: models.NoteToPing
|
||||
// - Tags: models.NoteTag
|
||||
type Note struct {
|
||||
ID string `gorm:"primarykey"` // Make ID a string (uuid) for other implementations
|
||||
CreatedAt time.Time // When this entry was created
|
||||
UpdatedAt time.Time // When this entry was last updated
|
||||
// When this entry was deleted (for soft deletions)
|
||||
// Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to
|
||||
// If not null, this entry is marked as deleted
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
// Creator Account // `gorm:"foreignKey:CreatorId;references:ID"` // Account that created the post
|
||||
CreatorId string
|
||||
Remote bool // Whether the note is originally a remote one and just "cached"
|
||||
// Raw content of the note. So without additional formatting applied
|
||||
// Might already have formatting applied beforehand from the origin server
|
||||
RawContent string
|
||||
ContentWarning *string // Content warnings of the note, if it contains any
|
||||
RepliesTo *string // Url of the message this replies to
|
||||
Quotes *string // url of the message this note quotes
|
||||
AccessLevel NoteAccessLevel // Where to send this message to (public, home, followers, dm)
|
||||
OriginServer string // Url of the origin server. Also the primary key for those
|
||||
}
|
28
storage-new/models/NoteAccessLevelType.go
Normal file
28
storage-new/models/NoteAccessLevelType.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package models
|
||||
|
||||
import "database/sql/driver"
|
||||
|
||||
type NoteAccessLevel uint8
|
||||
|
||||
const (
|
||||
// The note is intended for the public
|
||||
NOTE_TARGET_PUBLIC NoteAccessLevel = 0
|
||||
// The note is intended only for the home screen
|
||||
// not really any idea what the difference is compared to public
|
||||
// Maybe home notes don't show up on the server feed but still for everyone's home feed if it reaches them via follow or boost
|
||||
NOTE_TARGET_HOME NoteAccessLevel = 1 << iota
|
||||
// The note is intended only for followers
|
||||
NOTE_TARGET_FOLLOWERS
|
||||
// The note is intended only for a DM to one or more targets
|
||||
NOTE_TARGET_DM
|
||||
)
|
||||
|
||||
// Converts the NoteTarget value into a type the DB can use
|
||||
func (n *NoteAccessLevel) Value() (driver.Value, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (ct *NoteAccessLevel) Scan(value any) error {
|
||||
*ct = NoteAccessLevel(value.(uint8))
|
||||
return nil
|
||||
}
|
6
storage-new/models/NoteAttachments.go
Normal file
6
storage-new/models/NoteAttachments.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type NoteToAttachment struct {
|
||||
UserId string
|
||||
AttachmentId string
|
||||
}
|
6
storage-new/models/NoteEmotes.go
Normal file
6
storage-new/models/NoteEmotes.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type NoteToEmote struct {
|
||||
UserId string
|
||||
EmoteId string
|
||||
}
|
6
storage-new/models/NotePings.go
Normal file
6
storage-new/models/NotePings.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type NoteToPing struct {
|
||||
UserId string
|
||||
PingTargetId string
|
||||
}
|
6
storage-new/models/NoteTags.go
Normal file
6
storage-new/models/NoteTags.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type NoteTag struct {
|
||||
UserId string
|
||||
Tag string
|
||||
}
|
10
storage-new/models/Reaction.go
Normal file
10
storage-new/models/Reaction.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type Reaction struct {
|
||||
gorm.Model
|
||||
NoteId string
|
||||
ReactorId string
|
||||
EmoteId uint
|
||||
}
|
12
storage-new/models/RemoteServer.go
Normal file
12
storage-new/models/RemoteServer.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type RemoteServer struct {
|
||||
gorm.Model
|
||||
ServerType ServerSoftwareType // What software the server is running. Useful for formatting
|
||||
Domain string // `gorm:"primaryKey"` // Domain the server exists under. Additional primary key
|
||||
Name string // What the server wants to be known as (usually same as url)
|
||||
Icon string // ID of a media file
|
||||
IsSelf bool // Whether this server is yours truly
|
||||
}
|
37
storage-new/models/RemoteServerSoftwareType.go
Normal file
37
storage-new/models/RemoteServerSoftwareType.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
type ServerSoftwareType string
|
||||
|
||||
const (
|
||||
// Includes forks like glitch-soc, etc
|
||||
ServerSoftwareMastodon = ServerSoftwareType("Mastodon")
|
||||
// Includes forks like Ice Shrimp, Sharkey, Cutiekey, etc
|
||||
ServerSoftwareMisskey = ServerSoftwareType("Misskey")
|
||||
// Includes Akkoma
|
||||
ServerSoftwarePlemora = ServerSoftwareType("Plemora")
|
||||
// Wafrn is a new entry
|
||||
ServerSoftwareWafrn = ServerSoftwareType("Wafrn")
|
||||
// And of course, yours truly
|
||||
ServerSoftwareLinstrom = ServerSoftwareType("Linstrom")
|
||||
)
|
||||
|
||||
var AllServerSoftwareTypes = []ServerSoftwareType{
|
||||
ServerSoftwareMastodon,
|
||||
ServerSoftwareMisskey,
|
||||
ServerSoftwarePlemora,
|
||||
ServerSoftwareWafrn,
|
||||
ServerSoftwareLinstrom,
|
||||
}
|
||||
|
||||
func (r *ServerSoftwareType) Value() (driver.Value, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (ct *ServerSoftwareType) Scan(value any) error {
|
||||
*ct = ServerSoftwareType(value.(string))
|
||||
return nil
|
||||
}
|
182
storage-new/models/Role.go
Normal file
182
storage-new/models/Role.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Could I collapse all these go:generate command into more condensed ones?
|
||||
// Yes
|
||||
// Will I do that?
|
||||
// No
|
||||
// This is explicit in what is being done. And easier to understand
|
||||
|
||||
//go:generate go build -o RolesGenerator ../cmd/RolesGenerator/main.go
|
||||
//go:generate ./RolesGenerator -input=roles.go -output=rolesUtil_generated.go
|
||||
//go:generate rm RolesGenerator
|
||||
|
||||
//go:generate go build -o ApiGenerator ../cmd/RolesApiTypeGenerator/main.go
|
||||
//go:generate ./ApiGenerator -input=roles.go -output=../server/apiLinstromTypes_generated.go
|
||||
//go:generate rm ApiGenerator
|
||||
|
||||
//go:generate go build -o HelperGenerator ../cmd/RolesApiConverter/main.go
|
||||
//go:generate ./HelperGenerator -input=roles.go -output=../server/apiLinstromTypeHelpers_generated.go
|
||||
//go:generate rm HelperGenerator
|
||||
|
||||
//go:generate go build -o FrontendGenerator ../cmd/RolesFrontendGenerator/main.go
|
||||
//go:generate ./FrontendGenerator -input=roles.go -output=../frontend-reactive/app/models/role.ts
|
||||
//go:generate rm FrontendGenerator
|
||||
|
||||
// A role is, in concept, similar to how Discord handles roles
|
||||
// Some permission can be either disallowed (&false), don't care (nil) or allowed (&true)
|
||||
// Don't care just says to use the value from the next lower role where it is set
|
||||
// Blocks are part of the user relations
|
||||
type Role struct {
|
||||
// TODO: More control options
|
||||
// Extend upon whatever Masto, Akkoma and Misskey have
|
||||
// Lots of details please
|
||||
|
||||
// --- Role metadata ---
|
||||
|
||||
// Include full db model stuff
|
||||
gorm.Model
|
||||
|
||||
// Name of the role
|
||||
Name string `gorm:"primaryKey;unique"`
|
||||
|
||||
// Priority of the role
|
||||
// Lower priority gets applied first and thus overwritten by higher priority ones
|
||||
// If two roles have the same priority, the order is undetermined and may be random
|
||||
// Default priority for new roles is 1 to always overwrite default user
|
||||
// And full admin has max priority possible
|
||||
Priority uint32
|
||||
// Whether this role is for a for a single user only (like custom, per user permissions in Discord)
|
||||
// If yes, Name will be the id of the user in question
|
||||
IsUserRole bool
|
||||
|
||||
// Whether this role is one built into Linstrom from the start or not
|
||||
// Note: Built-in roles can't be modified
|
||||
IsBuiltIn bool
|
||||
|
||||
// --- User permissions ---
|
||||
CanSendMedia *bool // Local & remote
|
||||
CanSendCustomEmotes *bool // Local & remote
|
||||
CanSendCustomReactions *bool // Local & remote
|
||||
CanSendPublicNotes *bool // Local & remote
|
||||
CanSendLocalNotes *bool // Local & remote
|
||||
CanSendFollowerOnlyNotes *bool // Local & remote
|
||||
CanSendPrivateNotes *bool // Local & remote
|
||||
CanSendReplies *bool // Local & remote
|
||||
CanQuote *bool // Local only
|
||||
CanBoost *bool // Local only
|
||||
CanIncludeLinks *bool // Local & remote
|
||||
CanIncludeSurvey *bool // Local
|
||||
CanFederateFedi *bool // Local & remote
|
||||
CanFederateBsky *bool // Local
|
||||
|
||||
CanChangeDisplayName *bool // Local
|
||||
|
||||
CanSubmitReports *bool // Local & remote
|
||||
// If disabled, an account can no longer be interacted with. The owner can no longer change anything about it
|
||||
// And the UI will show a notice (and maybe include that info in the AP data too)
|
||||
// Only moderators and admins will be able to edit the account's roles
|
||||
CanLogin *bool // Local
|
||||
|
||||
CanMentionOthers *bool // Local & remote
|
||||
HasMentionCountLimit *bool // Local & remote
|
||||
MentionLimit *uint32 // Local & remote
|
||||
|
||||
// CanViewBoosts *bool
|
||||
// CanViewQuotes *bool
|
||||
// CanViewMedia *bool
|
||||
// CanViewCustomEmotes *bool
|
||||
|
||||
// --- Automod ---
|
||||
AutoNsfwMedia *bool // Local & remote
|
||||
AutoCwPosts *bool // Local & remote
|
||||
AutoCwPostsText *string // Local & remote
|
||||
ScanCreatedPublicNotes *bool // Local & remote
|
||||
ScanCreatedLocalNotes *bool // Local & remote
|
||||
ScanCreatedFollowerOnlyNotes *bool // Local & remote
|
||||
ScanCreatedPrivateNotes *bool // Local & remote
|
||||
// Blocks all interactions and federation between users with the role and all included ids/handles
|
||||
// TODO: Decide whether this is a list of handles or of account ids
|
||||
// Handles would increase the load due to having to search for them first
|
||||
// while ids would require to store every single account mentioned
|
||||
// which could cause escalating storage costs
|
||||
DisallowInteractionsWith []string `gorm:"type:bytes;serializer:gob"` // Local & remote
|
||||
|
||||
WithholdNotesForManualApproval *bool // Local & remote
|
||||
WithholdNotesBasedOnRegex *bool // Local & remote
|
||||
WithholdNotesRegexes []string `gorm:"type:bytes;serializer:gob"` // Local & remote
|
||||
|
||||
// --- Admin perms ---
|
||||
// If set, counts as all permissions being set as given and all restrictions being disabled
|
||||
FullAdmin *bool // Local
|
||||
CanAffectOtherAdmins *bool // Local
|
||||
CanDeleteNotes *bool // Local
|
||||
CanConfirmWithheldNotes *bool // Local
|
||||
CanAssignRoles *bool // Local
|
||||
CanSupressInteractionsBetweenUsers *bool // Local
|
||||
CanOverwriteDisplayNames *bool // Local
|
||||
CanManageCustomEmotes *bool // Local
|
||||
CanViewDeletedNotes *bool // Local
|
||||
CanRecoverDeletedNotes *bool // Local
|
||||
CanManageAvatarDecorations *bool // Local
|
||||
CanManageAds *bool // Local
|
||||
CanSendAnnouncements *bool // Local
|
||||
CanDeleteAccounts *bool // Local
|
||||
}
|
||||
|
||||
/*
|
||||
Mastodon permissions (highest permission to lowest):
|
||||
- Admin
|
||||
- Devops
|
||||
- View Audit log
|
||||
- View Dashboard
|
||||
- Manage Reports
|
||||
- Manage Federation
|
||||
- Manage Settings
|
||||
- Manage Blocks
|
||||
- Manage Taxonomies
|
||||
- Manage Appeals
|
||||
- Manage Users
|
||||
- Manage Invites
|
||||
- Manage Rules
|
||||
- Manage Announcements
|
||||
- Manage Custom Emojis
|
||||
- Manage Webhooks
|
||||
- Invite Users
|
||||
- Manage Roles
|
||||
- Manage User Access
|
||||
- Delete User Data
|
||||
*/
|
||||
|
||||
/*
|
||||
Misskey "permissions" (no order):
|
||||
- Global timeline available (interact with global timeline I think)
|
||||
- Local timeline available (same as global, but for local)
|
||||
- b-something timeline available
|
||||
- Can send public notes
|
||||
- How many mentions a note can have
|
||||
- Can invite others
|
||||
- How many invites can be sent
|
||||
- InviteLimitCycle (whatever that means)
|
||||
- Invite Expiration time (duration of how long invites stay valid I think)
|
||||
- Manage custom emojis
|
||||
- Manage custom avatar decorations
|
||||
- Seach for notes
|
||||
- Use translator
|
||||
- Hide ads from self
|
||||
- How much storage space the user has
|
||||
- Whether to mark all posts from account as nsfw
|
||||
- max number of pinned messages
|
||||
- max number of antennas
|
||||
- max number of muted words
|
||||
- max number of webhooks
|
||||
- max number of clips
|
||||
- max number of notes contained in a clip (? I think. Don't know enough about clips)
|
||||
- Max number of lists of users
|
||||
- max number of users in a user list
|
||||
- rate limit multiplier
|
||||
- max number of applied avatar decorations
|
||||
*/
|
240
storage-new/models/RolesDefaults.go
Normal file
240
storage-new/models/RolesDefaults.go
Normal file
|
@ -0,0 +1,240 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"git.mstar.dev/mstar/goutils/other"
|
||||
)
|
||||
|
||||
// Default role every user has. Defines sane defaults for a normal user
|
||||
// Will get overwritten by just about every other role due to every other role having higher priority
|
||||
var DefaultUserRole = Role{
|
||||
Name: "Default",
|
||||
Priority: 0,
|
||||
IsUserRole: false,
|
||||
IsBuiltIn: true,
|
||||
|
||||
CanSendMedia: other.IntoPointer(true),
|
||||
CanSendCustomEmotes: other.IntoPointer(true),
|
||||
CanSendCustomReactions: other.IntoPointer(true),
|
||||
CanSendPublicNotes: other.IntoPointer(true),
|
||||
CanSendLocalNotes: other.IntoPointer(true),
|
||||
CanSendFollowerOnlyNotes: other.IntoPointer(true),
|
||||
CanSendPrivateNotes: other.IntoPointer(true),
|
||||
CanSendReplies: other.IntoPointer(true),
|
||||
CanQuote: other.IntoPointer(true),
|
||||
CanBoost: other.IntoPointer(true),
|
||||
CanIncludeLinks: other.IntoPointer(true),
|
||||
CanIncludeSurvey: other.IntoPointer(true),
|
||||
CanFederateFedi: other.IntoPointer(true),
|
||||
CanFederateBsky: other.IntoPointer(true),
|
||||
|
||||
CanChangeDisplayName: other.IntoPointer(true),
|
||||
|
||||
CanSubmitReports: other.IntoPointer(true),
|
||||
CanLogin: other.IntoPointer(true),
|
||||
|
||||
CanMentionOthers: other.IntoPointer(true),
|
||||
HasMentionCountLimit: other.IntoPointer(false),
|
||||
MentionLimit: other.IntoPointer(
|
||||
uint32(math.MaxUint32),
|
||||
), // Set this to max, even if not used due to *HasMentionCountLimit == false
|
||||
|
||||
AutoNsfwMedia: other.IntoPointer(false),
|
||||
AutoCwPosts: other.IntoPointer(false),
|
||||
AutoCwPostsText: nil,
|
||||
WithholdNotesForManualApproval: other.IntoPointer(false),
|
||||
ScanCreatedPublicNotes: other.IntoPointer(false),
|
||||
ScanCreatedLocalNotes: other.IntoPointer(false),
|
||||
ScanCreatedFollowerOnlyNotes: other.IntoPointer(false),
|
||||
ScanCreatedPrivateNotes: other.IntoPointer(false),
|
||||
DisallowInteractionsWith: []string{},
|
||||
|
||||
FullAdmin: other.IntoPointer(false),
|
||||
CanAffectOtherAdmins: other.IntoPointer(false),
|
||||
CanDeleteNotes: other.IntoPointer(false),
|
||||
CanConfirmWithheldNotes: other.IntoPointer(false),
|
||||
CanAssignRoles: other.IntoPointer(false),
|
||||
CanSupressInteractionsBetweenUsers: other.IntoPointer(false),
|
||||
CanOverwriteDisplayNames: other.IntoPointer(false),
|
||||
CanManageCustomEmotes: other.IntoPointer(false),
|
||||
CanViewDeletedNotes: other.IntoPointer(false),
|
||||
CanRecoverDeletedNotes: other.IntoPointer(false),
|
||||
CanManageAvatarDecorations: other.IntoPointer(false),
|
||||
CanManageAds: other.IntoPointer(false),
|
||||
CanSendAnnouncements: other.IntoPointer(false),
|
||||
}
|
||||
|
||||
// Role providing maximum permissions
|
||||
var FullAdminRole = Role{
|
||||
Name: "fullAdmin",
|
||||
Priority: math.MaxUint32,
|
||||
IsUserRole: false,
|
||||
IsBuiltIn: true,
|
||||
|
||||
CanSendMedia: other.IntoPointer(true),
|
||||
CanSendCustomEmotes: other.IntoPointer(true),
|
||||
CanSendCustomReactions: other.IntoPointer(true),
|
||||
CanSendPublicNotes: other.IntoPointer(true),
|
||||
CanSendLocalNotes: other.IntoPointer(true),
|
||||
CanSendFollowerOnlyNotes: other.IntoPointer(true),
|
||||
CanSendPrivateNotes: other.IntoPointer(true),
|
||||
CanQuote: other.IntoPointer(true),
|
||||
CanBoost: other.IntoPointer(true),
|
||||
CanIncludeLinks: other.IntoPointer(true),
|
||||
CanIncludeSurvey: other.IntoPointer(true),
|
||||
|
||||
CanChangeDisplayName: other.IntoPointer(true),
|
||||
|
||||
CanSubmitReports: other.IntoPointer(true),
|
||||
CanLogin: other.IntoPointer(true),
|
||||
|
||||
CanMentionOthers: other.IntoPointer(true),
|
||||
HasMentionCountLimit: other.IntoPointer(false),
|
||||
MentionLimit: other.IntoPointer(
|
||||
uint32(math.MaxUint32),
|
||||
), // Set this to max, even if not used due to *HasMentionCountLimit == false
|
||||
|
||||
AutoNsfwMedia: other.IntoPointer(false),
|
||||
AutoCwPosts: other.IntoPointer(false),
|
||||
AutoCwPostsText: nil,
|
||||
WithholdNotesForManualApproval: other.IntoPointer(false),
|
||||
ScanCreatedPublicNotes: other.IntoPointer(false),
|
||||
ScanCreatedLocalNotes: other.IntoPointer(false),
|
||||
ScanCreatedFollowerOnlyNotes: other.IntoPointer(false),
|
||||
ScanCreatedPrivateNotes: other.IntoPointer(false),
|
||||
DisallowInteractionsWith: []string{},
|
||||
|
||||
FullAdmin: other.IntoPointer(true),
|
||||
CanAffectOtherAdmins: other.IntoPointer(true),
|
||||
CanDeleteNotes: other.IntoPointer(true),
|
||||
CanConfirmWithheldNotes: other.IntoPointer(true),
|
||||
CanAssignRoles: other.IntoPointer(true),
|
||||
CanSupressInteractionsBetweenUsers: other.IntoPointer(true),
|
||||
CanOverwriteDisplayNames: other.IntoPointer(true),
|
||||
CanManageCustomEmotes: other.IntoPointer(true),
|
||||
CanViewDeletedNotes: other.IntoPointer(true),
|
||||
CanRecoverDeletedNotes: other.IntoPointer(true),
|
||||
CanManageAvatarDecorations: other.IntoPointer(true),
|
||||
CanManageAds: other.IntoPointer(true),
|
||||
CanSendAnnouncements: other.IntoPointer(true),
|
||||
}
|
||||
|
||||
// Role for totally freezing an account, blocking all activity from it
|
||||
var AccountFreezeRole = Role{
|
||||
Name: "accountFreeze",
|
||||
Priority: math.MaxUint32 - 1,
|
||||
IsUserRole: false,
|
||||
IsBuiltIn: true,
|
||||
|
||||
CanSendMedia: other.IntoPointer(false),
|
||||
CanSendCustomEmotes: other.IntoPointer(false),
|
||||
CanSendCustomReactions: other.IntoPointer(false),
|
||||
CanSendPublicNotes: other.IntoPointer(false),
|
||||
CanSendLocalNotes: other.IntoPointer(false),
|
||||
CanSendFollowerOnlyNotes: other.IntoPointer(false),
|
||||
CanSendPrivateNotes: other.IntoPointer(false),
|
||||
CanSendReplies: other.IntoPointer(false),
|
||||
CanQuote: other.IntoPointer(false),
|
||||
CanBoost: other.IntoPointer(false),
|
||||
CanIncludeLinks: other.IntoPointer(false),
|
||||
CanIncludeSurvey: other.IntoPointer(false),
|
||||
CanFederateBsky: other.IntoPointer(false),
|
||||
CanFederateFedi: other.IntoPointer(false),
|
||||
|
||||
CanChangeDisplayName: other.IntoPointer(false),
|
||||
|
||||
CanSubmitReports: other.IntoPointer(false),
|
||||
CanLogin: other.IntoPointer(false),
|
||||
|
||||
CanMentionOthers: other.IntoPointer(false),
|
||||
HasMentionCountLimit: other.IntoPointer(false),
|
||||
MentionLimit: other.IntoPointer(
|
||||
uint32(math.MaxUint32),
|
||||
), // Set this to max, even if not used due to *HasMentionCountLimit == false
|
||||
|
||||
AutoNsfwMedia: other.IntoPointer(true),
|
||||
AutoCwPosts: other.IntoPointer(false),
|
||||
AutoCwPostsText: other.IntoPointer("Account frozen"),
|
||||
WithholdNotesForManualApproval: other.IntoPointer(true),
|
||||
ScanCreatedPublicNotes: other.IntoPointer(false),
|
||||
ScanCreatedLocalNotes: other.IntoPointer(false),
|
||||
ScanCreatedFollowerOnlyNotes: other.IntoPointer(false),
|
||||
ScanCreatedPrivateNotes: other.IntoPointer(false),
|
||||
DisallowInteractionsWith: []string{},
|
||||
|
||||
FullAdmin: other.IntoPointer(false),
|
||||
CanAffectOtherAdmins: other.IntoPointer(false),
|
||||
CanDeleteNotes: other.IntoPointer(false),
|
||||
CanConfirmWithheldNotes: other.IntoPointer(false),
|
||||
CanAssignRoles: other.IntoPointer(false),
|
||||
CanSupressInteractionsBetweenUsers: other.IntoPointer(false),
|
||||
CanOverwriteDisplayNames: other.IntoPointer(false),
|
||||
CanManageCustomEmotes: other.IntoPointer(false),
|
||||
CanViewDeletedNotes: other.IntoPointer(false),
|
||||
CanRecoverDeletedNotes: other.IntoPointer(false),
|
||||
CanManageAvatarDecorations: other.IntoPointer(false),
|
||||
CanManageAds: other.IntoPointer(false),
|
||||
CanSendAnnouncements: other.IntoPointer(false),
|
||||
}
|
||||
|
||||
var ServerActorRole = Role{
|
||||
Name: "ServerActor",
|
||||
Priority: math.MaxUint32,
|
||||
IsUserRole: true,
|
||||
IsBuiltIn: true,
|
||||
|
||||
CanSendMedia: other.IntoPointer(true),
|
||||
CanSendCustomEmotes: other.IntoPointer(true),
|
||||
CanSendCustomReactions: other.IntoPointer(true),
|
||||
CanSendPublicNotes: other.IntoPointer(true),
|
||||
CanSendLocalNotes: other.IntoPointer(true),
|
||||
CanSendFollowerOnlyNotes: other.IntoPointer(true),
|
||||
CanSendPrivateNotes: other.IntoPointer(true),
|
||||
CanQuote: other.IntoPointer(true),
|
||||
CanBoost: other.IntoPointer(true),
|
||||
CanIncludeLinks: other.IntoPointer(true),
|
||||
CanIncludeSurvey: other.IntoPointer(true),
|
||||
|
||||
CanChangeDisplayName: other.IntoPointer(true),
|
||||
|
||||
CanSubmitReports: other.IntoPointer(true),
|
||||
CanLogin: other.IntoPointer(true),
|
||||
|
||||
CanMentionOthers: other.IntoPointer(true),
|
||||
HasMentionCountLimit: other.IntoPointer(false),
|
||||
MentionLimit: other.IntoPointer(
|
||||
uint32(math.MaxUint32),
|
||||
), // Set this to max, even if not used due to *HasMentionCountLimit == false
|
||||
|
||||
AutoNsfwMedia: other.IntoPointer(false),
|
||||
AutoCwPosts: other.IntoPointer(false),
|
||||
AutoCwPostsText: nil,
|
||||
WithholdNotesForManualApproval: other.IntoPointer(false),
|
||||
ScanCreatedPublicNotes: other.IntoPointer(false),
|
||||
ScanCreatedLocalNotes: other.IntoPointer(false),
|
||||
ScanCreatedFollowerOnlyNotes: other.IntoPointer(false),
|
||||
ScanCreatedPrivateNotes: other.IntoPointer(false),
|
||||
DisallowInteractionsWith: []string{},
|
||||
|
||||
FullAdmin: other.IntoPointer(true),
|
||||
CanAffectOtherAdmins: other.IntoPointer(true),
|
||||
CanDeleteNotes: other.IntoPointer(true),
|
||||
CanConfirmWithheldNotes: other.IntoPointer(true),
|
||||
CanAssignRoles: other.IntoPointer(true),
|
||||
CanSupressInteractionsBetweenUsers: other.IntoPointer(true),
|
||||
CanOverwriteDisplayNames: other.IntoPointer(true),
|
||||
CanManageCustomEmotes: other.IntoPointer(true),
|
||||
CanViewDeletedNotes: other.IntoPointer(true),
|
||||
CanRecoverDeletedNotes: other.IntoPointer(true),
|
||||
CanManageAvatarDecorations: other.IntoPointer(true),
|
||||
CanManageAds: other.IntoPointer(true),
|
||||
CanSendAnnouncements: other.IntoPointer(true),
|
||||
}
|
||||
|
||||
var allDefaultRoles = []*Role{
|
||||
&DefaultUserRole,
|
||||
&FullAdminRole,
|
||||
&AccountFreezeRole,
|
||||
&ServerActorRole,
|
||||
}
|
53
storage-new/models/User.go
Normal file
53
storage-new/models/User.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Data stored in external types:
|
||||
// - Custom info fields
|
||||
// - Being types
|
||||
// - Tags
|
||||
// - Relations
|
||||
// - Pronouns
|
||||
// - Roles
|
||||
// - AP remote links
|
||||
// - Auth methods and tokens (hashed pw, totp key, passkey id)
|
||||
type User struct {
|
||||
ID string `gorm:"primarykey"` // ID is a uuid for this account
|
||||
// Username of the user (eg "max" if the full username is @max@example.com)
|
||||
// Assume unchangable (once set by a user) to be kind to other implementations
|
||||
// Would be an easy avenue to fuck with them though
|
||||
Username string `gorm:"unique"`
|
||||
CreatedAt time.Time // When this entry was created. Automatically set by gorm
|
||||
// When this account was last updated. Will also be used for refreshing remote accounts. Automatically set by gorm
|
||||
UpdatedAt time.Time
|
||||
// When this entry was deleted (for soft deletions)
|
||||
// Soft delete means that this entry still exists in the db, but gorm won't include it anymore unless specifically told to
|
||||
// If not null, this entry is marked as deleted
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
// Server RemoteServer // `gorm:"foreignKey:ServerId;references:ID"` // The server this user is from
|
||||
ServerId uint // Id of the server this user is from, needed for including RemoteServer
|
||||
DisplayName string // The display name of the user. Can be different from the handle
|
||||
Description string // The description of a user account
|
||||
IsBot bool // Whether to mark this account as a script controlled one
|
||||
Icon string // ID of a media file used as icon
|
||||
Background *string // ID of a media file used as background image
|
||||
Banner *string // ID of a media file used as banner
|
||||
Indexable bool // Whether this account can be found by crawlers
|
||||
PublicKey []byte // The public key of the account
|
||||
// Whether this account restricts following
|
||||
// If true, the owner must approve of a follow request first
|
||||
RestrictedFollow bool
|
||||
|
||||
Location *string
|
||||
Birthday *time.Time
|
||||
|
||||
// --- And internal account stuff ---
|
||||
// Whether the account got verified and is allowed to be active
|
||||
// For local accounts being active means being allowed to login and perform interactions
|
||||
// For remote users, if an account is not verified, any interactions it sends are discarded
|
||||
Verified bool
|
||||
}
|
7
storage-new/models/UserAuthentication.go
Normal file
7
storage-new/models/UserAuthentication.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package models
|
||||
|
||||
type UserAuthMethod struct {
|
||||
UserId string
|
||||
AuthMethod AuthenticationMethodType `gorm:"type:auth_method_type"`
|
||||
Token []byte
|
||||
}
|
26
storage-new/models/UserAuthenticationMethod.go
Normal file
26
storage-new/models/UserAuthenticationMethod.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package models
|
||||
|
||||
import "database/sql/driver"
|
||||
|
||||
type AuthenticationMethodType string
|
||||
|
||||
const (
|
||||
AuthMethodPassword AuthenticationMethodType = "password"
|
||||
AuthMethodGAuth AuthenticationMethodType = "g-auth" // Google Authenticator / totp
|
||||
AuthMethodMail AuthenticationMethodType = "mail"
|
||||
AuthMethodPasskey2fa AuthenticationMethodType = "passkey-2fa" // Passkey used as 2fa factor
|
||||
AuthMethodPasskey AuthenticationMethodType = "passkey" // Passkey as only auth key
|
||||
)
|
||||
|
||||
var AllAuthMethods = []AuthenticationMethodType{
|
||||
AuthMethodPassword, AuthMethodGAuth, AuthMethodMail, AuthMethodPasskey, AuthMethodPasskey2fa,
|
||||
}
|
||||
|
||||
func (ct *AuthenticationMethodType) Scan(value any) error {
|
||||
*ct = AuthenticationMethodType(value.([]byte))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ct AuthenticationMethodType) Value() (driver.Value, error) {
|
||||
return string(ct), nil
|
||||
}
|
27
storage-new/models/UserBeingType.go
Normal file
27
storage-new/models/UserBeingType.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
type BeingType string
|
||||
|
||||
const (
|
||||
BEING_HUMAN = BeingType("human")
|
||||
BEING_CAT = BeingType("cat")
|
||||
BEING_FOX = BeingType("fox")
|
||||
BEING_DOG = BeingType("dog")
|
||||
BEING_ROBOT = BeingType("robot")
|
||||
BEING_DOLL = BeingType("doll")
|
||||
)
|
||||
|
||||
var AllBeings = []BeingType{BEING_HUMAN, BEING_CAT, BEING_FOX, BEING_DOG, BEING_ROBOT, BEING_DOLL}
|
||||
|
||||
func (ct *BeingType) Scan(value any) error {
|
||||
*ct = BeingType(value.([]byte))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ct BeingType) Value() (driver.Value, error) {
|
||||
return string(ct), nil
|
||||
}
|
6
storage-new/models/UserBeings.go
Normal file
6
storage-new/models/UserBeings.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type UserBeings struct {
|
||||
UserId string
|
||||
Being BeingType `gorm:"type:being_type"`
|
||||
}
|
19
storage-new/models/UserInfoField.go
Normal file
19
storage-new/models/UserInfoField.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserInfoField struct {
|
||||
gorm.Model // Can actually just embed this as is here as those are not something directly exposed :3
|
||||
Name string
|
||||
Value string
|
||||
LastUrlCheckDate *time.Time // Used if the value is an url to somewhere. Empty if value is not an url
|
||||
// If the value is an url, this attribute indicates whether Linstrom was able to verify ownership
|
||||
// of the provided url via the common method of
|
||||
// "Does the target url contain a rel='me' link to the owner's account"
|
||||
Confirmed bool
|
||||
UserId string // Id of account this info field belongs to
|
||||
}
|
7
storage-new/models/UserRelation.go
Normal file
7
storage-new/models/UserRelation.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package models
|
||||
|
||||
type UserRelation struct {
|
||||
UserId string
|
||||
TargetUserId string
|
||||
Relation RelationType `gorm:"type:relation_type"`
|
||||
}
|
31
storage-new/models/UserRelationType.go
Normal file
31
storage-new/models/UserRelationType.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package models
|
||||
|
||||
import "database/sql/driver"
|
||||
|
||||
type RelationType string
|
||||
|
||||
const (
|
||||
RelationFollow RelationType = "follow"
|
||||
RelationMute RelationType = "mute"
|
||||
RelationNoBoosts RelationType = "no-boosts"
|
||||
RelationBlock RelationType = "block"
|
||||
RelationPreventFollow RelationType = "prevent-follow"
|
||||
)
|
||||
|
||||
// var AllBeings = []BeingType{BEING_HUMAN, BEING_CAT, BEING_FOX, BEING_DOG, BEING_ROBOT, BEING_DOLL}
|
||||
var AllRelations = []RelationType{
|
||||
RelationFollow,
|
||||
RelationMute,
|
||||
RelationNoBoosts,
|
||||
RelationBlock,
|
||||
RelationPreventFollow,
|
||||
}
|
||||
|
||||
func (ct *RelationType) Scan(value any) error {
|
||||
*ct = RelationType(value.([]byte))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ct RelationType) Value() (driver.Value, error) {
|
||||
return string(ct), nil
|
||||
}
|
21
storage-new/models/UserRemote.go
Normal file
21
storage-new/models/UserRemote.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type UserRemoteLinks struct {
|
||||
// ---- Section: gorm
|
||||
// Sets this struct up as a value that an Account may have
|
||||
gorm.Model
|
||||
UserId string
|
||||
|
||||
// Just about every link here is optional to accomodate for servers with only minimal accounts
|
||||
// Minimal being handle, ap link and inbox
|
||||
ApLink string
|
||||
ViewLink *string
|
||||
FollowersLink *string
|
||||
FollowingLink *string
|
||||
InboxLink string
|
||||
OutboxLink *string
|
||||
FeaturedLink *string
|
||||
FeaturedTagsLink *string
|
||||
}
|
6
storage-new/models/UserRoles.go
Normal file
6
storage-new/models/UserRoles.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type UserRole struct {
|
||||
UserId string
|
||||
RoleId uint
|
||||
}
|
6
storage-new/models/UserTags.go
Normal file
6
storage-new/models/UserTags.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type UserTag struct {
|
||||
UserId string
|
||||
Tag string
|
||||
}
|
1
storage-new/storage.go
Normal file
1
storage-new/storage.go
Normal file
|
@ -0,0 +1 @@
|
|||
package storage
|
|
@ -1,9 +1,9 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||
"git.mstar.dev/mstar/linstrom/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
@ -80,7 +80,10 @@ type Role struct {
|
|||
// Internal ids of accounts blocked by this role
|
||||
BlockedUsers []string `gorm:"type:bytes;serializer:gob"` // Local
|
||||
CanSubmitReports *bool // Local & remote
|
||||
CanLogin *bool // Local
|
||||
// If disabled, an account can no longer be interacted with. The owner can no longer change anything about it
|
||||
// And the UI will show a notice (and maybe include that info in the AP data too)
|
||||
// Only moderators and admins will be able to edit the account's roles
|
||||
CanLogin *bool // Local
|
||||
|
||||
CanMentionOthers *bool // Local & remote
|
||||
HasMentionCountLimit *bool // Local & remote
|
||||
|
@ -125,6 +128,7 @@ type Role struct {
|
|||
CanManageAvatarDecorations *bool // Local
|
||||
CanManageAds *bool // Local
|
||||
CanSendAnnouncements *bool // Local
|
||||
CanDeleteAccounts *bool // Local
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package storage
|
||||
|
||||
type AccountRestriction int64
|
||||
|
||||
const (
|
||||
// Account has no restrictions applied
|
||||
ACCOUNT_RESTRICTION_NONE = AccountRestriction(0)
|
||||
// All messages of the account get a content warning applied if none is set
|
||||
// Warning could be something like "Message auto-CWd by server"
|
||||
ACCOUNT_RESTRICTION_AUTO_CW = AccountRestriction(1 << iota)
|
||||
// Disable accessing the account via login or access token
|
||||
ACCOUNT_RESTRICTION_DISABLE_LOGIN
|
||||
// Disable sending activities to other servers if the account is local
|
||||
// Or reject all activities if the account is remote
|
||||
ACCOUNT_RESTRICTION_NO_FEDERATION
|
||||
// Disallow sending direct messages from that account
|
||||
ACCOUNT_RESTRICTION_NO_DMS
|
||||
// Disallow sending follower only messages from that account
|
||||
ACCOUNT_RESTRICTION_NO_FOLLOWER_POSTS
|
||||
// Disable outbound follow requests (restricted account can't send follow requests)
|
||||
ACCOUNT_RESTRICTION_DISABLE_OUTBOUND_FOLLOWS
|
||||
// Disable inbound follow requests (all follow requests to that account are automatically rejected)
|
||||
ACCOUNT_RESTRICTION_DISABLE_INBOUND_FOLLOWS
|
||||
// Forces all posts by that account to be follower only
|
||||
ACCOUNT_RESTRICTION_FORCE_FOLLOWERS_ONLY
|
||||
// Disable all outbound activities of an account.
|
||||
// Includes sending, updating or deleting own notes
|
||||
// as well as boosting or reacting to any notes
|
||||
ACCOUNT_RESTRICTION_DISABLE_ACTIVITIES
|
||||
// Account can only be viewed while logged in or via verified requests to the AP endpoints
|
||||
ACCOUNT_RESTRICTIONS_NO_PUBLIC_ACCESS
|
||||
// Force tag all media the account posts as sensitive
|
||||
ACCOUNT_RESTRICTIONS_FORCE_MEDIA_SENSITIVE
|
||||
)
|
Loading…
Reference in a new issue