mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-22 11:46:40 +00:00
Compare commits
8 commits
1290d042c2
...
26dd86b9ee
Author | SHA1 | Date | |
---|---|---|---|
26dd86b9ee | |||
2a437685fc | |||
a48cce82b9 | |||
40c33ccc49 | |||
90b773ae2a | |||
4b7d7f9b8b | |||
af5a766f62 | |||
d9e59820ed |
|
@ -385,7 +385,7 @@ We use [golangci-lint](https://golangci-lint.run/) for linting, which allows us
|
||||||
|
|
||||||
If you make a PR that doesn't pass the linter, it will be rejected. As such, it's good practice to run the linter locally before pushing or opening a PR.
|
If you make a PR that doesn't pass the linter, it will be rejected. As such, it's good practice to run the linter locally before pushing or opening a PR.
|
||||||
|
|
||||||
To do this, first install the linter following the instructions [here](https://golangci-lint.run/usage/install/#local-installation).
|
To do this, first install the linter following the instructions [here](https://golangci-lint.run/welcome/install/).
|
||||||
|
|
||||||
Then, you can run the linter with:
|
Then, you can run the linter with:
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,12 @@ definitions:
|
||||||
example: https://example.org/media/some_user/header/static/header.png
|
example: https://example.org/media/some_user/header/static/header.png
|
||||||
type: string
|
type: string
|
||||||
x-go-name: HeaderStatic
|
x-go-name: HeaderStatic
|
||||||
|
hide_boosts:
|
||||||
|
description: |-
|
||||||
|
Account has opted to hide boosts from their profile.
|
||||||
|
Key/value omitted if false.
|
||||||
|
type: boolean
|
||||||
|
x-go-name: HideBoosts
|
||||||
hide_collections:
|
hide_collections:
|
||||||
description: |-
|
description: |-
|
||||||
Account has opted to hide their followers/following collections.
|
Account has opted to hide their followers/following collections.
|
||||||
|
@ -2114,7 +2120,7 @@ definitions:
|
||||||
bitrate:
|
bitrate:
|
||||||
description: Bitrate of the media in bits per second.
|
description: Bitrate of the media in bits per second.
|
||||||
example: 1000000
|
example: 1000000
|
||||||
format: int64
|
format: uint64
|
||||||
type: integer
|
type: integer
|
||||||
x-go-name: Bitrate
|
x-go-name: Bitrate
|
||||||
duration:
|
duration:
|
||||||
|
@ -2289,6 +2295,12 @@ definitions:
|
||||||
example: https://example.org/media/some_user/header/static/header.png
|
example: https://example.org/media/some_user/header/static/header.png
|
||||||
type: string
|
type: string
|
||||||
x-go-name: HeaderStatic
|
x-go-name: HeaderStatic
|
||||||
|
hide_boosts:
|
||||||
|
description: |-
|
||||||
|
Account has opted to hide boosts from their profile.
|
||||||
|
Key/value omitted if false.
|
||||||
|
type: boolean
|
||||||
|
x-go-name: HideBoosts
|
||||||
hide_collections:
|
hide_collections:
|
||||||
description: |-
|
description: |-
|
||||||
Account has opted to hide their followers/following collections.
|
Account has opted to hide their followers/following collections.
|
||||||
|
|
|
@ -88,9 +88,9 @@ This setting does not affect visibility of your posts over the ActivityPub proto
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
Be aware that changes to this setting also apply retroactively.
|
Be aware that changes to this setting also apply retroactively.
|
||||||
|
|
||||||
That is, if you previously made a post on Unlisted visibility, while set to show only Public posts on your profile, and you change this setting to show Public and Unlisted, then the Unlisted post you previously made will be visible on your profile alongside your Public posts.
|
That is, if you previously made a post on Unlisted visibility, while set to show only Public posts on your profile, and you change this setting to show Public and Unlisted, then the Unlisted post you previously made will be visible on your profile alongside your Public posts.
|
||||||
|
|
||||||
Likewise, if you change this setting to show no posts, then all your posts will be hidden from your profile, regardless of when you created them, and what this option was set to at the time. This will apply until you change this setting again.
|
Likewise, if you change this setting to show no posts, then all your posts will be hidden from your profile, regardless of when you created them, and what this option was set to at the time. This will apply until you change this setting again.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
@ -134,6 +134,11 @@ This feed only includes posts set as 'Public' (see [Privacy Settings](./posts.md
|
||||||
!!! warning
|
!!! warning
|
||||||
Exposing your RSS feed allows *anyone* to subscribe to updates on your Public posts anonymously, bypassing follows and follow requests.
|
Exposing your RSS feed allows *anyone* to subscribe to updates on your Public posts anonymously, bypassing follows and follow requests.
|
||||||
|
|
||||||
|
#### Hide boosts from your public page
|
||||||
|
|
||||||
|
By default, GoToSocial will display posts boosted by you on your public web profile. If you do not wish to display them, You can hide them by checking this box.
|
||||||
|
|
||||||
|
|
||||||
#### Hide Who You Follow / Are Followed By
|
#### Hide Who You Follow / Are Followed By
|
||||||
|
|
||||||
By default, GoToSocial shows your following/followers counts on your public web profile, and allows others to see who you follow and are followed by. This can be useful for account discovery purposes. However, for privacy + safety reasons you may wish to hide these counts, and to hide your following/followers lists from other accounts. You can do this by checking this box.
|
By default, GoToSocial shows your following/followers counts on your public web profile, and allows others to see who you follow and are followed by. This can be useful for account discovery purposes. However, for privacy + safety reasons you may wish to hide these counts, and to hide your following/followers lists from other accounts. You can do this by checking this box.
|
||||||
|
@ -196,7 +201,7 @@ If you want to reset all your policies to the initial defaults, you can click on
|
||||||
|
|
||||||
!!! danger
|
!!! danger
|
||||||
While GoToSocial respects interaction policies, it is not guaranteed that other server softwares will, and it is possible that accounts on other servers will still send out replies and boosts of your post to their followers, even if your instance forbids these interactions.
|
While GoToSocial respects interaction policies, it is not guaranteed that other server softwares will, and it is possible that accounts on other servers will still send out replies and boosts of your post to their followers, even if your instance forbids these interactions.
|
||||||
|
|
||||||
As more ActivityPub servers roll out support for interaction policies, this issue will hopefully diminish, but in the meantime GoToSocial can offer only a "best effort" attempt to restrict interactions with your posts according to the policies you have set.
|
As more ActivityPub servers roll out support for interaction policies, this issue will hopefully diminish, but in the meantime GoToSocial can offer only a "best effort" attempt to restrict interactions with your posts according to the policies you have set.
|
||||||
|
|
||||||
## Email & Password
|
## Email & Password
|
||||||
|
|
|
@ -24,12 +24,12 @@ profile gotosocial flags=(attach_disconnected, mediate_deleted) {
|
||||||
|
|
||||||
# Embedded ffmpeg needs read
|
# Embedded ffmpeg needs read
|
||||||
# permission on /dev/urandom.
|
# permission on /dev/urandom.
|
||||||
owner /dev/ r,
|
/dev/ r,
|
||||||
owner /dev/urandom r,
|
/dev/urandom r,
|
||||||
|
|
||||||
# Temp dir access is needed for storing
|
# Temp dir access is needed for storing
|
||||||
# files briefly during media processing.
|
# files briefly during media processing.
|
||||||
owner /tmp/ r,
|
/tmp/ r,
|
||||||
owner /tmp/* rwk,
|
owner /tmp/* rwk,
|
||||||
|
|
||||||
# If running with GTS_WAZERO_COMPILATION_CACHE set,
|
# If running with GTS_WAZERO_COMPILATION_CACHE set,
|
||||||
|
@ -39,7 +39,7 @@ profile gotosocial flags=(attach_disconnected, mediate_deleted) {
|
||||||
|
|
||||||
# If you've enabled logging to syslog, allow GoToSocial
|
# If you've enabled logging to syslog, allow GoToSocial
|
||||||
# to write logs by uncommenting the following line:
|
# to write logs by uncommenting the following line:
|
||||||
# owner /var/log/syslog w,
|
# /var/log/syslog w,
|
||||||
|
|
||||||
# These directories are not currently used by any of
|
# These directories are not currently used by any of
|
||||||
# the recommended GoToSocial installation methods, but
|
# the recommended GoToSocial installation methods, but
|
||||||
|
@ -65,6 +65,7 @@ profile gotosocial flags=(attach_disconnected, mediate_deleted) {
|
||||||
/etc/services r,
|
/etc/services r,
|
||||||
/proc/sys/net/core/somaxconn r,
|
/proc/sys/net/core/somaxconn r,
|
||||||
/sys/fs/cgroup/system.slice/gotosocial.service/{,*} r,
|
/sys/fs/cgroup/system.slice/gotosocial.service/{,*} r,
|
||||||
|
/sys/kernel/mm/hugepages/ r,
|
||||||
/sys/kernel/mm/transparent_hugepage/hpage_pmd_size r,
|
/sys/kernel/mm/transparent_hugepage/hpage_pmd_size r,
|
||||||
owner /proc/*/cgroup r,
|
owner /proc/*/cgroup r,
|
||||||
owner /proc/*/cpuset r,
|
owner /proc/*/cpuset r,
|
||||||
|
|
|
@ -348,6 +348,7 @@ func parseUpdateAccountForm(c *gin.Context) (*apimodel.UpdateCredentialsRequest,
|
||||||
form.Theme == nil &&
|
form.Theme == nil &&
|
||||||
form.CustomCSS == nil &&
|
form.CustomCSS == nil &&
|
||||||
form.EnableRSS == nil &&
|
form.EnableRSS == nil &&
|
||||||
|
form.HideBoosts == nil &&
|
||||||
form.HideCollections == nil &&
|
form.HideCollections == nil &&
|
||||||
form.WebVisibility == nil) {
|
form.WebVisibility == nil) {
|
||||||
return nil, errors.New("empty form submitted")
|
return nil, errors.New("empty form submitted")
|
||||||
|
|
|
@ -145,8 +145,8 @@ func validateCreateEmoji(form *apimodel.EmojiCreateRequest) error {
|
||||||
return errors.New("no emoji given")
|
return errors.New("no emoji given")
|
||||||
}
|
}
|
||||||
|
|
||||||
maxSize := config.GetMediaEmojiLocalMaxSize()
|
maxSize := int64(config.GetMediaEmojiLocalMaxSize()) // #nosec G115 -- Already validated.
|
||||||
if form.Image.Size > int64(maxSize) {
|
if form.Image.Size > maxSize {
|
||||||
return fmt.Errorf("emoji image too large: image is %dKB but size limit for custom emojis is %dKB", form.Image.Size/1024, maxSize/1024)
|
return fmt.Errorf("emoji image too large: image is %dKB but size limit for custom emojis is %dKB", form.Image.Size/1024, maxSize/1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,8 +208,8 @@ func validateUpdateEmoji(form *apimodel.EmojiUpdateRequest) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasImage {
|
if hasImage {
|
||||||
maxSize := config.GetMediaEmojiLocalMaxSize()
|
maxSize := int64(config.GetMediaEmojiLocalMaxSize()) // #nosec G115 -- Already validated.
|
||||||
if form.Image.Size > int64(maxSize) {
|
if form.Image.Size > maxSize {
|
||||||
return fmt.Errorf("emoji image too large: image is %dKB but size limit for custom emojis is %dKB", form.Image.Size/1024, maxSize/1024)
|
return fmt.Errorf("emoji image too large: image is %dKB but size limit for custom emojis is %dKB", form.Image.Size/1024, maxSize/1024)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,9 @@ type Account struct {
|
||||||
// Account has enabled RSS feed.
|
// Account has enabled RSS feed.
|
||||||
// Key/value omitted if false.
|
// Key/value omitted if false.
|
||||||
EnableRSS bool `json:"enable_rss,omitempty"`
|
EnableRSS bool `json:"enable_rss,omitempty"`
|
||||||
|
// Account has opted to hide boosts from their profile.
|
||||||
|
// Key/value omitted if false.
|
||||||
|
HideBoosts bool `json:"hide_boosts,omitempty"`
|
||||||
// Account has opted to hide their followers/following collections.
|
// Account has opted to hide their followers/following collections.
|
||||||
// Key/value omitted if false.
|
// Key/value omitted if false.
|
||||||
HideCollections bool `json:"hide_collections,omitempty"`
|
HideCollections bool `json:"hide_collections,omitempty"`
|
||||||
|
@ -225,6 +228,8 @@ type UpdateCredentialsRequest struct {
|
||||||
CustomCSS *string `form:"custom_css" json:"custom_css"`
|
CustomCSS *string `form:"custom_css" json:"custom_css"`
|
||||||
// Enable RSS feed of public toots for this account at /@[username]/feed.rss
|
// Enable RSS feed of public toots for this account at /@[username]/feed.rss
|
||||||
EnableRSS *bool `form:"enable_rss" json:"enable_rss"`
|
EnableRSS *bool `form:"enable_rss" json:"enable_rss"`
|
||||||
|
// Hide boosts from this account's profile page.
|
||||||
|
HideBoosts *bool `form:"hide_boosts" json:"hide_boosts"`
|
||||||
// Hide this account's following/followers collections.
|
// Hide this account's following/followers collections.
|
||||||
HideCollections *bool `form:"hide_collections" json:"hide_collections"`
|
HideCollections *bool `form:"hide_collections" json:"hide_collections"`
|
||||||
// Visibility of statuses to show via the web view.
|
// Visibility of statuses to show via the web view.
|
||||||
|
|
|
@ -160,7 +160,7 @@ type MediaDimensions struct {
|
||||||
Duration float32 `json:"duration,omitempty"`
|
Duration float32 `json:"duration,omitempty"`
|
||||||
// Bitrate of the media in bits per second.
|
// Bitrate of the media in bits per second.
|
||||||
// example: 1000000
|
// example: 1000000
|
||||||
Bitrate int `json:"bitrate,omitempty"`
|
Bitrate uint64 `json:"bitrate,omitempty"`
|
||||||
// Size of the media, in the format `[width]x[height]`.
|
// Size of the media, in the format `[width]x[height]`.
|
||||||
// Not set for audio.
|
// Not set for audio.
|
||||||
// example: 1920x1080
|
// example: 1920x1080
|
||||||
|
|
|
@ -118,6 +118,10 @@ type WebStatus struct {
|
||||||
// Override API account with web account.
|
// Override API account with web account.
|
||||||
Account *WebAccount `json:"account"`
|
Account *WebAccount `json:"account"`
|
||||||
|
|
||||||
|
// Account that reblogged the status.
|
||||||
|
// needed to properly render reblogged statuses on profile pages.
|
||||||
|
ReblogAccount *WebAccount `json:"reblog_account"`
|
||||||
|
|
||||||
// Web version of media
|
// Web version of media
|
||||||
// attached to this status.
|
// attached to this status.
|
||||||
MediaAttachments []*WebAttachment `json:"media_attachments"`
|
MediaAttachments []*WebAttachment `json:"media_attachments"`
|
||||||
|
|
2
internal/cache/domain/domain.go
vendored
2
internal/cache/domain/domain.go
vendored
|
@ -220,7 +220,7 @@ func (n *node) getChild(part string) *node {
|
||||||
|
|
||||||
for i < j {
|
for i < j {
|
||||||
// avoid overflow when computing h
|
// avoid overflow when computing h
|
||||||
h := int(uint(i+j) >> 1)
|
h := int(uint(i+j) >> 1) // #nosec G115
|
||||||
// i ≤ h < j
|
// i ≤ h < j
|
||||||
|
|
||||||
if n.child[h].part < part {
|
if n.child[h].part < part {
|
||||||
|
|
|
@ -1017,6 +1017,7 @@ func (a *accountDB) GetAccountWebStatuses(
|
||||||
) ([]*gtsmodel.Status, error) {
|
) ([]*gtsmodel.Status, error) {
|
||||||
// Check for an easy case: account exposes no statuses via the web.
|
// Check for an easy case: account exposes no statuses via the web.
|
||||||
webVisibility := account.Settings.WebVisibility
|
webVisibility := account.Settings.WebVisibility
|
||||||
|
hideBoosts := *account.Settings.HideBoosts
|
||||||
if webVisibility == gtsmodel.VisibilityNone {
|
if webVisibility == gtsmodel.VisibilityNone {
|
||||||
return nil, db.ErrNoEntries
|
return nil, db.ErrNoEntries
|
||||||
}
|
}
|
||||||
|
@ -1035,9 +1036,12 @@ func (a *accountDB) GetAccountWebStatuses(
|
||||||
// Select only IDs from table
|
// Select only IDs from table
|
||||||
Column("status.id").
|
Column("status.id").
|
||||||
Where("? = ?", bun.Ident("status.account_id"), account.ID).
|
Where("? = ?", bun.Ident("status.account_id"), account.ID).
|
||||||
// Don't show replies or boosts.
|
// Don't show replies.
|
||||||
Where("? IS NULL", bun.Ident("status.in_reply_to_uri")).
|
Where("? IS NULL", bun.Ident("status.in_reply_to_uri"))
|
||||||
Where("? IS NULL", bun.Ident("status.boost_of_id"))
|
|
||||||
|
if hideBoosts {
|
||||||
|
q = q.Where("? IS NULL", bun.Ident("status.boost_of_id"))
|
||||||
|
}
|
||||||
|
|
||||||
// Select statuses for this account according
|
// Select statuses for this account according
|
||||||
// to their web visibility preference.
|
// to their web visibility preference.
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -489,7 +490,10 @@ func deriveBunDBPGOptions() (*pgx.ConnConfig, error) {
|
||||||
cfg.Host = address
|
cfg.Host = address
|
||||||
}
|
}
|
||||||
if port := config.GetDbPort(); port > 0 {
|
if port := config.GetDbPort(); port > 0 {
|
||||||
cfg.Port = uint16(port)
|
if port > math.MaxUint16 {
|
||||||
|
return nil, errors.New("invalid port, must be in range 1-65535")
|
||||||
|
}
|
||||||
|
cfg.Port = uint16(port) // #nosec G115 -- Just validated above.
|
||||||
}
|
}
|
||||||
if u := config.GetDbUser(); u != "" {
|
if u := config.GetDbUser(); u != "" {
|
||||||
cfg.User = u
|
cfg.User = u
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// GoToSocial
|
||||||
|
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
up := func(ctx context.Context, db *bun.DB) error {
|
||||||
|
_, err := db.ExecContext(ctx, "ALTER TABLE ? ADD COLUMN ? BOOLEAN DEFAULT FALSE", bun.Ident("account_settings"), bun.Ident("hide_boosts"))
|
||||||
|
if err != nil && !(strings.Contains(err.Error(), "already exists") || strings.Contains(err.Error(), "duplicate column name") || strings.Contains(err.Error(), "SQLSTATE 42701")) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
down := func(ctx context.Context, db *bun.DB) error {
|
||||||
|
_, err := db.ExecContext(ctx, "ALTER TABLE ? DROP COLUMN ?", bun.Ident("account_settings"), bun.Ident("hide_boosts"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Migrations.Register(up, down); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -97,11 +97,11 @@ func() (*media.ProcessingEmoji, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get maximum supported remote emoji size.
|
// Get maximum supported remote emoji size.
|
||||||
maxsz := config.GetMediaEmojiRemoteMaxSize()
|
maxsz := int64(config.GetMediaEmojiRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Prepare data function to dereference remote emoji media.
|
// Prepare data function to dereference remote emoji media.
|
||||||
data := func(context.Context) (io.ReadCloser, error) {
|
data := func(context.Context) (io.ReadCloser, error) {
|
||||||
return tsport.DereferenceMedia(ctx, url, int64(maxsz))
|
return tsport.DereferenceMedia(ctx, url, maxsz)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new emoji with prepared info.
|
// Create new emoji with prepared info.
|
||||||
|
@ -189,11 +189,11 @@ func() (*media.ProcessingEmoji, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get maximum supported remote emoji size.
|
// Get maximum supported remote emoji size.
|
||||||
maxsz := config.GetMediaEmojiRemoteMaxSize()
|
maxsz := int64(config.GetMediaEmojiRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Prepare data function to dereference remote emoji media.
|
// Prepare data function to dereference remote emoji media.
|
||||||
data := func(context.Context) (io.ReadCloser, error) {
|
data := func(context.Context) (io.ReadCloser, error) {
|
||||||
return tsport.DereferenceMedia(ctx, url, int64(maxsz))
|
return tsport.DereferenceMedia(ctx, url, maxsz)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update emoji with prepared info.
|
// Update emoji with prepared info.
|
||||||
|
@ -255,11 +255,11 @@ func() (*media.ProcessingEmoji, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get maximum supported remote emoji size.
|
// Get maximum supported remote emoji size.
|
||||||
maxsz := config.GetMediaEmojiRemoteMaxSize()
|
maxsz := int64(config.GetMediaEmojiRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Prepare data function to dereference remote emoji media.
|
// Prepare data function to dereference remote emoji media.
|
||||||
data := func(context.Context) (io.ReadCloser, error) {
|
data := func(context.Context) (io.ReadCloser, error) {
|
||||||
return tsport.DereferenceMedia(ctx, url, int64(maxsz))
|
return tsport.DereferenceMedia(ctx, url, maxsz)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recache emoji with prepared info.
|
// Recache emoji with prepared info.
|
||||||
|
|
|
@ -77,14 +77,14 @@ func() (*media.ProcessingMedia, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get maximum supported remote media size.
|
// Get maximum supported remote media size.
|
||||||
maxsz := config.GetMediaRemoteMaxSize()
|
maxsz := int64(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Create media with prepared info.
|
// Create media with prepared info.
|
||||||
return d.mediaManager.CreateMedia(
|
return d.mediaManager.CreateMedia(
|
||||||
ctx,
|
ctx,
|
||||||
accountID,
|
accountID,
|
||||||
func(ctx context.Context) (io.ReadCloser, error) {
|
func(ctx context.Context) (io.ReadCloser, error) {
|
||||||
return tsport.DereferenceMedia(ctx, url, int64(maxsz))
|
return tsport.DereferenceMedia(ctx, url, maxsz)
|
||||||
},
|
},
|
||||||
info,
|
info,
|
||||||
)
|
)
|
||||||
|
@ -168,14 +168,14 @@ func() (*media.ProcessingMedia, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get maximum supported remote media size.
|
// Get maximum supported remote media size.
|
||||||
maxsz := config.GetMediaRemoteMaxSize()
|
maxsz := int64(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Recache media with prepared info,
|
// Recache media with prepared info,
|
||||||
// this will also update media in db.
|
// this will also update media in db.
|
||||||
return d.mediaManager.CacheMedia(
|
return d.mediaManager.CacheMedia(
|
||||||
attach,
|
attach,
|
||||||
func(ctx context.Context) (io.ReadCloser, error) {
|
func(ctx context.Context) (io.ReadCloser, error) {
|
||||||
return tsport.DereferenceMedia(ctx, url, int64(maxsz))
|
return tsport.DereferenceMedia(ctx, url, maxsz)
|
||||||
},
|
},
|
||||||
), nil
|
), nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,6 +33,7 @@ type AccountSettings struct {
|
||||||
Theme string `bun:",nullzero"` // Preset CSS theme filename selected by this Account (empty string if nothing set).
|
Theme string `bun:",nullzero"` // Preset CSS theme filename selected by this Account (empty string if nothing set).
|
||||||
CustomCSS string `bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses.
|
CustomCSS string `bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses.
|
||||||
EnableRSS *bool `bun:",nullzero,notnull,default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed
|
EnableRSS *bool `bun:",nullzero,notnull,default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed
|
||||||
|
HideBoosts *bool `bun:",nullzero,notnull,default:false"` // Hide boosts from this accounts profile page.
|
||||||
HideCollections *bool `bun:",nullzero,notnull,default:false"` // Hide this account's followers/following collections.
|
HideCollections *bool `bun:",nullzero,notnull,default:false"` // Hide this account's followers/following collections.
|
||||||
WebVisibility Visibility `bun:",nullzero,notnull,default:public"` // Visibility level of statuses that visitors can view via the web profile.
|
WebVisibility Visibility `bun:",nullzero,notnull,default:public"` // Visibility level of statuses that visitors can view via the web profile.
|
||||||
InteractionPolicyDirect *InteractionPolicy `bun:""` // Interaction policy to use for new direct visibility statuses by this account. If null, assume default policy.
|
InteractionPolicyDirect *InteractionPolicy `bun:""` // Interaction policy to use for new direct visibility statuses by this account. If null, assume default policy.
|
||||||
|
|
|
@ -340,14 +340,14 @@ func (c *Client) do(r *Request) (rsp *http.Response, retry bool, err error) {
|
||||||
|
|
||||||
if u, _ := strconv.ParseUint(after, 10, 32); u != 0 {
|
if u, _ := strconv.ParseUint(after, 10, 32); u != 0 {
|
||||||
// An integer no. of backoff seconds was provided.
|
// An integer no. of backoff seconds was provided.
|
||||||
r.backoff = time.Duration(u) * time.Second
|
r.backoff = time.Duration(u) * time.Second // #nosec G115 -- We clamp backoff below.
|
||||||
} else if at, _ := http.ParseTime(after); !at.Before(now) {
|
} else if at, _ := http.ParseTime(after); !at.Before(now) {
|
||||||
// An HTTP formatted future date-time was provided.
|
// An HTTP formatted future date-time was provided.
|
||||||
r.backoff = at.Sub(now)
|
r.backoff = at.Sub(now)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't let their provided backoff exceed our max.
|
// Don't let their provided backoff exceed our max.
|
||||||
if max := baseBackoff * time.Duration(c.retries); //
|
if max := baseBackoff * time.Duration(c.retries); // #nosec G115 -- We control c.retries.
|
||||||
r.backoff > max {
|
r.backoff > max {
|
||||||
r.backoff = max
|
r.backoff = max
|
||||||
}
|
}
|
||||||
|
|
|
@ -556,10 +556,10 @@ func (res *ffprobeResult) Process() (*result, error) {
|
||||||
if p := strings.SplitN(str, "/", 2); len(p) == 2 {
|
if p := strings.SplitN(str, "/", 2); len(p) == 2 {
|
||||||
n, _ := strconv.ParseUint(p[0], 10, 32)
|
n, _ := strconv.ParseUint(p[0], 10, 32)
|
||||||
d, _ := strconv.ParseUint(p[1], 10, 32)
|
d, _ := strconv.ParseUint(p[1], 10, 32)
|
||||||
num, den = uint32(n), uint32(d)
|
num, den = uint32(n), uint32(d) // #nosec G115 -- ParseUint is configured to check
|
||||||
} else {
|
} else {
|
||||||
n, _ := strconv.ParseUint(p[0], 10, 32)
|
n, _ := strconv.ParseUint(p[0], 10, 32)
|
||||||
num = uint32(n)
|
num = uint32(n) // #nosec G115 -- ParseUint is configured to check
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set final divised framerate.
|
// Set final divised framerate.
|
||||||
|
|
|
@ -399,9 +399,9 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
|
||||||
g16 := uint16(s[1])
|
g16 := uint16(s[1])
|
||||||
b16 := uint16(s[2])
|
b16 := uint16(s[2])
|
||||||
a16 := uint16(a)
|
a16 := uint16(a)
|
||||||
d[0] = uint8(r16 * 0xff / a16)
|
d[0] = uint8(r16 * 0xff / a16) // #nosec G115 -- Overflow desired.
|
||||||
d[1] = uint8(g16 * 0xff / a16)
|
d[1] = uint8(g16 * 0xff / a16) // #nosec G115 -- Overflow desired.
|
||||||
d[2] = uint8(b16 * 0xff / a16)
|
d[2] = uint8(b16 * 0xff / a16) // #nosec G115 -- Overflow desired.
|
||||||
d[3] = a
|
d[3] = a
|
||||||
}
|
}
|
||||||
j += 4
|
j += 4
|
||||||
|
@ -431,9 +431,9 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
|
||||||
g32 := uint32(s[2])<<8 | uint32(s[3])
|
g32 := uint32(s[2])<<8 | uint32(s[3])
|
||||||
b32 := uint32(s[4])<<8 | uint32(s[5])
|
b32 := uint32(s[4])<<8 | uint32(s[5])
|
||||||
a32 := uint32(s[6])<<8 | uint32(s[7])
|
a32 := uint32(s[6])<<8 | uint32(s[7])
|
||||||
d[0] = uint8((r32 * 0xffff / a32) >> 8)
|
d[0] = uint8((r32 * 0xffff / a32) >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[1] = uint8((g32 * 0xffff / a32) >> 8)
|
d[1] = uint8((g32 * 0xffff / a32) >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[2] = uint8((b32 * 0xffff / a32) >> 8)
|
d[2] = uint8((b32 * 0xffff / a32) >> 8) // #nosec G115 -- Overflow desired.
|
||||||
}
|
}
|
||||||
d[3] = a
|
d[3] = a
|
||||||
j += 4
|
j += 4
|
||||||
|
@ -509,30 +509,30 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
|
||||||
cr1 := int32(img.Cr[ic]) - 128
|
cr1 := int32(img.Cr[ic]) - 128
|
||||||
|
|
||||||
r := yy1 + 91881*cr1
|
r := yy1 + 91881*cr1
|
||||||
if uint32(r)&0xff000000 == 0 {
|
if uint32(r)&0xff000000 == 0 { //nolint:gosec
|
||||||
r >>= 16
|
r >>= 16
|
||||||
} else {
|
} else {
|
||||||
r = ^(r >> 31)
|
r = ^(r >> 31)
|
||||||
}
|
}
|
||||||
|
|
||||||
g := yy1 - 22554*cb1 - 46802*cr1
|
g := yy1 - 22554*cb1 - 46802*cr1
|
||||||
if uint32(g)&0xff000000 == 0 {
|
if uint32(g)&0xff000000 == 0 { //nolint:gosec
|
||||||
g >>= 16
|
g >>= 16
|
||||||
} else {
|
} else {
|
||||||
g = ^(g >> 31)
|
g = ^(g >> 31)
|
||||||
}
|
}
|
||||||
|
|
||||||
b := yy1 + 116130*cb1
|
b := yy1 + 116130*cb1
|
||||||
if uint32(b)&0xff000000 == 0 {
|
if uint32(b)&0xff000000 == 0 { //nolint:gosec
|
||||||
b >>= 16
|
b >>= 16
|
||||||
} else {
|
} else {
|
||||||
b = ^(b >> 31)
|
b = ^(b >> 31)
|
||||||
}
|
}
|
||||||
|
|
||||||
d := dst[j : j+4 : j+4]
|
d := dst[j : j+4 : j+4]
|
||||||
d[0] = uint8(r)
|
d[0] = uint8(r) // #nosec G115 -- Overflow desired.
|
||||||
d[1] = uint8(g)
|
d[1] = uint8(g) // #nosec G115 -- Overflow desired.
|
||||||
d[2] = uint8(b)
|
d[2] = uint8(b) // #nosec G115 -- Overflow desired.
|
||||||
d[3] = 0xff
|
d[3] = 0xff
|
||||||
|
|
||||||
iy++
|
iy++
|
||||||
|
@ -569,9 +569,9 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
|
||||||
d := dst[j : j+4 : j+4]
|
d := dst[j : j+4 : j+4]
|
||||||
switch a16 {
|
switch a16 {
|
||||||
case 0xffff:
|
case 0xffff:
|
||||||
d[0] = uint8(r16 >> 8)
|
d[0] = uint8(r16 >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[1] = uint8(g16 >> 8)
|
d[1] = uint8(g16 >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[2] = uint8(b16 >> 8)
|
d[2] = uint8(b16 >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[3] = 0xff
|
d[3] = 0xff
|
||||||
case 0:
|
case 0:
|
||||||
d[0] = 0
|
d[0] = 0
|
||||||
|
@ -579,10 +579,10 @@ func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
|
||||||
d[2] = 0
|
d[2] = 0
|
||||||
d[3] = 0
|
d[3] = 0
|
||||||
default:
|
default:
|
||||||
d[0] = uint8(((r16 * 0xffff) / a16) >> 8)
|
d[0] = uint8(((r16 * 0xffff) / a16) >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[1] = uint8(((g16 * 0xffff) / a16) >> 8)
|
d[1] = uint8(((g16 * 0xffff) / a16) >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[2] = uint8(((b16 * 0xffff) / a16) >> 8)
|
d[2] = uint8(((b16 * 0xffff) / a16) >> 8) // #nosec G115 -- Overflow desired.
|
||||||
d[3] = uint8(a16 >> 8)
|
d[3] = uint8(a16 >> 8) // #nosec G115 -- Overflow desired.
|
||||||
}
|
}
|
||||||
j += 4
|
j += 4
|
||||||
}
|
}
|
||||||
|
@ -617,7 +617,7 @@ func clampFloat(x float64) uint8 {
|
||||||
return 255
|
return 255
|
||||||
}
|
}
|
||||||
if v > 0 {
|
if v > 0 {
|
||||||
return uint8(v)
|
return uint8(v) // #nosec G115 -- Just checked.
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,6 @@ func (m *Manager) RefetchEmojis(ctx context.Context, domain string, dereferenceM
|
||||||
refetchIDs []string
|
refetchIDs []string
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get max supported remote emoji media size.
|
|
||||||
maxsz := config.GetMediaEmojiRemoteMaxSize()
|
|
||||||
|
|
||||||
// page through emojis 20 at a time, looking for those with missing images
|
// page through emojis 20 at a time, looking for those with missing images
|
||||||
for {
|
for {
|
||||||
// Fetch next block of emojis from database
|
// Fetch next block of emojis from database
|
||||||
|
@ -111,8 +108,10 @@ func (m *Manager) RefetchEmojis(ctx context.Context, domain string, dereferenceM
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get max supported remote emoji media size.
|
||||||
|
maxsz := int64(config.GetMediaEmojiRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
dataFunc := func(ctx context.Context) (reader io.ReadCloser, err error) {
|
dataFunc := func(ctx context.Context) (reader io.ReadCloser, err error) {
|
||||||
return dereferenceMedia(ctx, emojiImageIRI, int64(maxsz))
|
return dereferenceMedia(ctx, emojiImageIRI, maxsz)
|
||||||
}
|
}
|
||||||
|
|
||||||
processingEmoji, err := m.UpdateEmoji(ctx, emoji, dataFunc, AdditionalEmojiInfo{
|
processingEmoji, err := m.UpdateEmoji(ctx, emoji, dataFunc, AdditionalEmojiInfo{
|
||||||
|
|
|
@ -145,7 +145,7 @@ func drainToTmp(rc io.ReadCloser) (string, error) {
|
||||||
// Check to see if limit was reached,
|
// Check to see if limit was reached,
|
||||||
// (produces more useful error messages).
|
// (produces more useful error messages).
|
||||||
if lr != nil && lr.N <= 0 {
|
if lr != nil && lr.N <= 0 {
|
||||||
err := fmt.Errorf("reached read limit %s", bytesize.Size(limit))
|
err := fmt.Errorf("reached read limit %s", bytesize.Size(limit)) // #nosec G115 -- Just logging
|
||||||
return path, gtserror.SetLimitReached(err)
|
return path, gtserror.SetLimitReached(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,7 @@ func Logger(logClientIP bool) gin.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a nicer looking bytecount
|
// Generate a nicer looking bytecount
|
||||||
size := bytesize.Size(c.Writer.Size())
|
size := bytesize.Size(c.Writer.Size()) // #nosec G115 -- Just logging
|
||||||
|
|
||||||
// Finally, write log entry with status text + body size.
|
// Finally, write log entry with status text + body size.
|
||||||
l.Logf(lvl, "%s: wrote %s", statusText, size)
|
l.Logf(lvl, "%s: wrote %s", statusText, size)
|
||||||
|
|
|
@ -48,7 +48,7 @@ func NewRequestID() string {
|
||||||
b := make([]byte, 12)
|
b := make([]byte, 12)
|
||||||
|
|
||||||
// Get current time in milliseconds.
|
// Get current time in milliseconds.
|
||||||
ms := uint64(time.Now().UnixMilli())
|
ms := uint64(time.Now().UnixMilli()) // #nosec G115 -- Pre-1970 clock?
|
||||||
|
|
||||||
// Store binary time data in byte buffer.
|
// Store binary time data in byte buffer.
|
||||||
binary.LittleEndian.PutUint64(b[0:8], ms)
|
binary.LittleEndian.PutUint64(b[0:8], ms)
|
||||||
|
|
|
@ -82,12 +82,16 @@ func Throttle(cpuMultiplier int, retryAfter time.Duration) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {}
|
return func(c *gin.Context) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if retryAfter < 0 {
|
||||||
|
retryAfter = 0
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
limit = runtime.GOMAXPROCS(0) * cpuMultiplier
|
limit = runtime.GOMAXPROCS(0) * cpuMultiplier
|
||||||
queueLimit = limit * cpuMultiplier
|
queueLimit = limit * cpuMultiplier
|
||||||
tokens = make(chan token, limit)
|
tokens = make(chan token, limit)
|
||||||
requestCount = atomic.Int64{}
|
requestCount = atomic.Int64{}
|
||||||
retryAfterStr = strconv.FormatUint(uint64(retryAfter/time.Second), 10)
|
retryAfterStr = strconv.FormatUint(uint64(retryAfter/time.Second), 10) // #nosec G115 -- Checked right above
|
||||||
)
|
)
|
||||||
|
|
||||||
// prefill token channel
|
// prefill token channel
|
||||||
|
|
|
@ -42,6 +42,16 @@ func (suite *GetRSSTestSuite) TestGetAccountRSSAdmin() {
|
||||||
<description>Posts from @admin@localhost:8080</description>
|
<description>Posts from @admin@localhost:8080</description>
|
||||||
<pubDate>Wed, 20 Oct 2021 10:41:37 +0000</pubDate>
|
<pubDate>Wed, 20 Oct 2021 10:41:37 +0000</pubDate>
|
||||||
<lastBuildDate>Wed, 20 Oct 2021 10:41:37 +0000</lastBuildDate>
|
<lastBuildDate>Wed, 20 Oct 2021 10:41:37 +0000</lastBuildDate>
|
||||||
|
<item>
|
||||||
|
<title>introduction post</title>
|
||||||
|
<link>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</link>
|
||||||
|
<description>@the_mighty_zork@localhost:8080 made a new post: "hello everyone!"</description>
|
||||||
|
<content:encoded><![CDATA[hello everyone!]]></content:encoded>
|
||||||
|
<author>@the_mighty_zork@localhost:8080</author>
|
||||||
|
<guid isPermaLink="true">http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</guid>
|
||||||
|
<pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>
|
||||||
|
<source>http://localhost:8080/@the_mighty_zork/feed.rss</source>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<title>open to see some puppies</title>
|
<title>open to see some puppies</title>
|
||||||
<link>http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37</link>
|
<link>http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37</link>
|
||||||
|
|
|
@ -274,6 +274,11 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
||||||
settingsColumns = append(settingsColumns, "enable_rss")
|
settingsColumns = append(settingsColumns, "enable_rss")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if form.HideBoosts != nil {
|
||||||
|
account.Settings.HideBoosts = form.HideBoosts
|
||||||
|
settingsColumns = append(settingsColumns, "hide_boosts")
|
||||||
|
}
|
||||||
|
|
||||||
if form.HideCollections != nil {
|
if form.HideCollections != nil {
|
||||||
account.Settings.HideCollections = form.HideCollections
|
account.Settings.HideCollections = form.HideCollections
|
||||||
settingsColumns = append(settingsColumns, "hide_collections")
|
settingsColumns = append(settingsColumns, "hide_collections")
|
||||||
|
@ -463,9 +468,10 @@ func (p *Processor) UpdateAvatar(
|
||||||
) {
|
) {
|
||||||
// Get maximum supported local media size.
|
// Get maximum supported local media size.
|
||||||
maxsz := config.GetMediaLocalMaxSize()
|
maxsz := config.GetMediaLocalMaxSize()
|
||||||
|
maxszInt64 := int64(maxsz) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Ensure media within size bounds.
|
// Ensure media within size bounds.
|
||||||
if avatar.Size > int64(maxsz) {
|
if avatar.Size > maxszInt64 {
|
||||||
text := fmt.Sprintf("media exceeds configured max size: %s", maxsz)
|
text := fmt.Sprintf("media exceeds configured max size: %s", maxsz)
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
@ -478,7 +484,7 @@ func (p *Processor) UpdateAvatar(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the multipart file reader to ensure is limited to max.
|
// Wrap the multipart file reader to ensure is limited to max.
|
||||||
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, int64(maxsz))
|
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, maxszInt64)
|
||||||
|
|
||||||
// Write to instance storage.
|
// Write to instance storage.
|
||||||
return p.c.StoreLocalMedia(ctx,
|
return p.c.StoreLocalMedia(ctx,
|
||||||
|
@ -508,9 +514,10 @@ func (p *Processor) UpdateHeader(
|
||||||
) {
|
) {
|
||||||
// Get maximum supported local media size.
|
// Get maximum supported local media size.
|
||||||
maxsz := config.GetMediaLocalMaxSize()
|
maxsz := config.GetMediaLocalMaxSize()
|
||||||
|
maxszInt64 := int64(maxsz) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Ensure media within size bounds.
|
// Ensure media within size bounds.
|
||||||
if header.Size > int64(maxsz) {
|
if header.Size > maxszInt64 {
|
||||||
text := fmt.Sprintf("media exceeds configured max size: %s", maxsz)
|
text := fmt.Sprintf("media exceeds configured max size: %s", maxsz)
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
@ -523,7 +530,7 @@ func (p *Processor) UpdateHeader(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the multipart file reader to ensure is limited to max.
|
// Wrap the multipart file reader to ensure is limited to max.
|
||||||
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, int64(maxsz))
|
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, maxszInt64)
|
||||||
|
|
||||||
// Write to instance storage.
|
// Write to instance storage.
|
||||||
return p.c.StoreLocalMedia(ctx,
|
return p.c.StoreLocalMedia(ctx,
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-bytesize"
|
|
||||||
"codeberg.org/gruf/go-iotools"
|
"codeberg.org/gruf/go-iotools"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
@ -46,9 +45,10 @@ func (p *Processor) EmojiCreate(
|
||||||
|
|
||||||
// Get maximum supported local emoji size.
|
// Get maximum supported local emoji size.
|
||||||
maxsz := config.GetMediaEmojiLocalMaxSize()
|
maxsz := config.GetMediaEmojiLocalMaxSize()
|
||||||
|
maxszInt64 := int64(maxsz) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Ensure media within size bounds.
|
// Ensure media within size bounds.
|
||||||
if form.Image.Size > int64(maxsz) {
|
if form.Image.Size > maxszInt64 {
|
||||||
text := fmt.Sprintf("emoji exceeds configured max size: %s", maxsz)
|
text := fmt.Sprintf("emoji exceeds configured max size: %s", maxsz)
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func (p *Processor) EmojiCreate(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the multipart file reader to ensure is limited to max.
|
// Wrap the multipart file reader to ensure is limited to max.
|
||||||
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, int64(maxsz))
|
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, maxszInt64)
|
||||||
data := func(context.Context) (io.ReadCloser, error) {
|
data := func(context.Context) (io.ReadCloser, error) {
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
@ -301,9 +301,10 @@ func (p *Processor) emojiUpdateCopy(
|
||||||
|
|
||||||
// Get maximum supported local emoji size.
|
// Get maximum supported local emoji size.
|
||||||
maxsz := config.GetMediaEmojiLocalMaxSize()
|
maxsz := config.GetMediaEmojiLocalMaxSize()
|
||||||
|
maxszInt := int(maxsz) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Ensure target emoji image within size bounds.
|
// Ensure target emoji image within size bounds.
|
||||||
if bytesize.Size(target.ImageFileSize) > maxsz {
|
if target.ImageFileSize > maxszInt {
|
||||||
text := fmt.Sprintf("emoji exceeds configured max size: %s", maxsz)
|
text := fmt.Sprintf("emoji exceeds configured max size: %s", maxsz)
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
@ -442,9 +443,10 @@ func (p *Processor) emojiUpdateModify(
|
||||||
|
|
||||||
// Get maximum supported local emoji size.
|
// Get maximum supported local emoji size.
|
||||||
maxsz := config.GetMediaEmojiLocalMaxSize()
|
maxsz := config.GetMediaEmojiLocalMaxSize()
|
||||||
|
maxszInt64 := int64(maxsz) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Ensure media within size bounds.
|
// Ensure media within size bounds.
|
||||||
if image.Size > int64(maxsz) {
|
if image.Size > maxszInt64 {
|
||||||
text := fmt.Sprintf("emoji exceeds configured max size: %s", maxsz)
|
text := fmt.Sprintf("emoji exceeds configured max size: %s", maxsz)
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
@ -457,7 +459,7 @@ func (p *Processor) emojiUpdateModify(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the multipart file reader to ensure is limited to max.
|
// Wrap the multipart file reader to ensure is limited to max.
|
||||||
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, int64(maxsz))
|
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, int64(maxsz)) // #nosec G115 -- Already validated.
|
||||||
data := func(context.Context) (io.ReadCloser, error) {
|
data := func(context.Context) (io.ReadCloser, error) {
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,10 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
|
||||||
|
|
||||||
// Get maximum supported local media size.
|
// Get maximum supported local media size.
|
||||||
maxsz := config.GetMediaLocalMaxSize()
|
maxsz := config.GetMediaLocalMaxSize()
|
||||||
|
maxszInt64 := int64(maxsz) // #nosec G115 -- Already validated.
|
||||||
|
|
||||||
// Ensure media within size bounds.
|
// Ensure media within size bounds.
|
||||||
if form.File.Size > int64(maxsz) {
|
if form.File.Size > maxszInt64 {
|
||||||
text := fmt.Sprintf("media exceeds configured max size: %s", maxsz)
|
text := fmt.Sprintf("media exceeds configured max size: %s", maxsz)
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
@ -58,7 +59,7 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the multipart file reader to ensure is limited to max.
|
// Wrap the multipart file reader to ensure is limited to max.
|
||||||
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, int64(maxsz))
|
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, maxszInt64)
|
||||||
|
|
||||||
// Create local media and write to instance storage.
|
// Create local media and write to instance storage.
|
||||||
attachment, errWithCode := p.c.StoreLocalMedia(ctx,
|
attachment, errWithCode := p.c.StoreLocalMedia(ctx,
|
||||||
|
|
|
@ -53,7 +53,7 @@ func (t *transport) DereferenceMedia(ctx context.Context, iri *url.URL, maxsz in
|
||||||
// Check media within size limit.
|
// Check media within size limit.
|
||||||
if rsp.ContentLength > maxsz {
|
if rsp.ContentLength > maxsz {
|
||||||
_ = rsp.Body.Close() // close early.
|
_ = rsp.Body.Close() // close early.
|
||||||
sz := bytesize.Size(maxsz) // nicer log format
|
sz := bytesize.Size(maxsz) //nolint:gosec
|
||||||
return nil, gtserror.Newf("media body exceeds max size %s", sz)
|
return nil, gtserror.Newf("media body exceeds max size %s", sz)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,7 @@ func (c *Converter) accountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
|
||||||
// Bits that vary between remote + local accounts:
|
// Bits that vary between remote + local accounts:
|
||||||
// - Account (acct) string.
|
// - Account (acct) string.
|
||||||
// - Role.
|
// - Role.
|
||||||
// - Settings things (enableRSS, theme, customCSS, hideCollections).
|
// - Settings things (enableRSS, theme, customCSS, hideBoosts ,hideCollections).
|
||||||
|
|
||||||
var (
|
var (
|
||||||
acct string
|
acct string
|
||||||
|
@ -310,6 +310,7 @@ func (c *Converter) accountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
|
||||||
enableRSS bool
|
enableRSS bool
|
||||||
theme string
|
theme string
|
||||||
customCSS string
|
customCSS string
|
||||||
|
hideBoosts bool
|
||||||
hideCollections bool
|
hideCollections bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -338,6 +339,7 @@ func (c *Converter) accountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
|
||||||
enableRSS = *a.Settings.EnableRSS
|
enableRSS = *a.Settings.EnableRSS
|
||||||
theme = a.Settings.Theme
|
theme = a.Settings.Theme
|
||||||
customCSS = a.Settings.CustomCSS
|
customCSS = a.Settings.CustomCSS
|
||||||
|
hideBoosts = *a.Settings.HideBoosts
|
||||||
hideCollections = *a.Settings.HideCollections
|
hideCollections = *a.Settings.HideCollections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,6 +382,7 @@ func (c *Converter) accountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
|
||||||
Theme: theme,
|
Theme: theme,
|
||||||
CustomCSS: customCSS,
|
CustomCSS: customCSS,
|
||||||
EnableRSS: enableRSS,
|
EnableRSS: enableRSS,
|
||||||
|
HideBoosts: hideBoosts,
|
||||||
HideCollections: hideCollections,
|
HideCollections: hideCollections,
|
||||||
Roles: roles,
|
Roles: roles,
|
||||||
}
|
}
|
||||||
|
@ -647,7 +650,7 @@ func (c *Converter) AttachmentToAPIAttachment(ctx context.Context, media *gtsmod
|
||||||
Size: toAPISize(media.FileMeta.Original.Width, media.FileMeta.Original.Height),
|
Size: toAPISize(media.FileMeta.Original.Width, media.FileMeta.Original.Height),
|
||||||
FrameRate: toAPIFrameRate(media.FileMeta.Original.Framerate),
|
FrameRate: toAPIFrameRate(media.FileMeta.Original.Framerate),
|
||||||
Duration: util.PtrOrZero(media.FileMeta.Original.Duration),
|
Duration: util.PtrOrZero(media.FileMeta.Original.Duration),
|
||||||
Bitrate: int(util.PtrOrZero(media.FileMeta.Original.Bitrate)),
|
Bitrate: util.PtrOrZero(media.FileMeta.Original.Bitrate),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy over local file URL.
|
// Copy over local file URL.
|
||||||
|
@ -1092,7 +1095,15 @@ func (c *Converter) StatusToWebStatus(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
s *gtsmodel.Status,
|
s *gtsmodel.Status,
|
||||||
) (*apimodel.WebStatus, error) {
|
) (*apimodel.WebStatus, error) {
|
||||||
apiStatus, err := c.statusToFrontend(ctx, s,
|
|
||||||
|
isBoost := s.BoostOf != nil
|
||||||
|
status := s
|
||||||
|
|
||||||
|
if isBoost {
|
||||||
|
status = s.BoostOf
|
||||||
|
}
|
||||||
|
|
||||||
|
apiStatus, err := c.statusToFrontend(ctx, status,
|
||||||
nil, // No authed requester.
|
nil, // No authed requester.
|
||||||
statusfilter.FilterContextNone, // No filters.
|
statusfilter.FilterContextNone, // No filters.
|
||||||
nil, // No filters.
|
nil, // No filters.
|
||||||
|
@ -1103,7 +1114,7 @@ func (c *Converter) StatusToWebStatus(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert status author to web model.
|
// Convert status author to web model.
|
||||||
acct, err := c.AccountToWebAccount(ctx, s.Account)
|
acct, err := c.AccountToWebAccount(ctx, status.Account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1113,6 +1124,14 @@ func (c *Converter) StatusToWebStatus(
|
||||||
Account: acct,
|
Account: acct,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isBoost {
|
||||||
|
reblogAcct, err := c.AccountToWebAccount(ctx, s.Account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
webStatus.ReblogAccount = reblogAcct
|
||||||
|
}
|
||||||
|
|
||||||
// Whack a newline before and after each "pre" to make it easier to outdent it.
|
// Whack a newline before and after each "pre" to make it easier to outdent it.
|
||||||
webStatus.Content = strings.ReplaceAll(webStatus.Content, "<pre>", "\n<pre>")
|
webStatus.Content = strings.ReplaceAll(webStatus.Content, "<pre>", "\n<pre>")
|
||||||
webStatus.Content = strings.ReplaceAll(webStatus.Content, "</pre>", "</pre>\n")
|
webStatus.Content = strings.ReplaceAll(webStatus.Content, "</pre>", "</pre>\n")
|
||||||
|
@ -1529,9 +1548,9 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins
|
||||||
Version: config.GetSoftwareVersion(),
|
Version: config.GetSoftwareVersion(),
|
||||||
Languages: config.GetInstanceLanguages().TagStrs(),
|
Languages: config.GetInstanceLanguages().TagStrs(),
|
||||||
Registrations: config.GetAccountsRegistrationOpen(),
|
Registrations: config.GetAccountsRegistrationOpen(),
|
||||||
ApprovalRequired: true, // approval always required
|
ApprovalRequired: true, // approval always required
|
||||||
InvitesEnabled: false, // todo: not supported yet
|
InvitesEnabled: false, // todo: not supported yet
|
||||||
MaxTootChars: uint(config.GetStatusesMaxChars()),
|
MaxTootChars: uint(config.GetStatusesMaxChars()), // #nosec G115 -- Already validated.
|
||||||
Rules: c.InstanceRulesToAPIRules(i.Rules),
|
Rules: c.InstanceRulesToAPIRules(i.Rules),
|
||||||
Terms: i.Terms,
|
Terms: i.Terms,
|
||||||
TermsRaw: i.TermsText,
|
TermsRaw: i.TermsText,
|
||||||
|
@ -1551,9 +1570,9 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins
|
||||||
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
|
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
|
||||||
instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes
|
instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes
|
||||||
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
|
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
|
||||||
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize())
|
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
|
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
|
||||||
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize())
|
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
|
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
|
||||||
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit
|
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit
|
||||||
instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
|
instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
|
||||||
|
@ -1563,7 +1582,7 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins
|
||||||
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
||||||
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
||||||
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
|
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
|
||||||
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) // #nosec G115 -- Already validated.
|
||||||
instance.Configuration.OIDCEnabled = config.GetOIDCEnabled()
|
instance.Configuration.OIDCEnabled = config.GetOIDCEnabled()
|
||||||
|
|
||||||
// URLs
|
// URLs
|
||||||
|
@ -1695,9 +1714,9 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins
|
||||||
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
|
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
|
||||||
instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes
|
instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes
|
||||||
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
|
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
|
||||||
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize())
|
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
|
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
|
||||||
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize())
|
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
|
||||||
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
|
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
|
||||||
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit
|
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit
|
||||||
instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
|
instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
|
||||||
|
@ -1707,7 +1726,7 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins
|
||||||
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
||||||
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
||||||
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
|
instance.Configuration.Accounts.MaxProfileFields = instanceAccountsMaxProfileFields
|
||||||
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize()) // #nosec G115 -- Already validated.
|
||||||
instance.Configuration.OIDCEnabled = config.GetOIDCEnabled()
|
instance.Configuration.OIDCEnabled = config.GetOIDCEnabled()
|
||||||
|
|
||||||
// registrations
|
// registrations
|
||||||
|
|
|
@ -1402,6 +1402,7 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"fields": []
|
"fields": []
|
||||||
},
|
},
|
||||||
|
"reblog_account": null,
|
||||||
"media_attachments": [
|
"media_attachments": [
|
||||||
{
|
{
|
||||||
"id": "01HE7Y3C432WRSNS10EZM86SA5",
|
"id": "01HE7Y3C432WRSNS10EZM86SA5",
|
||||||
|
|
|
@ -39,6 +39,12 @@
|
||||||
func (c *Converter) StatusToRSSItem(ctx context.Context, s *gtsmodel.Status) (*feeds.Item, error) {
|
func (c *Converter) StatusToRSSItem(ctx context.Context, s *gtsmodel.Status) (*feeds.Item, error) {
|
||||||
// see https://cyber.harvard.edu/rss/rss.html
|
// see https://cyber.harvard.edu/rss/rss.html
|
||||||
|
|
||||||
|
// If status is a boost,
|
||||||
|
// display the boost instead.
|
||||||
|
if s.BoostOf != nil {
|
||||||
|
s = s.BoostOf
|
||||||
|
}
|
||||||
|
|
||||||
// Title -- The title of the item.
|
// Title -- The title of the item.
|
||||||
// example: Venice Film Festival Tries to Quit Sinking
|
// example: Venice Film Festival Tries to Quit Sinking
|
||||||
var title string
|
var title string
|
||||||
|
|
|
@ -657,6 +657,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
EnableRSS: util.Ptr(false),
|
EnableRSS: util.Ptr(false),
|
||||||
|
HideBoosts: util.Ptr(false),
|
||||||
HideCollections: util.Ptr(false),
|
HideCollections: util.Ptr(false),
|
||||||
WebVisibility: gtsmodel.VisibilityPublic,
|
WebVisibility: gtsmodel.VisibilityPublic,
|
||||||
},
|
},
|
||||||
|
@ -668,6 +669,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
EnableRSS: util.Ptr(true),
|
EnableRSS: util.Ptr(true),
|
||||||
|
HideBoosts: util.Ptr(false),
|
||||||
HideCollections: util.Ptr(false),
|
HideCollections: util.Ptr(false),
|
||||||
WebVisibility: gtsmodel.VisibilityPublic,
|
WebVisibility: gtsmodel.VisibilityPublic,
|
||||||
},
|
},
|
||||||
|
@ -679,6 +681,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
|
||||||
Sensitive: util.Ptr(false),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
EnableRSS: util.Ptr(true),
|
EnableRSS: util.Ptr(true),
|
||||||
|
HideBoosts: util.Ptr(false),
|
||||||
HideCollections: util.Ptr(false),
|
HideCollections: util.Ptr(false),
|
||||||
WebVisibility: gtsmodel.VisibilityUnlocked,
|
WebVisibility: gtsmodel.VisibilityUnlocked,
|
||||||
},
|
},
|
||||||
|
@ -690,6 +693,7 @@ func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings {
|
||||||
Sensitive: util.Ptr(true),
|
Sensitive: util.Ptr(true),
|
||||||
Language: "fr",
|
Language: "fr",
|
||||||
EnableRSS: util.Ptr(false),
|
EnableRSS: util.Ptr(false),
|
||||||
|
HideBoosts: util.Ptr(false),
|
||||||
HideCollections: util.Ptr(true),
|
HideCollections: util.Ptr(true),
|
||||||
WebVisibility: gtsmodel.VisibilityPublic,
|
WebVisibility: gtsmodel.VisibilityPublic,
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,6 +41,12 @@ main {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.boosted {
|
||||||
|
padding: 0 0.75rem 0.75rem;
|
||||||
|
color: var(--fg-reduced);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.status-header > address {
|
.status-header > address {
|
||||||
/*
|
/*
|
||||||
Avoid stretching so wide that user
|
Avoid stretching so wide that user
|
||||||
|
@ -59,17 +65,27 @@ main {
|
||||||
"avatar author-strap author-strap";
|
"avatar author-strap author-strap";
|
||||||
gap: 0 0.5rem;
|
gap: 0 0.5rem;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
grid-area: avatar;
|
grid-area: avatar;
|
||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
width: 3.5rem;
|
width: 3.5rem;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
border: 0.15rem solid $avatar-border;
|
border: 0.15rem solid $avatar-border;
|
||||||
border-radius: $br;
|
border-radius: $br;
|
||||||
overflow: hidden; /* hides corners from img overflowing */
|
overflow: hidden; /* hides corners from img overflowing */
|
||||||
|
|
||||||
|
.boosted-avatar {
|
||||||
|
height: 50%;
|
||||||
|
width: 50%;
|
||||||
|
z-index: 10;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
inset-inline-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -77,7 +93,7 @@ main {
|
||||||
background: $bg;
|
background: $bg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.author-strap {
|
.author-strap {
|
||||||
grid-area: author-strap;
|
grid-area: author-strap;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -87,7 +103,7 @@ main {
|
||||||
"display display"
|
"display display"
|
||||||
"user user";
|
"user user";
|
||||||
gap: 0 0.5rem;
|
gap: 0 0.5rem;
|
||||||
|
|
||||||
.displayname, .username {
|
.displayname, .username {
|
||||||
justify-self: start;
|
justify-self: start;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
|
@ -95,12 +111,12 @@ main {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.3rem;
|
line-height: 1.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.displayname {
|
.displayname {
|
||||||
grid-area: display;
|
grid-area: display;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.username {
|
.username {
|
||||||
grid-area: user;
|
grid-area: user;
|
||||||
color: $link-fg;
|
color: $link-fg;
|
||||||
|
@ -200,34 +216,34 @@ main {
|
||||||
.poll {
|
.poll {
|
||||||
background-color: $gray2;
|
background-color: $gray2;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-radius: $br;
|
border-radius: $br;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
||||||
.poll-options {
|
.poll-options {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
||||||
.poll-option {
|
.poll-option {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.1rem;
|
gap: 0.1rem;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
meter {
|
meter {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-vote-summary {
|
.poll-vote-summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -236,7 +252,7 @@ main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-info {
|
.poll-info {
|
||||||
background-color: $gray4;
|
background-color: $gray4;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -245,7 +261,7 @@ main {
|
||||||
border-radius: $br-inner;
|
border-radius: $br-inner;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -301,12 +317,12 @@ main {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
grid-template-columns: 1fr auto 1fr;
|
grid-template-columns: 1fr auto 1fr;
|
||||||
grid-template-rows: 1fr 1fr;
|
grid-template-rows: 1fr 1fr;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"eye sensitive ."
|
"eye sensitive ."
|
||||||
". sensitive .";
|
". sensitive .";
|
||||||
|
|
||||||
|
@ -369,7 +385,7 @@ main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0.8rem;
|
padding: 0.8rem;
|
||||||
border: 0.2rem dashed $white2;
|
border: 0.2rem dashed $white2;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -518,4 +534,4 @@ main {
|
||||||
.plyr {
|
.plyr {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ function UserProfileForm({ data: profile }) {
|
||||||
maxPinnedFields: instance?.configuration?.accounts?.max_profile_fields ?? 6
|
maxPinnedFields: instance?.configuration?.accounts?.max_profile_fields ?? 6
|
||||||
};
|
};
|
||||||
}, [instance]);
|
}, [instance]);
|
||||||
|
|
||||||
// Parse out available theme options into nice format.
|
// Parse out available theme options into nice format.
|
||||||
const { data: themes } = useAccountThemesQuery();
|
const { data: themes } = useAccountThemesQuery();
|
||||||
const themeOptions = useMemo(() => {
|
const themeOptions = useMemo(() => {
|
||||||
|
@ -114,6 +114,7 @@ function UserProfileForm({ data: profile }) {
|
||||||
locked: useBoolInput("locked", { source: profile }),
|
locked: useBoolInput("locked", { source: profile }),
|
||||||
discoverable: useBoolInput("discoverable", { source: profile}),
|
discoverable: useBoolInput("discoverable", { source: profile}),
|
||||||
enableRSS: useBoolInput("enable_rss", { source: profile }),
|
enableRSS: useBoolInput("enable_rss", { source: profile }),
|
||||||
|
hideBoosts: useBoolInput("hide_boosts", { source: profile }),
|
||||||
hideCollections: useBoolInput("hide_collections", { source: profile }),
|
hideCollections: useBoolInput("hide_collections", { source: profile }),
|
||||||
webVisibility: useTextInput("web_visibility", { source: profile, valueSelector: (p) => p.source?.web_visibility }),
|
webVisibility: useTextInput("web_visibility", { source: profile, valueSelector: (p) => p.source?.web_visibility }),
|
||||||
fields: useFieldArrayInput("fields_attributes", {
|
fields: useFieldArrayInput("fields_attributes", {
|
||||||
|
@ -158,7 +159,7 @@ function UserProfileForm({ data: profile }) {
|
||||||
autoCapitalize="sentences"
|
autoCapitalize="sentences"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="file-input-with-image-description">
|
<div className="file-input-with-image-description">
|
||||||
<FileInput
|
<FileInput
|
||||||
label="Avatar (1:1 images look best)"
|
label="Avatar (1:1 images look best)"
|
||||||
|
@ -257,6 +258,10 @@ function UserProfileForm({ data: profile }) {
|
||||||
field={form.enableRSS}
|
field={form.enableRSS}
|
||||||
label="Enable RSS feed of posts."
|
label="Enable RSS feed of posts."
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
field={form.hideBoosts}
|
||||||
|
label="Hide boosts from your public page"
|
||||||
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
field={form.hideCollections}
|
field={form.hideCollections}
|
||||||
label="Hide who you follow / are followed by."
|
label="Hide who you follow / are followed by."
|
||||||
|
|
|
@ -247,6 +247,16 @@
|
||||||
class="status expanded"
|
class="status expanded"
|
||||||
{{- includeAttr "status_attributes.tmpl" . | indentAttr 6 }}
|
{{- includeAttr "status_attributes.tmpl" . | indentAttr 6 }}
|
||||||
>
|
>
|
||||||
|
{{- if .ReblogAccount }}
|
||||||
|
<div class="boosted text-cutoff">
|
||||||
|
<i class="fa fa-retweet" aria-hidden="true"></i> 
|
||||||
|
{{- if $.account.DisplayName }}
|
||||||
|
{{- emojify $.account.Emojis (escape $.account.DisplayName) }} boosted
|
||||||
|
{{- else }}
|
||||||
|
{{- $.account.Username }} boosted
|
||||||
|
{{- end }}
|
||||||
|
</div>
|
||||||
|
{{- end }}
|
||||||
{{- include "status.tmpl" . | indent 6 }}
|
{{- include "status.tmpl" . | indent 6 }}
|
||||||
</article>
|
</article>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
@ -264,4 +274,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
@ -48,6 +48,16 @@
|
||||||
alt="Avatar for {{ .Username -}}"
|
alt="Avatar for {{ .Username -}}"
|
||||||
title="Avatar for {{ .Username -}}"
|
title="Avatar for {{ .Username -}}"
|
||||||
>
|
>
|
||||||
|
{{ if $.ReblogAccount }}
|
||||||
|
<img
|
||||||
|
class="boosted-avatar"
|
||||||
|
src="{{ $.ReblogAccount.Avatar }}"
|
||||||
|
alt="Avatar for {{ $.ReblogAccount.Username -}}"
|
||||||
|
title="Avatar for {{ $.ReblogAccount.Username -}}"
|
||||||
|
>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
</picture>
|
</picture>
|
||||||
<div class="author-strap">
|
<div class="author-strap">
|
||||||
<span class="displayname text-cutoff">
|
<span class="displayname text-cutoff">
|
||||||
|
@ -63,4 +73,4 @@
|
||||||
<span class="sr-only">(open profile)</span>
|
<span class="sr-only">(open profile)</span>
|
||||||
</a>
|
</a>
|
||||||
</address>
|
</address>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
Loading…
Reference in a new issue