Compare commits
100
Commits
a9e4ad55e9
...
11af286501
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11af286501 | ||
|
|
613d928149 | ||
|
|
7ecc7d33d4 | ||
|
|
9c8c18919f | ||
|
|
69fd525b8d | ||
|
|
1df783d0ff | ||
|
|
8a5afae48a | ||
|
|
15e0a2eb73 | ||
|
|
bf685c5702 | ||
|
|
d1fd6bc8cb | ||
|
|
6c8daab1bd | ||
|
|
3e1f06913f | ||
|
|
1e80c6dff8 | ||
|
|
6482fc9f59 | ||
|
|
31d6fc94fe | ||
|
|
8e89c71a34 | ||
|
|
1ad9376c4b | ||
|
|
3ddc5a8d50 | ||
|
|
6fd3aad81d | ||
|
|
520183c39f | ||
|
|
86078d8b90 | ||
|
|
caa27d6fdb | ||
|
|
24b1dd2ed8 | ||
|
|
c1f4096e11 | ||
|
|
db0ddfe009 | ||
|
|
9ea94e9963 | ||
|
|
c72ae603c1 | ||
|
|
5e8373fcac | ||
|
|
b48b3cbc35 | ||
|
|
515e378b4b | ||
|
|
583668f750 | ||
|
|
d109c0cd7d | ||
|
|
7949670061 | ||
|
|
9a7a072050 | ||
|
|
eb608414f6 | ||
|
|
0d38420a7a | ||
|
|
d81c148f68 | ||
|
|
89b4e39542 | ||
|
|
4ad771ac3d | ||
|
|
0e8ff0bea3 | ||
|
|
ce976a5f0b | ||
|
|
5e64b6ea41 | ||
|
|
49f7c12b7e | ||
|
|
9dcee79432 | ||
|
|
e78d62ce63 | ||
|
|
b3947ef7ea | ||
|
|
6abbf8f50c | ||
|
|
da9d5b8411 | ||
|
|
05dd162de5 | ||
|
|
fc862b3fa0 | ||
|
|
6f8e9f9496 | ||
|
|
d6d126a874 | ||
|
|
555b305405 | ||
|
|
58b13d3355 | ||
|
|
ff45f75731 | ||
|
|
d9c97fb982 | ||
|
|
3ab5702741 | ||
|
|
cc43d14199 | ||
|
|
9c243f0ddf | ||
|
|
4b39458bf1 | ||
|
|
47857e283e | ||
|
|
6017d575c8 | ||
|
|
536eeb804a | ||
|
|
5c5e28a09c | ||
|
|
4690b13d6b | ||
|
|
78eec1e021 | ||
|
|
244dc2d7c1 | ||
|
|
03bac4ab48 | ||
|
|
2d4ae6ca20 | ||
|
|
2a4f53eb54 | ||
|
|
d494856ca2 | ||
|
|
e8a5ff95d4 | ||
|
|
a282daa12f | ||
|
|
2ff4baeb18 | ||
|
|
96ae5c7dce | ||
|
|
d328fe1fd8 | ||
|
|
8ab8659889 | ||
|
|
5733c86d8f | ||
|
|
a1d94a7f99 | ||
|
|
c1366518ad | ||
|
|
bd8dc8326c | ||
|
|
d03cac106c | ||
|
|
1c69164a72 | ||
|
|
2e8e61309a | ||
|
|
67ef2b45d9 | ||
|
|
91a635a5ca | ||
|
|
088023cf15 | ||
|
|
0f48bc4904 | ||
|
|
e4aa69dc46 | ||
|
|
dc8ed52b58 | ||
|
|
648856dcd1 | ||
|
|
a4ed2c14be | ||
|
|
8e2e05e48e | ||
|
|
d77b80a94a | ||
|
|
bd59d8ab40 | ||
|
|
70282761d3 | ||
|
|
e02f6dbc20 | ||
|
|
cb387d88be | ||
|
|
b45552ade0 | ||
|
|
2b42ff5ce4 |
@@ -11,7 +11,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
- name: Add comment
|
- name: Add comment
|
||||||
uses: peter-evans/create-or-update-comment@853a4fc475ab347cfa392aa2ee451b4fe83e774e
|
uses: peter-evans/create-or-update-comment@2ef7be1cfae84b02f7b4e47ce45c1f93762d1d92
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
body: |
|
body: |
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- develop
|
- develop
|
||||||
paths: ['web/stories/**', 'web/components/**', 'web/.storybook/**'] # Trigger the action only when files change in the folders defined here
|
paths: [
|
||||||
|
'web/stories/**',
|
||||||
|
'web/components/**',
|
||||||
|
'web/.storybook/**',
|
||||||
|
'web/i18n/**',
|
||||||
|
] # Trigger the action only when files change in the folders defined here
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-deploy:
|
build-and-deploy:
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
name: Translation job
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run the workflow every hour
|
||||||
|
- cron: '0 * * * *'
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'web/i18n/en/translation.json'
|
||||||
|
- 'web/**/*.tsx'
|
||||||
|
- 'web/**/*.js'
|
||||||
|
- 'crowdin.yml'
|
||||||
|
- '.github/workflows/translations.yml'
|
||||||
|
- 'web/i18next-parser.config.mjs'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
generate-translations:
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./web
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }}
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Generate translation files
|
||||||
|
run: npm run translate
|
||||||
|
|
||||||
|
- name: Crowdin upload sources/download translations
|
||||||
|
uses: crowdin/github-action@v1
|
||||||
|
with:
|
||||||
|
upload_sources: true
|
||||||
|
download_translations: true
|
||||||
|
create_pull_request: true
|
||||||
|
pull_request_title: 'New Translations'
|
||||||
|
localization_branch_name: translations
|
||||||
|
pull_request_base_branch_name: 'develop'
|
||||||
|
commit_message: 'Updated translations'
|
||||||
|
config: crowdin.yml
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||||
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||||
|
|
||||||
|
# - name: Commit changes
|
||||||
|
# uses: EndBug/add-and-commit@v9
|
||||||
|
# with:
|
||||||
|
# author_name: Owncast
|
||||||
|
# author_email: owncast@owncast.online
|
||||||
|
# message: 'Commit updated translations'
|
||||||
|
# add: 'web/i18n/**'
|
||||||
|
# env:
|
||||||
|
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ import "path/filepath"
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// StaticVersionNumber is the version of Owncast that is used when it's not overwritten via build-time settings.
|
// StaticVersionNumber is the version of Owncast that is used when it's not overwritten via build-time settings.
|
||||||
StaticVersionNumber = "0.2.1" // Shown when you build from develop
|
StaticVersionNumber = "0.2.2" // Shown when you build from develop
|
||||||
// FfmpegSuggestedVersion is the version of ffmpeg we suggest.
|
// FfmpegSuggestedVersion is the version of ffmpeg we suggest.
|
||||||
FfmpegSuggestedVersion = "v4.1.5" // Requires the v
|
FfmpegSuggestedVersion = "v4.1.5" // Requires the v
|
||||||
// DataDirectory is the directory we save data to.
|
// DataDirectory is the directory we save data to.
|
||||||
|
|||||||
+6
-3
@@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
|
"github.com/owncast/owncast/webserver/handlers/generated"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defaults will hold default configuration values.
|
// Defaults will hold default configuration values.
|
||||||
@@ -25,7 +26,7 @@ type Defaults struct {
|
|||||||
WebServerIP string
|
WebServerIP string
|
||||||
Name string
|
Name string
|
||||||
AdminPassword string
|
AdminPassword string
|
||||||
StreamKeys []models.StreamKey
|
StreamKeys []generated.StreamKey
|
||||||
|
|
||||||
StreamVariants []models.StreamOutputVariant
|
StreamVariants []models.StreamOutputVariant
|
||||||
|
|
||||||
@@ -43,14 +44,16 @@ type Defaults struct {
|
|||||||
|
|
||||||
// GetDefaults will return default configuration values.
|
// GetDefaults will return default configuration values.
|
||||||
func GetDefaults() Defaults {
|
func GetDefaults() Defaults {
|
||||||
|
defaultStreamKey := "abc123"
|
||||||
|
defaultStreamKeyComment := "Default stream key"
|
||||||
return Defaults{
|
return Defaults{
|
||||||
Name: "New Owncast Server",
|
Name: "New Owncast Server",
|
||||||
Summary: "This is a new live video streaming server powered by Owncast.",
|
Summary: "This is a new live video streaming server powered by Owncast.",
|
||||||
ServerWelcomeMessage: "",
|
ServerWelcomeMessage: "",
|
||||||
Logo: "logo.svg",
|
Logo: "logo.svg",
|
||||||
AdminPassword: "abc123",
|
AdminPassword: "abc123",
|
||||||
StreamKeys: []models.StreamKey{
|
StreamKeys: []generated.StreamKey{
|
||||||
{Key: "abc123", Comment: "Default stream key"},
|
{Key: &defaultStreamKey, Comment: &defaultStreamKeyComment},
|
||||||
},
|
},
|
||||||
Tags: []string{
|
Tags: []string{
|
||||||
"owncast",
|
"owncast",
|
||||||
|
|||||||
+7
-3
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
|
"github.com/owncast/owncast/persistence/chatmessagerepository"
|
||||||
"github.com/owncast/owncast/persistence/configrepository"
|
"github.com/owncast/owncast/persistence/configrepository"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
@@ -103,7 +104,8 @@ func SendSystemMessage(text string, ephemeral bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !ephemeral {
|
if !ephemeral {
|
||||||
saveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
|
chatMessageRepository := chatmessagerepository.Get()
|
||||||
|
chatMessageRepository.SaveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -131,7 +133,8 @@ func SendFediverseAction(eventType string, userAccountName string, image *string
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
saveFederatedAction(message)
|
chatMessageRepository := chatmessagerepository.Get()
|
||||||
|
chatMessageRepository.SaveFederatedAction(message)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -152,7 +155,8 @@ func SendSystemAction(text string, ephemeral bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !ephemeral {
|
if !ephemeral {
|
||||||
saveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
|
chatMessageRepository := chatmessagerepository.Get()
|
||||||
|
chatMessageRepository.SaveEvent(message.ID, nil, message.Body, message.GetMessageType(), nil, message.Timestamp, nil, nil, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+3
-2
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/core/webhooks"
|
"github.com/owncast/owncast/core/webhooks"
|
||||||
|
"github.com/owncast/owncast/persistence/chatmessagerepository"
|
||||||
"github.com/owncast/owncast/persistence/configrepository"
|
"github.com/owncast/owncast/persistence/configrepository"
|
||||||
"github.com/owncast/owncast/persistence/userrepository"
|
"github.com/owncast/owncast/persistence/userrepository"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
@@ -172,8 +173,8 @@ func (s *Server) userMessageSent(eventData chatClientEvent) {
|
|||||||
// Send chat message sent webhook
|
// Send chat message sent webhook
|
||||||
webhooks.SendChatEvent(&event)
|
webhooks.SendChatEvent(&event)
|
||||||
chatMessagesSentCounter.Inc()
|
chatMessagesSentCounter.Inc()
|
||||||
|
chatMessageRepository := chatmessagerepository.Get()
|
||||||
SaveUserMessage(event)
|
chatMessageRepository.SaveUserMessage(event)
|
||||||
eventData.client.MessageCount++
|
eventData.client.MessageCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ import (
|
|||||||
|
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/core/webhooks"
|
"github.com/owncast/owncast/core/webhooks"
|
||||||
|
"github.com/owncast/owncast/persistence/chatmessagerepository"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetMessagesVisibility will set the visibility of multiple messages by ID.
|
// SetMessagesVisibility will set the visibility of multiple messages by ID.
|
||||||
func SetMessagesVisibility(messageIDs []string, visibility bool) error {
|
func SetMessagesVisibility(messageIDs []string, visibility bool) error {
|
||||||
// Save new message visibility
|
// Save new message visibility
|
||||||
if err := saveMessageVisibility(messageIDs, visibility); err != nil {
|
chatMessageRepository := chatmessagerepository.Get()
|
||||||
|
if err := chatMessageRepository.SetMessageVisibilityForMessageIDs(messageIDs, visibility); err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
package chat
|
package chat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/models"
|
|
||||||
"github.com/owncast/owncast/persistence/authrepository"
|
"github.com/owncast/owncast/persistence/authrepository"
|
||||||
"github.com/owncast/owncast/persistence/tables"
|
"github.com/owncast/owncast/persistence/tables"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _datastore *data.Datastore
|
var _datastore *data.Datastore
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxBacklogHours = 2 // Keep backlog max hours worth of messages
|
maxBacklogHours = 2 // Keep backlog max hours worth of messages
|
||||||
maxBacklogNumber = 50 // Return max number of messages in history request
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupPersistence() {
|
func setupPersistence() {
|
||||||
@@ -37,456 +29,3 @@ func setupPersistence() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveUserMessage will save a single chat event to the messages database.
|
|
||||||
func SaveUserMessage(event events.UserMessageEvent) {
|
|
||||||
saveEvent(event.ID, &event.User.ID, event.Body, event.Type, event.HiddenAt, event.Timestamp, nil, nil, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveFederatedAction(event events.FediverseEngagementEvent) {
|
|
||||||
saveEvent(event.ID, nil, event.Body, event.Type, nil, event.Timestamp, event.Image, &event.Link, &event.UserAccountName, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint: unparam
|
|
||||||
func saveEvent(id string, userID *string, body string, eventType string, hidden *time.Time, timestamp time.Time, image *string, link *string, title *string, subtitle *string) {
|
|
||||||
defer func() {
|
|
||||||
_historyCache = nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
tx, err := _datastore.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error saving", eventType, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer tx.Rollback() // nolint
|
|
||||||
|
|
||||||
stmt, err := tx.Prepare("INSERT INTO messages(id, user_id, body, eventType, hidden_at, timestamp, image, link, title, subtitle) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error saving", eventType, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
if _, err = stmt.Exec(id, userID, body, eventType, hidden, timestamp, image, link, title, subtitle); err != nil {
|
|
||||||
log.Errorln("error saving", eventType, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = tx.Commit(); err != nil {
|
|
||||||
log.Errorln("error saving", eventType, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeUserMessageEventFromRowData(row rowData) events.UserMessageEvent {
|
|
||||||
scopes := ""
|
|
||||||
if row.userScopes != nil {
|
|
||||||
scopes = *row.userScopes
|
|
||||||
}
|
|
||||||
|
|
||||||
previousUsernames := []string{}
|
|
||||||
if row.previousUsernames != nil {
|
|
||||||
previousUsernames = strings.Split(*row.previousUsernames, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
displayName := ""
|
|
||||||
if row.userDisplayName != nil {
|
|
||||||
displayName = *row.userDisplayName
|
|
||||||
}
|
|
||||||
|
|
||||||
displayColor := 0
|
|
||||||
if row.userDisplayColor != nil {
|
|
||||||
displayColor = *row.userDisplayColor
|
|
||||||
}
|
|
||||||
|
|
||||||
createdAt := time.Time{}
|
|
||||||
if row.userCreatedAt != nil {
|
|
||||||
createdAt = *row.userCreatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
isBot := (row.userType != nil && *row.userType == "API")
|
|
||||||
scopeSlice := strings.Split(scopes, ",")
|
|
||||||
|
|
||||||
u := models.User{
|
|
||||||
ID: *row.userID,
|
|
||||||
DisplayName: displayName,
|
|
||||||
DisplayColor: displayColor,
|
|
||||||
CreatedAt: createdAt,
|
|
||||||
DisabledAt: row.userDisabledAt,
|
|
||||||
NameChangedAt: row.userNameChangedAt,
|
|
||||||
PreviousNames: previousUsernames,
|
|
||||||
AuthenticatedAt: row.userAuthenticatedAt,
|
|
||||||
Authenticated: row.userAuthenticatedAt != nil,
|
|
||||||
Scopes: scopeSlice,
|
|
||||||
IsBot: isBot,
|
|
||||||
}
|
|
||||||
|
|
||||||
message := events.UserMessageEvent{
|
|
||||||
Event: events.Event{
|
|
||||||
Type: row.eventType,
|
|
||||||
ID: row.id,
|
|
||||||
Timestamp: row.timestamp,
|
|
||||||
},
|
|
||||||
UserEvent: events.UserEvent{
|
|
||||||
User: &u,
|
|
||||||
HiddenAt: row.hiddenAt,
|
|
||||||
},
|
|
||||||
MessageEvent: events.MessageEvent{
|
|
||||||
Body: row.body,
|
|
||||||
RawBody: row.body,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeSystemMessageChatEventFromRowData(row rowData) events.SystemMessageEvent {
|
|
||||||
message := events.SystemMessageEvent{
|
|
||||||
Event: events.Event{
|
|
||||||
Type: row.eventType,
|
|
||||||
ID: row.id,
|
|
||||||
Timestamp: row.timestamp,
|
|
||||||
},
|
|
||||||
MessageEvent: events.MessageEvent{
|
|
||||||
Body: row.body,
|
|
||||||
RawBody: row.body,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeActionMessageChatEventFromRowData(row rowData) events.ActionEvent {
|
|
||||||
message := events.ActionEvent{
|
|
||||||
Event: events.Event{
|
|
||||||
Type: row.eventType,
|
|
||||||
ID: row.id,
|
|
||||||
Timestamp: row.timestamp,
|
|
||||||
},
|
|
||||||
MessageEvent: events.MessageEvent{
|
|
||||||
Body: row.body,
|
|
||||||
RawBody: row.body,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeFederatedActionChatEventFromRowData(row rowData) events.FediverseEngagementEvent {
|
|
||||||
message := events.FediverseEngagementEvent{
|
|
||||||
Event: events.Event{
|
|
||||||
Type: row.eventType,
|
|
||||||
ID: row.id,
|
|
||||||
Timestamp: row.timestamp,
|
|
||||||
},
|
|
||||||
MessageEvent: events.MessageEvent{
|
|
||||||
Body: row.body,
|
|
||||||
RawBody: row.body,
|
|
||||||
},
|
|
||||||
Image: row.image,
|
|
||||||
Link: *row.link,
|
|
||||||
UserAccountName: *row.title,
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
type rowData struct {
|
|
||||||
timestamp time.Time
|
|
||||||
image *string
|
|
||||||
previousUsernames *string
|
|
||||||
|
|
||||||
userDisplayName *string
|
|
||||||
userDisplayColor *int
|
|
||||||
userID *string
|
|
||||||
title *string
|
|
||||||
subtitle *string
|
|
||||||
link *string
|
|
||||||
|
|
||||||
userType *string
|
|
||||||
userScopes *string
|
|
||||||
hiddenAt *time.Time
|
|
||||||
userCreatedAt *time.Time
|
|
||||||
userDisabledAt *time.Time
|
|
||||||
userAuthenticatedAt *time.Time
|
|
||||||
userNameChangedAt *time.Time
|
|
||||||
body string
|
|
||||||
eventType models.EventType
|
|
||||||
id string
|
|
||||||
}
|
|
||||||
|
|
||||||
func getChat(rows *sql.Rows) ([]interface{}, error) {
|
|
||||||
history := make([]interface{}, 0)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
row := rowData{}
|
|
||||||
|
|
||||||
// Convert a database row into a chat event
|
|
||||||
if err := rows.Scan(
|
|
||||||
&row.id,
|
|
||||||
&row.userID,
|
|
||||||
&row.body,
|
|
||||||
&row.title,
|
|
||||||
&row.subtitle,
|
|
||||||
&row.image,
|
|
||||||
&row.link,
|
|
||||||
&row.eventType,
|
|
||||||
&row.hiddenAt,
|
|
||||||
&row.timestamp,
|
|
||||||
&row.userDisplayName,
|
|
||||||
&row.userDisplayColor,
|
|
||||||
&row.userCreatedAt,
|
|
||||||
&row.userDisabledAt,
|
|
||||||
&row.previousUsernames,
|
|
||||||
&row.userNameChangedAt,
|
|
||||||
&row.userAuthenticatedAt,
|
|
||||||
&row.userScopes,
|
|
||||||
&row.userType,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var message interface{}
|
|
||||||
|
|
||||||
switch row.eventType {
|
|
||||||
case events.MessageSent:
|
|
||||||
message = makeUserMessageEventFromRowData(row)
|
|
||||||
case events.SystemMessageSent:
|
|
||||||
message = makeSystemMessageChatEventFromRowData(row)
|
|
||||||
case events.ChatActionSent:
|
|
||||||
message = makeActionMessageChatEventFromRowData(row)
|
|
||||||
case events.FediverseEngagementFollow:
|
|
||||||
message = makeFederatedActionChatEventFromRowData(row)
|
|
||||||
case events.FediverseEngagementLike:
|
|
||||||
message = makeFederatedActionChatEventFromRowData(row)
|
|
||||||
case events.FediverseEngagementRepost:
|
|
||||||
message = makeFederatedActionChatEventFromRowData(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
history = append(history, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return history, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _historyCache *[]interface{}
|
|
||||||
|
|
||||||
// GetChatModerationHistory will return all the chat messages suitable for moderation purposes.
|
|
||||||
func GetChatModerationHistory() []interface{} {
|
|
||||||
if _historyCache != nil {
|
|
||||||
return *_historyCache
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := _datastore.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error fetching chat moderation history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer tx.Rollback() // nolint
|
|
||||||
|
|
||||||
// Get all messages regardless of visibility
|
|
||||||
query := "SELECT messages.id, user_id, body, title, subtitle, image, link, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, authenticated_at, scopes, type FROM messages INNER JOIN users ON messages.user_id = users.id ORDER BY timestamp DESC"
|
|
||||||
stmt, err := tx.Prepare(query)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error fetching chat moderation history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := stmt.Query()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error fetching chat moderation history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer stmt.Close()
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
result, err := getChat(rows)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(err)
|
|
||||||
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_historyCache = &result
|
|
||||||
|
|
||||||
if err = tx.Commit(); err != nil {
|
|
||||||
log.Errorln("error fetching chat moderation history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetChatHistory will return all the chat messages suitable for returning as user-facing chat history.
|
|
||||||
func GetChatHistory() []interface{} {
|
|
||||||
tx, err := _datastore.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error fetching chat history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer tx.Rollback() // nolint
|
|
||||||
|
|
||||||
// Get all visible messages
|
|
||||||
query := "SELECT messages.id, messages.user_id, messages.body, messages.title, messages.subtitle, messages.image, messages.link, messages.eventType, messages.hidden_at, messages.timestamp, users.display_name, users.display_color, users.created_at, users.disabled_at, users.previous_names, users.namechanged_at, users.authenticated_at, users.scopes, users.type FROM users JOIN messages ON users.id = messages.user_id WHERE hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT ?"
|
|
||||||
|
|
||||||
stmt, err := tx.Prepare(query)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error fetching chat history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := stmt.Query(maxBacklogNumber)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error fetching chat history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer stmt.Close()
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
m, err := getChat(rows)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(err)
|
|
||||||
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = tx.Commit(); err != nil {
|
|
||||||
log.Errorln("error fetching chat history", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invert order of messages
|
|
||||||
for i, j := 0, len(m)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
m[i], m[j] = m[j], m[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMessagesFromUser returns chat messages that were sent by a specific user.
|
|
||||||
func GetMessagesFromUser(userID string) ([]events.UserMessageEvent, error) {
|
|
||||||
query, err := _datastore.GetQueries().GetMessagesFromUser(context.Background(), sql.NullString{String: userID, Valid: true})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
results := make([]events.UserMessageEvent, len(query))
|
|
||||||
for i, row := range query {
|
|
||||||
results[i] = events.UserMessageEvent{
|
|
||||||
Event: events.Event{
|
|
||||||
Timestamp: row.Timestamp.Time,
|
|
||||||
ID: row.ID,
|
|
||||||
},
|
|
||||||
MessageEvent: events.MessageEvent{
|
|
||||||
Body: row.Body.String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMessageVisibilityForUserID will bulk change the visibility of messages for a user
|
|
||||||
// and then send out visibility changed events to chat clients.
|
|
||||||
func SetMessageVisibilityForUserID(userID string, visible bool) error {
|
|
||||||
defer func() {
|
|
||||||
_historyCache = nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
tx, err := _datastore.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error while setting message visibility", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer tx.Rollback() // nolint
|
|
||||||
query := "SELECT messages.id, user_id, body, title, subtitle, image, link, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, authenticated_at, scopes, type FROM messages INNER JOIN users ON messages.user_id = users.id WHERE user_id IS ?"
|
|
||||||
|
|
||||||
stmt, err := tx.Prepare(query)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error while setting message visibility", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := stmt.Query(userID)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error while setting message visibility", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
defer stmt.Close()
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
// Get a list of IDs to send to the connected clients to hide
|
|
||||||
ids := make([]string, 0)
|
|
||||||
|
|
||||||
messages, err := getChat(rows)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln(err)
|
|
||||||
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(messages) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, message := range messages {
|
|
||||||
ids = append(ids, message.(events.UserMessageEvent).ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = tx.Commit(); err != nil {
|
|
||||||
log.Errorln("error while setting message visibility ", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the clients to hide/show these messages.
|
|
||||||
return SetMessagesVisibility(ids, visible)
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveMessageVisibility(messageIDs []string, visible bool) error {
|
|
||||||
defer func() {
|
|
||||||
_historyCache = nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
_datastore.DbLock.Lock()
|
|
||||||
defer _datastore.DbLock.Unlock()
|
|
||||||
|
|
||||||
tx, err := _datastore.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:gosec
|
|
||||||
stmt, err := tx.Prepare("UPDATE messages SET hidden_at=? WHERE id IN (?" + strings.Repeat(",?", len(messageIDs)-1) + ")")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
var hiddenAt *time.Time
|
|
||||||
if !visible {
|
|
||||||
now := time.Now()
|
|
||||||
hiddenAt = &now
|
|
||||||
} else {
|
|
||||||
hiddenAt = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
args := make([]interface{}, len(messageIDs)+1)
|
|
||||||
args[0] = hiddenAt
|
|
||||||
for i, id := range messageIDs {
|
|
||||||
args[i+1] = id
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = stmt.Exec(args...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = tx.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
+2
-1
@@ -75,7 +75,8 @@ func SetupPersistence(file string) error {
|
|||||||
_, _ = db.Exec("pragma temp_store = memory")
|
_, _ = db.Exec("pragma temp_store = memory")
|
||||||
_, _ = db.Exec("pragma wal_checkpoint(full)")
|
_, _ = db.Exec("pragma wal_checkpoint(full)")
|
||||||
|
|
||||||
createWebhooksTable()
|
tables.CreateConfigTable(db)
|
||||||
|
tables.CreateWebhooksTable(db)
|
||||||
tables.CreateUsersTable(db)
|
tables.CreateUsersTable(db)
|
||||||
tables.CreateAccessTokenTable(db)
|
tables.CreateAccessTokenTable(db)
|
||||||
|
|
||||||
|
|||||||
@@ -107,19 +107,11 @@ func (ds *Datastore) Save(e models.ConfigEntry) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup will create the datastore table and perform initial initialization.
|
// Setup will perform initial initialization.
|
||||||
func (ds *Datastore) Setup() {
|
func (ds *Datastore) Setup() {
|
||||||
ds.cache = make(map[string][]byte)
|
ds.cache = make(map[string][]byte)
|
||||||
ds.DB = GetDatabase()
|
ds.DB = GetDatabase()
|
||||||
ds.DbLock = &sync.Mutex{}
|
ds.DbLock = &sync.Mutex{}
|
||||||
|
|
||||||
createTableSQL := `CREATE TABLE IF NOT EXISTS datastore (
|
|
||||||
"key" string NOT NULL PRIMARY KEY,
|
|
||||||
"value" BLOB,
|
|
||||||
"timestamp" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL
|
|
||||||
);`
|
|
||||||
|
|
||||||
ds.MustExec(createTableSQL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset will delete all config entries in the datastore and start over.
|
// Reset will delete all config entries in the datastore and start over.
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package data
|
|
||||||
|
|
||||||
// GetMessagesCount will return the number of messages in the database.
|
|
||||||
func GetMessagesCount() int64 {
|
|
||||||
query := `SELECT COUNT(*) FROM messages`
|
|
||||||
rows, err := _db.Query(query)
|
|
||||||
if err != nil || rows.Err() != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var count int64
|
|
||||||
for rows.Next() {
|
|
||||||
if err := rows.Scan(&count); err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
+3
-2
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
"github.com/owncast/owncast/persistence/configrepository"
|
"github.com/owncast/owncast/persistence/configrepository"
|
||||||
|
"github.com/owncast/owncast/webserver/handlers/generated"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _hasInboundRTMPConnection = false
|
var _hasInboundRTMPConnection = false
|
||||||
@@ -87,11 +88,11 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) {
|
|||||||
|
|
||||||
// If a stream key override was specified then use that instead.
|
// If a stream key override was specified then use that instead.
|
||||||
if config.TemporaryStreamKey != "" {
|
if config.TemporaryStreamKey != "" {
|
||||||
validStreamingKeys = []models.StreamKey{{Key: config.TemporaryStreamKey}}
|
validStreamingKeys = []generated.StreamKey{{Key: &config.TemporaryStreamKey}}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, key := range validStreamingKeys {
|
for _, key := range validStreamingKeys {
|
||||||
if secretMatch(key.Key, c.URL.Path) {
|
if key.Key != nil && secretMatch(*key.Key, c.URL.Path) {
|
||||||
accessGranted = true
|
accessGranted = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,6 @@ func (t *Transcoder) getString() string {
|
|||||||
"-max_muxing_queue_size", "400", // Workaround for Too many packets error: https://trac.ffmpeg.org/ticket/6375?cversion=0
|
"-max_muxing_queue_size", "400", // Workaround for Too many packets error: https://trac.ffmpeg.org/ticket/6375?cversion=0
|
||||||
|
|
||||||
"-method PUT", // HLS results sent back to us will be over PUTs
|
"-method PUT", // HLS results sent back to us will be over PUTs
|
||||||
"-http_persistent", "1", // Ensures persistent HTTP connections
|
|
||||||
|
|
||||||
localListenerAddress + "/%v/stream.m3u8", // Send HLS playlists back to us over HTTP
|
localListenerAddress + "/%v/stream.m3u8", // Send HLS playlists back to us over HTTP
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegNvencCommand(t *testing.T) {
|
|||||||
cmd := transcoder.getString()
|
cmd := transcoder.getString()
|
||||||
|
|
||||||
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
||||||
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel cuda -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_nvenc -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -tune:v:0 ll -map a:0? -c:a:0 copy -preset p3 -map v:0 -c:v:1 h264_nvenc -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -tune:v:1 ll -map a:0? -c:a:1 copy -preset p5 -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset p1 -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdoieGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
|
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel cuda -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_nvenc -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -tune:v:0 ll -map a:0? -c:a:0 copy -preset p3 -map v:0 -c:v:1 h264_nvenc -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -tune:v:1 ll -map a:0? -c:a:1 copy -preset p5 -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset p1 -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdoieGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegOmxCommand(t *testing.T) {
|
|||||||
cmd := transcoder.getString()
|
cmd := transcoder.getString()
|
||||||
|
|
||||||
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
||||||
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_omx -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_omx -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
|
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_omx -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_omx -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegQuicksyncCommand(t *testing.T) {
|
|||||||
cmd := transcoder.getString()
|
cmd := transcoder.getString()
|
||||||
|
|
||||||
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
||||||
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -init_hw_device qsv=hw -filter_hw_device hw -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_qsv -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=qsv" -preset medium -map v:0 -c:v:1 h264_qsv -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=qsv" -preset veryslow -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset veryfast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt qsv -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
|
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -init_hw_device qsv=hw -filter_hw_device hw -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_qsv -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=qsv" -preset medium -map v:0 -c:v:1 h264_qsv -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=qsv" -preset veryslow -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset veryfast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt qsv -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegVaapiCommand(t *testing.T) {
|
|||||||
cmd := transcoder.getString()
|
cmd := transcoder.getString()
|
||||||
|
|
||||||
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
||||||
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device /dev/dri/renderD128 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_vaapi -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=vaapi" -preset veryfast -map v:0 -c:v:1 h264_vaapi -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=vaapi" -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt vaapi -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
|
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device /dev/dri/renderD128 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_vaapi -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=vaapi" -preset veryfast -map v:0 -c:v:1 h264_vaapi -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=vaapi" -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt vaapi -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegVideoToolboxCommand(t *testing.T) {
|
|||||||
cmd := transcoder.getString()
|
cmd := transcoder.getString()
|
||||||
|
|
||||||
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
||||||
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_videotoolbox -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -realtime true -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_videotoolbox -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt nv12 -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
|
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_videotoolbox -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -realtime true -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_videotoolbox -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt nv12 -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegx264Command(t *testing.T) {
|
|||||||
cmd := transcoder.getString()
|
cmd := transcoder.getString()
|
||||||
|
|
||||||
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
|
||||||
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 libx264 -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -x264-params:v:0 "scenecut=0:open_gop=0" -bufsize:v:0 1088k -profile:v:0 high -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 libx264 -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -x264-params:v:1 "scenecut=0:open_gop=0" -bufsize:v:1 3572k -profile:v:1 high -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT -http_persistent 1 http://127.0.0.1:8123/%v/stream.m3u8`
|
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 libx264 -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -x264-params:v:0 "scenecut=0:open_gop=0" -bufsize:v:0 1088k -profile:v:0 high -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 libx264 -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -x264-params:v:1 "scenecut=0:open_gop=0" -bufsize:v:1 3572k -profile:v:1 high -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/core/data"
|
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
|
"github.com/owncast/owncast/persistence/webhookrepository"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebhookEvent represents an event sent as a webhook.
|
// WebhookEvent represents an event sent as a webhook.
|
||||||
@@ -31,7 +31,8 @@ func SendEventToWebhooks(payload WebhookEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendEventToWebhooks(payload WebhookEvent, wg *sync.WaitGroup) {
|
func sendEventToWebhooks(payload WebhookEvent, wg *sync.WaitGroup) {
|
||||||
webhooks := data.GetWebhooksForEvent(payload.Type)
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
webhooks := webhooksRepo.GetWebhooksForEvent(payload.Type)
|
||||||
|
|
||||||
for _, webhook := range webhooks {
|
for _, webhook := range webhooks {
|
||||||
// Use wg to track the number of notifications to be sent.
|
// Use wg to track the number of notifications to be sent.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
|
"github.com/owncast/owncast/persistence/webhookrepository"
|
||||||
jsonpatch "gopkg.in/evanphx/json-patch.v5"
|
jsonpatch "gopkg.in/evanphx/json-patch.v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,12 +63,14 @@ func TestPublicSend(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
hook, err := data.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
|
||||||
|
hook, err := webhooksRepo.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := data.DeleteWebhook(hook); err != nil {
|
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -107,13 +110,15 @@ func TestRouting(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
|
||||||
for _, eventType := range eventTypes {
|
for _, eventType := range eventTypes {
|
||||||
hook, err := data.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
|
hook, err := webhooksRepo.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := data.DeleteWebhook(hook); err != nil {
|
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -148,13 +153,15 @@ func TestMultiple(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
|
||||||
for i := 0; i < times; i++ {
|
for i := 0; i < times; i++ {
|
||||||
hook, err := data.InsertWebhook(fmt.Sprintf("%v/%v", svr.URL, i), []models.EventType{models.MessageSent})
|
hook, err := webhooksRepo.InsertWebhook(fmt.Sprintf("%v/%v", svr.URL, i), []models.EventType{models.MessageSent})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := data.DeleteWebhook(hook); err != nil {
|
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -186,14 +193,16 @@ func TestTimestamps(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
|
||||||
for i, eventType := range eventTypes {
|
for i, eventType := range eventTypes {
|
||||||
hook, err := data.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
|
hook, err := webhooksRepo.InsertWebhook(svr.URL+"/"+eventType, []models.EventType{eventType})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
handlerIds[i] = hook
|
handlerIds[i] = hook
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := data.DeleteWebhook(hook); err != nil {
|
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -209,7 +218,7 @@ func TestTimestamps(t *testing.T) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
hooks, err := data.GetWebhooks()
|
hooks, err := webhooksRepo.GetWebhooks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -285,12 +294,14 @@ func TestParallel(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
hook, err := data.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
|
||||||
|
hook, err := webhooksRepo.InsertWebhook(svr.URL, []models.EventType{models.MessageSent})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := data.DeleteWebhook(hook); err != nil {
|
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -320,13 +331,15 @@ func checkPayload(t *testing.T, eventType models.EventType, send func(), expecte
|
|||||||
}))
|
}))
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
|
||||||
// Subscribe to the webhook.
|
// Subscribe to the webhook.
|
||||||
hook, err := data.InsertWebhook(svr.URL, []models.EventType{eventType})
|
hook, err := webhooksRepo.InsertWebhook(svr.URL, []models.EventType{eventType})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := data.DeleteWebhook(hook); err != nil {
|
if err := webhooksRepo.DeleteWebhook(hook); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/owncast/owncast/core/data"
|
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
|
"github.com/owncast/owncast/persistence/webhookrepository"
|
||||||
)
|
)
|
||||||
|
|
||||||
// webhookWorkerPoolSize defines the number of concurrent HTTP webhook requests.
|
// webhookWorkerPoolSize defines the number of concurrent HTTP webhook requests.
|
||||||
@@ -87,7 +87,8 @@ func sendWebhook(job Job) error {
|
|||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if err := data.SetWebhookAsUsed(job.webhook); err != nil {
|
webhooksRepo := webhookrepository.Get()
|
||||||
|
if err := webhooksRepo.SetWebhookAsUsed(job.webhook); err != nil {
|
||||||
log.Warnln(err)
|
log.Warnln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
project_id_env: CROWDIN_PROJECT_ID
|
||||||
|
api_token_env: CROWDIN_PERSONAL_TOKEN
|
||||||
|
pull_request_title: Translations update
|
||||||
|
pull_request_labels:
|
||||||
|
- crowdin
|
||||||
|
- i18n
|
||||||
|
- translation
|
||||||
|
commit_message: 'Updated translations for %language%'
|
||||||
|
files:
|
||||||
|
- source: /web/i18n/en/translation.json
|
||||||
|
translation: /web/i18n/%two_letters_code%/translation.json
|
||||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -9,7 +9,7 @@ require (
|
|||||||
github.com/SherClockHolmes/webpush-go v1.4.0
|
github.com/SherClockHolmes/webpush-go v1.4.0
|
||||||
github.com/TwiN/go-away v1.6.14
|
github.com/TwiN/go-away v1.6.14
|
||||||
github.com/andybalholm/cascadia v1.3.3
|
github.com/andybalholm/cascadia v1.3.3
|
||||||
github.com/aws/aws-sdk-go v1.55.5
|
github.com/aws/aws-sdk-go v1.55.6
|
||||||
github.com/go-chi/chi/v5 v5.2.0
|
github.com/go-chi/chi/v5 v5.2.0
|
||||||
github.com/go-fed/activity v1.0.1-0.20220119073622-b14b50eecad0
|
github.com/go-fed/activity v1.0.1-0.20220119073622-b14b50eecad0
|
||||||
github.com/go-fed/httpsig v1.1.0
|
github.com/go-fed/httpsig v1.1.0
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7D
|
|||||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
|
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
|
||||||
|
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
|||||||
+3
-2
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/nakabonne/tstorage"
|
"github.com/nakabonne/tstorage"
|
||||||
"github.com/owncast/owncast/core"
|
"github.com/owncast/owncast/core"
|
||||||
"github.com/owncast/owncast/core/chat"
|
"github.com/owncast/owncast/core/chat"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/persistence/chatmessagerepository"
|
||||||
"github.com/owncast/owncast/persistence/userrepository"
|
"github.com/owncast/owncast/persistence/userrepository"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -56,7 +56,8 @@ func collectChatClientCount() {
|
|||||||
activeChatClientCount.Set(float64(count))
|
activeChatClientCount.Set(float64(count))
|
||||||
|
|
||||||
// Total message count
|
// Total message count
|
||||||
cmc := data.GetMessagesCount()
|
chatMessageRepository := chatmessagerepository.Get()
|
||||||
|
cmc := chatMessageRepository.GetMessagesCount()
|
||||||
// Insert message count into Prometheus collector.
|
// Insert message count into Prometheus collector.
|
||||||
currentChatMessageCount.Set(float64(cmc))
|
currentChatMessageCount.Set(float64(cmc))
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
// StreamKey represents a single stream key.
|
|
||||||
type StreamKey struct {
|
|
||||||
Key string `json:"key"`
|
|
||||||
Comment string `json:"comment"`
|
|
||||||
}
|
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
openapi: 3.1.0
|
openapi: 3.1.0
|
||||||
|
|
||||||
info:
|
info:
|
||||||
version: 0.2.1
|
version: 0.2.2
|
||||||
title: Owncast APIs
|
title: Owncast APIs
|
||||||
description: |-
|
description: |-
|
||||||
Internal
|
Internal
|
||||||
|
|||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "owncast",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,523 @@
|
|||||||
|
package chatmessagerepository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
|
"github.com/owncast/owncast/core/data"
|
||||||
|
"github.com/owncast/owncast/models"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxBacklogNumber = 50 // Return max number of messages in history request
|
||||||
|
|
||||||
|
type ChatMessageRepository interface {
|
||||||
|
SaveUserMessage(event events.UserMessageEvent)
|
||||||
|
SaveFederatedAction(event events.FediverseEngagementEvent)
|
||||||
|
SaveEvent(id string, userID *string, body string, eventType string, hidden *time.Time, timestamp time.Time, image *string, link *string, title *string, subtitle *string)
|
||||||
|
GetChatModerationHistory() []interface{}
|
||||||
|
GetChatHistory() []interface{}
|
||||||
|
GetMessagesFromUser(userID string) ([]events.UserMessageEvent, error)
|
||||||
|
GetMessageIdsForUserID(userID string) ([]string, error)
|
||||||
|
SetMessageVisibilityForMessageIDs(messageIDs []string, visible bool) error
|
||||||
|
GetMessagesCount() int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SqlChatMessageRepository struct {
|
||||||
|
datastore *data.Datastore
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This is temporary during the transition period.
|
||||||
|
var temporaryGlobalInstance ChatMessageRepository
|
||||||
|
|
||||||
|
// Get will return the user repository.
|
||||||
|
func Get() ChatMessageRepository {
|
||||||
|
if temporaryGlobalInstance == nil {
|
||||||
|
i := New(data.GetDatastore())
|
||||||
|
temporaryGlobalInstance = i
|
||||||
|
}
|
||||||
|
return temporaryGlobalInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
// New will create a new instance of the UserRepository.
|
||||||
|
func New(datastore *data.Datastore) ChatMessageRepository {
|
||||||
|
r := SqlChatMessageRepository{
|
||||||
|
datastore: datastore,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveUserMessage will save a single chat event to the messages database.
|
||||||
|
func (r *SqlChatMessageRepository) SaveUserMessage(event events.UserMessageEvent) {
|
||||||
|
r.SaveEvent(event.ID, &event.User.ID, event.Body, event.Type, event.HiddenAt, event.Timestamp, nil, nil, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SqlChatMessageRepository) SaveFederatedAction(event events.FediverseEngagementEvent) {
|
||||||
|
r.SaveEvent(event.ID, nil, event.Body, event.Type, nil, event.Timestamp, event.Image, &event.Link, &event.UserAccountName, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: unparam
|
||||||
|
func (r *SqlChatMessageRepository) SaveEvent(id string, userID *string, body string, eventType string, hidden *time.Time, timestamp time.Time, image *string, link *string, title *string, subtitle *string) {
|
||||||
|
defer func() {
|
||||||
|
_historyCache = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
tx, err := r.datastore.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error saving", eventType, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer tx.Rollback() // nolint
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare("INSERT INTO messages(id, user_id, body, eventType, hidden_at, timestamp, image, link, title, subtitle) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error saving", eventType, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
if _, err = stmt.Exec(id, userID, body, eventType, hidden, timestamp, image, link, title, subtitle); err != nil {
|
||||||
|
log.Errorln("error saving", eventType, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
log.Errorln("error saving", eventType, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUserMessageEventFromRowData(row rowData) events.UserMessageEvent {
|
||||||
|
scopes := ""
|
||||||
|
if row.userScopes != nil {
|
||||||
|
scopes = *row.userScopes
|
||||||
|
}
|
||||||
|
|
||||||
|
previousUsernames := []string{}
|
||||||
|
if row.previousUsernames != nil {
|
||||||
|
previousUsernames = strings.Split(*row.previousUsernames, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
displayName := ""
|
||||||
|
if row.userDisplayName != nil {
|
||||||
|
displayName = *row.userDisplayName
|
||||||
|
}
|
||||||
|
|
||||||
|
displayColor := 0
|
||||||
|
if row.userDisplayColor != nil {
|
||||||
|
displayColor = *row.userDisplayColor
|
||||||
|
}
|
||||||
|
|
||||||
|
createdAt := time.Time{}
|
||||||
|
if row.userCreatedAt != nil {
|
||||||
|
createdAt = *row.userCreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
isBot := (row.userType != nil && *row.userType == "API")
|
||||||
|
scopeSlice := strings.Split(scopes, ",")
|
||||||
|
|
||||||
|
u := models.User{
|
||||||
|
ID: *row.userID,
|
||||||
|
DisplayName: displayName,
|
||||||
|
DisplayColor: displayColor,
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
DisabledAt: row.userDisabledAt,
|
||||||
|
NameChangedAt: row.userNameChangedAt,
|
||||||
|
PreviousNames: previousUsernames,
|
||||||
|
AuthenticatedAt: row.userAuthenticatedAt,
|
||||||
|
Authenticated: row.userAuthenticatedAt != nil,
|
||||||
|
Scopes: scopeSlice,
|
||||||
|
IsBot: isBot,
|
||||||
|
}
|
||||||
|
|
||||||
|
message := events.UserMessageEvent{
|
||||||
|
Event: events.Event{
|
||||||
|
Type: row.eventType,
|
||||||
|
ID: row.id,
|
||||||
|
Timestamp: row.timestamp,
|
||||||
|
},
|
||||||
|
UserEvent: events.UserEvent{
|
||||||
|
User: &u,
|
||||||
|
HiddenAt: row.hiddenAt,
|
||||||
|
},
|
||||||
|
MessageEvent: events.MessageEvent{
|
||||||
|
Body: row.body,
|
||||||
|
RawBody: row.body,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSystemMessageChatEventFromRowData(row rowData) events.SystemMessageEvent {
|
||||||
|
message := events.SystemMessageEvent{
|
||||||
|
Event: events.Event{
|
||||||
|
Type: row.eventType,
|
||||||
|
ID: row.id,
|
||||||
|
Timestamp: row.timestamp,
|
||||||
|
},
|
||||||
|
MessageEvent: events.MessageEvent{
|
||||||
|
Body: row.body,
|
||||||
|
RawBody: row.body,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeActionMessageChatEventFromRowData(row rowData) events.ActionEvent {
|
||||||
|
message := events.ActionEvent{
|
||||||
|
Event: events.Event{
|
||||||
|
Type: row.eventType,
|
||||||
|
ID: row.id,
|
||||||
|
Timestamp: row.timestamp,
|
||||||
|
},
|
||||||
|
MessageEvent: events.MessageEvent{
|
||||||
|
Body: row.body,
|
||||||
|
RawBody: row.body,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFederatedActionChatEventFromRowData(row rowData) events.FediverseEngagementEvent {
|
||||||
|
message := events.FediverseEngagementEvent{
|
||||||
|
Event: events.Event{
|
||||||
|
Type: row.eventType,
|
||||||
|
ID: row.id,
|
||||||
|
Timestamp: row.timestamp,
|
||||||
|
},
|
||||||
|
MessageEvent: events.MessageEvent{
|
||||||
|
Body: row.body,
|
||||||
|
RawBody: row.body,
|
||||||
|
},
|
||||||
|
Image: row.image,
|
||||||
|
Link: *row.link,
|
||||||
|
UserAccountName: *row.title,
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
type rowData struct {
|
||||||
|
timestamp time.Time
|
||||||
|
image *string
|
||||||
|
previousUsernames *string
|
||||||
|
|
||||||
|
userDisplayName *string
|
||||||
|
userDisplayColor *int
|
||||||
|
userID *string
|
||||||
|
title *string
|
||||||
|
subtitle *string
|
||||||
|
link *string
|
||||||
|
|
||||||
|
userType *string
|
||||||
|
userScopes *string
|
||||||
|
hiddenAt *time.Time
|
||||||
|
userCreatedAt *time.Time
|
||||||
|
userDisabledAt *time.Time
|
||||||
|
userAuthenticatedAt *time.Time
|
||||||
|
userNameChangedAt *time.Time
|
||||||
|
body string
|
||||||
|
eventType models.EventType
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getChat(rows *sql.Rows) ([]interface{}, error) {
|
||||||
|
history := make([]interface{}, 0)
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
row := rowData{}
|
||||||
|
|
||||||
|
// Convert a database row into a chat event
|
||||||
|
if err := rows.Scan(
|
||||||
|
&row.id,
|
||||||
|
&row.userID,
|
||||||
|
&row.body,
|
||||||
|
&row.title,
|
||||||
|
&row.subtitle,
|
||||||
|
&row.image,
|
||||||
|
&row.link,
|
||||||
|
&row.eventType,
|
||||||
|
&row.hiddenAt,
|
||||||
|
&row.timestamp,
|
||||||
|
&row.userDisplayName,
|
||||||
|
&row.userDisplayColor,
|
||||||
|
&row.userCreatedAt,
|
||||||
|
&row.userDisabledAt,
|
||||||
|
&row.previousUsernames,
|
||||||
|
&row.userNameChangedAt,
|
||||||
|
&row.userAuthenticatedAt,
|
||||||
|
&row.userScopes,
|
||||||
|
&row.userType,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var message interface{}
|
||||||
|
|
||||||
|
switch row.eventType {
|
||||||
|
case events.MessageSent:
|
||||||
|
message = makeUserMessageEventFromRowData(row)
|
||||||
|
case events.SystemMessageSent:
|
||||||
|
message = makeSystemMessageChatEventFromRowData(row)
|
||||||
|
case events.ChatActionSent:
|
||||||
|
message = makeActionMessageChatEventFromRowData(row)
|
||||||
|
case events.FediverseEngagementFollow:
|
||||||
|
message = makeFederatedActionChatEventFromRowData(row)
|
||||||
|
case events.FediverseEngagementLike:
|
||||||
|
message = makeFederatedActionChatEventFromRowData(row)
|
||||||
|
case events.FediverseEngagementRepost:
|
||||||
|
message = makeFederatedActionChatEventFromRowData(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
history = append(history, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return history, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _historyCache *[]interface{}
|
||||||
|
|
||||||
|
// GetChatModerationHistory will return all the chat messages suitable for moderation purposes.
|
||||||
|
func (r *SqlChatMessageRepository) GetChatModerationHistory() []interface{} {
|
||||||
|
if _historyCache != nil {
|
||||||
|
return *_historyCache
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := r.datastore.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error fetching chat moderation history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer tx.Rollback() // nolint
|
||||||
|
|
||||||
|
// Get all messages regardless of visibility
|
||||||
|
query := "SELECT messages.id, user_id, body, title, subtitle, image, link, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, authenticated_at, scopes, type FROM messages INNER JOIN users ON messages.user_id = users.id ORDER BY timestamp DESC"
|
||||||
|
stmt, err := tx.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error fetching chat moderation history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.Query()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error fetching chat moderation history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
result, err := getChat(rows)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_historyCache = &result
|
||||||
|
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
log.Errorln("error fetching chat moderation history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChatHistory will return all the chat messages suitable for returning as user-facing chat history.
|
||||||
|
func (r *SqlChatMessageRepository) GetChatHistory() []interface{} {
|
||||||
|
tx, err := r.datastore.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error fetching chat history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer tx.Rollback() // nolint
|
||||||
|
|
||||||
|
// Get all visible messages
|
||||||
|
query := "SELECT messages.id, messages.user_id, messages.body, messages.title, messages.subtitle, messages.image, messages.link, messages.eventType, messages.hidden_at, messages.timestamp, users.display_name, users.display_color, users.created_at, users.disabled_at, users.previous_names, users.namechanged_at, users.authenticated_at, users.scopes, users.type FROM users JOIN messages ON users.id = messages.user_id WHERE hidden_at IS NULL AND disabled_at IS NULL ORDER BY timestamp DESC LIMIT ?"
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error fetching chat history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.Query(maxBacklogNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error fetching chat history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
m, err := getChat(rows)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
log.Errorln("There is a problem enumerating chat message rows. Please report this:", query)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
log.Errorln("error fetching chat history", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert order of messages
|
||||||
|
for i, j := 0, len(m)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
m[i], m[j] = m[j], m[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMessagesFromUser returns chat messages that were sent by a specific user.
|
||||||
|
func (r *SqlChatMessageRepository) GetMessagesFromUser(userID string) ([]events.UserMessageEvent, error) {
|
||||||
|
query, err := r.datastore.GetQueries().GetMessagesFromUser(context.Background(), sql.NullString{String: userID, Valid: true})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]events.UserMessageEvent, len(query))
|
||||||
|
for i, row := range query {
|
||||||
|
results[i] = events.UserMessageEvent{
|
||||||
|
Event: events.Event{
|
||||||
|
Timestamp: row.Timestamp.Time,
|
||||||
|
ID: row.ID,
|
||||||
|
},
|
||||||
|
MessageEvent: events.MessageEvent{
|
||||||
|
Body: row.Body.String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMessageIdsForUserID will return the chat message IDs for a specific user.
|
||||||
|
func (r *SqlChatMessageRepository) GetMessageIdsForUserID(userID string) ([]string, error) {
|
||||||
|
defer func() {
|
||||||
|
_historyCache = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
tx, err := r.datastore.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("error while setting message visibility")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer tx.Rollback() // nolint
|
||||||
|
query := "SELECT messages.id, user_id, body, title, subtitle, image, link, eventType, hidden_at, timestamp, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, authenticated_at, scopes, type FROM messages INNER JOIN users ON messages.user_id = users.id WHERE user_id IS ?"
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("error while setting message visibility")
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.Query(userID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error while setting message visibility", err)
|
||||||
|
return nil, errors.New("error while setting message visibility")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
// Get a list of IDs to send to the connected clients to hide
|
||||||
|
ids := make([]string, 0)
|
||||||
|
|
||||||
|
messages, err := getChat(rows)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
return nil, errors.New("There is a problem enumerating chat message rows. Please report this: " + query)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(messages) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, message := range messages {
|
||||||
|
ids = append(ids, message.(events.UserMessageEvent).ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
return nil, errors.New("error while setting message visibility")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the clients to hide/show these messages.
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SqlChatMessageRepository) SetMessageVisibilityForMessageIDs(messageIDs []string, visible bool) error {
|
||||||
|
defer func() {
|
||||||
|
_historyCache = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
if len(messageIDs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.datastore.DbLock.Lock()
|
||||||
|
defer r.datastore.DbLock.Unlock()
|
||||||
|
|
||||||
|
tx, err := r.datastore.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:gosec
|
||||||
|
stmt, err := tx.Prepare("UPDATE messages SET hidden_at=? WHERE id IN (?" + strings.Repeat(",?", len(messageIDs)-1) + ")")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
var hiddenAt *time.Time
|
||||||
|
if !visible {
|
||||||
|
now := time.Now()
|
||||||
|
hiddenAt = &now
|
||||||
|
} else {
|
||||||
|
hiddenAt = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(messageIDs)+1)
|
||||||
|
args[0] = hiddenAt
|
||||||
|
for i, id := range messageIDs {
|
||||||
|
args[i+1] = id
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = stmt.Exec(args...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMessagesCount will return the number of messages in the database.
|
||||||
|
func (r *SqlChatMessageRepository) GetMessagesCount() int64 {
|
||||||
|
query := `SELECT COUNT(*) FROM messages`
|
||||||
|
rows, err := r.datastore.DB.Query(query)
|
||||||
|
if err != nil || rows.Err() != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var count int64
|
||||||
|
for rows.Next() {
|
||||||
|
if err := rows.Scan(&count); err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/webserver/handlers/generated"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,8 +63,9 @@ func migrateToDatastoreValues2(datastore *data.Datastore, configRepository Confi
|
|||||||
oldAdminPassword, _ := datastore.GetString("stream_key")
|
oldAdminPassword, _ := datastore.GetString("stream_key")
|
||||||
// Avoids double hashing the password
|
// Avoids double hashing the password
|
||||||
_ = datastore.SetString("admin_password_key", oldAdminPassword)
|
_ = datastore.SetString("admin_password_key", oldAdminPassword)
|
||||||
_ = configRepository.SetStreamKeys([]models.StreamKey{
|
comment := "Default stream key"
|
||||||
{Key: oldAdminPassword, Comment: "Default stream key"},
|
_ = configRepository.SetStreamKeys([]generated.StreamKey{
|
||||||
|
{Key: &oldAdminPassword, Comment: &comment},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
"github.com/owncast/owncast/webserver/handlers/generated"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigRepository interface {
|
type ConfigRepository interface {
|
||||||
@@ -114,8 +115,8 @@ type ConfigRepository interface {
|
|||||||
SetCustomOfflineMessage(message string) error
|
SetCustomOfflineMessage(message string) error
|
||||||
SetCustomColorVariableValues(variables map[string]string) error
|
SetCustomColorVariableValues(variables map[string]string) error
|
||||||
GetCustomColorVariableValues() map[string]string
|
GetCustomColorVariableValues() map[string]string
|
||||||
GetStreamKeys() []models.StreamKey
|
GetStreamKeys() []generated.StreamKey
|
||||||
SetStreamKeys(actions []models.StreamKey) error
|
SetStreamKeys(actions []generated.StreamKey) error
|
||||||
SetDisableSearchIndexing(disableSearchIndexing bool) error
|
SetDisableSearchIndexing(disableSearchIndexing bool) error
|
||||||
GetDisableSearchIndexing() bool
|
GetDisableSearchIndexing() bool
|
||||||
GetVideoServingEndpoint() string
|
GetVideoServingEndpoint() string
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
"github.com/owncast/owncast/static"
|
"github.com/owncast/owncast/static"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
"github.com/owncast/owncast/webserver/handlers/generated"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -957,22 +958,22 @@ func (r *SqlConfigRepository) GetCustomColorVariableValues() map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetStreamKeys will return valid stream keys.
|
// GetStreamKeys will return valid stream keys.
|
||||||
func (r *SqlConfigRepository) GetStreamKeys() []models.StreamKey {
|
func (r *SqlConfigRepository) GetStreamKeys() []generated.StreamKey {
|
||||||
configEntry, err := r.datastore.Get(streamKeysKey)
|
configEntry, err := r.datastore.Get(streamKeysKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []models.StreamKey{}
|
return []generated.StreamKey{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var streamKeys []models.StreamKey
|
var streamKeys []generated.StreamKey
|
||||||
if err := configEntry.GetObject(&streamKeys); err != nil {
|
if err := configEntry.GetObject(&streamKeys); err != nil {
|
||||||
return []models.StreamKey{}
|
return []generated.StreamKey{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return streamKeys
|
return streamKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStreamKeys will set valid stream keys.
|
// SetStreamKeys will set valid stream keys.
|
||||||
func (r *SqlConfigRepository) SetStreamKeys(actions []models.StreamKey) error {
|
func (r *SqlConfigRepository) SetStreamKeys(actions []generated.StreamKey) error {
|
||||||
configEntry := models.ConfigEntry{Key: streamKeysKey, Value: actions}
|
configEntry := models.ConfigEntry{Key: streamKeysKey, Value: actions}
|
||||||
return r.datastore.Save(configEntry)
|
return r.datastore.Save(configEntry)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package tables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/owncast/owncast/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateConfigTable(db *sql.DB) {
|
||||||
|
createTableSQL := `CREATE TABLE IF NOT EXISTS datastore (
|
||||||
|
"key" string NOT NULL PRIMARY KEY,
|
||||||
|
"value" BLOB,
|
||||||
|
"timestamp" DATE DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||||
|
);`
|
||||||
|
|
||||||
|
utils.MustExec(createTableSQL, db)
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package tables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateWebhooksTable(db *sql.DB) {
|
||||||
|
log.Traceln("Creating webhooks table...")
|
||||||
|
|
||||||
|
createTableSQL := `CREATE TABLE IF NOT EXISTS webhooks (
|
||||||
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"url" string NOT NULL,
|
||||||
|
"events" TEXT NOT NULL,
|
||||||
|
"timestamp" DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"last_used" DATETIME
|
||||||
|
);`
|
||||||
|
|
||||||
|
stmt, err := db.Prepare(createTableSQL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
if _, err = stmt.Exec(); err != nil {
|
||||||
|
log.Warnln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package data
|
package webhookrepository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@@ -6,38 +6,51 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createWebhooksTable() {
|
type WebhookRepository interface {
|
||||||
log.Traceln("Creating webhooks table...")
|
InsertWebhook(url string, events []models.EventType) (int, error)
|
||||||
|
DeleteWebhook(id int) error
|
||||||
|
GetWebhooksForEvent(event models.EventType) []models.Webhook
|
||||||
|
GetWebhooks() ([]models.Webhook, error)
|
||||||
|
SetWebhookAsUsed(webhook models.Webhook) error
|
||||||
|
}
|
||||||
|
|
||||||
createTableSQL := `CREATE TABLE IF NOT EXISTS webhooks (
|
type SqlWebhookRepository struct {
|
||||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
datastore *data.Datastore
|
||||||
"url" string NOT NULL,
|
}
|
||||||
"events" TEXT NOT NULL,
|
|
||||||
"timestamp" DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"last_used" DATETIME
|
|
||||||
);`
|
|
||||||
|
|
||||||
stmt, err := _db.Prepare(createTableSQL)
|
// NOTE: This is temporary during the transition period.
|
||||||
if err != nil {
|
var temporaryGlobalInstance WebhookRepository
|
||||||
log.Fatal(err)
|
|
||||||
|
// Get will return the user repository.
|
||||||
|
func Get() WebhookRepository {
|
||||||
|
if temporaryGlobalInstance == nil {
|
||||||
|
i := New(data.GetDatastore())
|
||||||
|
temporaryGlobalInstance = i
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
return temporaryGlobalInstance
|
||||||
if _, err = stmt.Exec(); err != nil {
|
}
|
||||||
log.Warnln(err)
|
|
||||||
|
// New will create a new instance of the UserRepository.
|
||||||
|
func New(datastore *data.Datastore) WebhookRepository {
|
||||||
|
r := SqlWebhookRepository{
|
||||||
|
datastore: datastore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &r
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertWebhook will add a new webhook to the database.
|
// InsertWebhook will add a new webhook to the database.
|
||||||
func InsertWebhook(url string, events []models.EventType) (int, error) {
|
func (r *SqlWebhookRepository) InsertWebhook(url string, events []models.EventType) (int, error) {
|
||||||
log.Traceln("Adding new webhook")
|
log.Traceln("Adding new webhook")
|
||||||
|
|
||||||
eventsString := strings.Join(events, ",")
|
eventsString := strings.Join(events, ",")
|
||||||
|
|
||||||
tx, err := _db.Begin()
|
tx, err := r.datastore.DB.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -65,10 +78,10 @@ func InsertWebhook(url string, events []models.EventType) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteWebhook will delete a webhook from the database.
|
// DeleteWebhook will delete a webhook from the database.
|
||||||
func DeleteWebhook(id int) error {
|
func (r *SqlWebhookRepository) DeleteWebhook(id int) error {
|
||||||
log.Traceln("Deleting webhook")
|
log.Traceln("Deleting webhook")
|
||||||
|
|
||||||
tx, err := _db.Begin()
|
tx, err := r.datastore.DB.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -96,7 +109,7 @@ func DeleteWebhook(id int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhooksForEvent will return all of the webhooks that want to be notified about an event type.
|
// GetWebhooksForEvent will return all of the webhooks that want to be notified about an event type.
|
||||||
func GetWebhooksForEvent(event models.EventType) []models.Webhook {
|
func (r *SqlWebhookRepository) GetWebhooksForEvent(event models.EventType) []models.Webhook {
|
||||||
webhooks := make([]models.Webhook, 0)
|
webhooks := make([]models.Webhook, 0)
|
||||||
|
|
||||||
query := `SELECT * FROM (
|
query := `SELECT * FROM (
|
||||||
@@ -111,9 +124,9 @@ func GetWebhooksForEvent(event models.EventType) []models.Webhook {
|
|||||||
SELECT id, url, event
|
SELECT id, url, event
|
||||||
FROM split
|
FROM split
|
||||||
WHERE event <> ''
|
WHERE event <> ''
|
||||||
) AS webhook WHERE event IS "` + event + `"`
|
) AS webhook WHERE event IS ?`
|
||||||
|
|
||||||
rows, err := _db.Query(query)
|
rows, err := r.datastore.DB.Query(query, event)
|
||||||
if err != nil || rows.Err() != nil {
|
if err != nil || rows.Err() != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -140,12 +153,12 @@ func GetWebhooksForEvent(event models.EventType) []models.Webhook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhooks will return all the webhooks.
|
// GetWebhooks will return all the webhooks.
|
||||||
func GetWebhooks() ([]models.Webhook, error) { //nolint
|
func (r *SqlWebhookRepository) GetWebhooks() ([]models.Webhook, error) { //nolint
|
||||||
webhooks := make([]models.Webhook, 0)
|
webhooks := make([]models.Webhook, 0)
|
||||||
|
|
||||||
query := "SELECT * FROM webhooks"
|
query := "SELECT * FROM webhooks"
|
||||||
|
|
||||||
rows, err := _db.Query(query)
|
rows, err := r.datastore.DB.Query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return webhooks, err
|
return webhooks, err
|
||||||
}
|
}
|
||||||
@@ -193,8 +206,8 @@ func GetWebhooks() ([]models.Webhook, error) { //nolint
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetWebhookAsUsed will update the last used time for a webhook.
|
// SetWebhookAsUsed will update the last used time for a webhook.
|
||||||
func SetWebhookAsUsed(webhook models.Webhook) error {
|
func (r *SqlWebhookRepository) SetWebhookAsUsed(webhook models.Webhook) error {
|
||||||
tx, err := _db.Begin()
|
tx, err := r.datastore.DB.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[4619],{54619:function(e,t,o){"use strict";o.r(t),o.d(t,{NotifyReminderPopup:function(){return h}});var n=o(85893),s=o(67294),r=o(5152),c=o.n(r),i=o(37039),l=o(4330),a=o.n(l),p=o(62960),u=o.n(p);let d=e=>{let{open:t,title:o,content:s,children:r}=e;return(0,n.jsxs)("div",{style:{width:"max-content",height:"max-content"},children:[t&&(0,n.jsx)("div",{className:u().anchor,children:(0,n.jsxs)("div",{className:u().popover,children:[(0,n.jsx)("div",{className:u().title,children:o}),(0,n.jsx)("hr",{style:{color:"var(--color-owncast-palette-4)"}}),(0,n.jsx)("div",{className:u().content,children:s})]})}),r]})},_=c()(()=>o.e(2155).then(o.t.bind(o,12155,23)),{loadableGenerated:{webpack:()=>[12155]},ssr:!1}),h=e=>{let{children:t,open:o,notificationClicked:r,notificationClosed:c}=e,[l,p]=(0,s.useState)(o),[u,h]=(0,s.useState)(!1),{t:m}=(0,i.$G)();(0,s.useEffect)(()=>{p(o)},[o]),(0,s.useEffect)(()=>{h(!0)},[]);let v=(0,n.jsx)("div",{className:a().title,children:m("Stay updated!")}),b=e=>{e.stopPropagation(),r()},x=(0,n.jsxs)("div",{onClick:b,onKeyDown:b,role:"menuitem",tabIndex:0,children:[(0,n.jsx)("button",{type:"button","aria-label":"Follow",className:a().closebutton,onClick:e=>{e.stopPropagation(),p(!1),c()},children:(0,n.jsx)(_,{})}),(0,n.jsx)("div",{className:a().contentbutton,children:m("Click and never miss future streams!")})]});return u&&(0,n.jsx)(d,{open:l,title:v,content:x,children:t})}},4330:function(e){e.exports={popupBackgroundColor:"var(--theme-color-components-primary-button-background)",contentbutton:"NotifyReminderPopup_contentbutton___iqOh",closebutton:"NotifyReminderPopup_closebutton__dpvj4",title:"NotifyReminderPopup_title__imysF"}},62960:function(e){e.exports={anchor:"Popover_anchor__GI7l_",popover:"Popover_popover__pMNs7",title:"Popover_title__T__E6",content:"Popover_content__7gDLm"}}}]);
|
||||||
@@ -1 +0,0 @@
|
|||||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[4619],{54619:function(e,t,o){"use strict";o.r(t),o.d(t,{NotifyReminderPopup:function(){return _}});var n=o(85893),s=o(67294),r=o(5152),c=o.n(r),i=o(4330),l=o.n(i),a=o(62960),p=o.n(a);let u=e=>{let{open:t,title:o,content:s,children:r}=e;return(0,n.jsxs)("div",{style:{width:"max-content",height:"max-content"},children:[t&&(0,n.jsx)("div",{className:p().anchor,children:(0,n.jsxs)("div",{className:p().popover,children:[(0,n.jsx)("div",{className:p().title,children:o}),(0,n.jsx)("hr",{style:{color:"var(--color-owncast-palette-4)"}}),(0,n.jsx)("div",{className:p().content,children:s})]})}),r]})},d=c()(()=>o.e(2155).then(o.t.bind(o,12155,23)),{loadableGenerated:{webpack:()=>[12155]},ssr:!1}),_=e=>{let{children:t,open:o,notificationClicked:r,notificationClosed:c}=e,[i,a]=(0,s.useState)(o),[p,_]=(0,s.useState)(!1);(0,s.useEffect)(()=>{a(o)},[o]),(0,s.useEffect)(()=>{_(!0)},[]);let h=(0,n.jsx)("div",{className:l().title,children:"Stay updated!"}),m=e=>{e.stopPropagation(),r()},v=(0,n.jsxs)("div",{onClick:m,onKeyDown:m,role:"menuitem",tabIndex:0,children:[(0,n.jsx)("button",{type:"button","aria-label":"Follow",className:l().closebutton,onClick:e=>{e.stopPropagation(),a(!1),c()},children:(0,n.jsx)(d,{})}),(0,n.jsx)("div",{className:l().contentbutton,children:"Click and never miss future streams!"})]});return p&&(0,n.jsx)(u,{open:i,title:h,content:v,children:t})}},4330:function(e){e.exports={popupBackgroundColor:"var(--theme-color-components-primary-button-background)",contentbutton:"NotifyReminderPopup_contentbutton___iqOh",closebutton:"NotifyReminderPopup_closebutton__dpvj4",title:"NotifyReminderPopup_title__imysF"}},62960:function(e){e.exports={anchor:"Popover_anchor__GI7l_",popover:"Popover_popover__pMNs7",title:"Popover_title__T__E6",content:"Popover_content__7gDLm"}}}]);
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8142],{22367:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{"fill-rule":"evenodd",viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm0 76c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zm128.01 198.83c.03 0 .05.01.09.06l45.02 45.01a.2.2 0 01.05.09.12.12 0 010 .07c0 .02-.01.04-.05.08L557.25 512l127.87 127.86a.27.27 0 01.05.06v.02a.12.12 0 010 .07c0 .03-.01.05-.05.09l-45.02 45.02a.2.2 0 01-.09.05.12.12 0 01-.07 0c-.02 0-.04-.01-.08-.05L512 557.25 384.14 685.12c-.04.04-.06.05-.08.05a.12.12 0 01-.07 0c-.03 0-.05-.01-.09-.05l-45.02-45.02a.2.2 0 01-.05-.09.12.12 0 010-.07c0-.02.01-.04.06-.08L466.75 512 338.88 384.14a.27.27 0 01-.05-.06l-.01-.02a.12.12 0 010-.07c0-.03.01-.05.05-.09l45.02-45.02a.2.2 0 01.09-.05.12.12 0 01.07 0c.02 0 .04.01.08.06L512 466.75l127.86-127.86c.04-.05.06-.06.08-.06a.12.12 0 01.07 0z"}}]},name:"close-circle",theme:"outlined"}},18142:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,l=(a=r(22284))&&a.__esModule?a:{default:a};t.default=l,e.exports=l},22284:function(e,t,r){var a=r(64836),l=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=a(r(42122)),u=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=l(e)&&"function"!=typeof e)return{default:e};var r=o(void 0);if(r&&r.has(e))return r.get(e);var a={__proto__:null},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var u in e)if("default"!==u&&({}).hasOwnProperty.call(e,u)){var c=n?Object.getOwnPropertyDescriptor(e,u):null;c&&(c.get||c.set)?Object.defineProperty(a,u,c):a[u]=e[u]}return a.default=e,r&&r.set(e,a),a}(r(67294)),c=a(r(22367)),f=a(r(3247));function o(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(o=function(e){return e?r:t})(e)}var i=u.forwardRef(function(e,t){return u.createElement(f.default,(0,n.default)((0,n.default)({},e),{},{ref:t,icon:c.default}))});t.default=i}}]);
|
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8142],{74644:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{"fill-rule":"evenodd",viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm0 76c-205.4 0-372 166.6-372 372s166.6 372 372 372 372-166.6 372-372-166.6-372-372-372zm128.01 198.83c.03 0 .05.01.09.06l45.02 45.01a.2.2 0 01.05.09.12.12 0 010 .07c0 .02-.01.04-.05.08L557.25 512l127.87 127.86a.27.27 0 01.05.06v.02a.12.12 0 010 .07c0 .03-.01.05-.05.09l-45.02 45.02a.2.2 0 01-.09.05.12.12 0 01-.07 0c-.02 0-.04-.01-.08-.05L512 557.25 384.14 685.12c-.04.04-.06.05-.08.05a.12.12 0 01-.07 0c-.03 0-.05-.01-.09-.05l-45.02-45.02a.2.2 0 01-.05-.09.12.12 0 010-.07c0-.02.01-.04.06-.08L466.75 512 338.88 384.14a.27.27 0 01-.05-.06l-.01-.02a.12.12 0 010-.07c0-.03.01-.05.05-.09l45.02-45.02a.2.2 0 01.09-.05.12.12 0 01.07 0c.02 0 .04.01.08.06L512 466.75l127.86-127.86c.04-.05.06-.06.08-.06a.12.12 0 01.07 0z"}}]},name:"close-circle",theme:"outlined"}},18142:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,l=(a=r(22284))&&a.__esModule?a:{default:a};t.default=l,e.exports=l},22284:function(e,t,r){var a=r(64836),l=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=a(r(42122)),u=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=l(e)&&"function"!=typeof e)return{default:e};var r=o(void 0);if(r&&r.has(e))return r.get(e);var a={__proto__:null},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var u in e)if("default"!==u&&({}).hasOwnProperty.call(e,u)){var c=n?Object.getOwnPropertyDescriptor(e,u):null;c&&(c.get||c.set)?Object.defineProperty(a,u,c):a[u]=e[u]}return a.default=e,r&&r.set(e,a),a}(r(67294)),c=a(r(74644)),f=a(r(3247));function o(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(o=function(e){return e?r:t})(e)}var i=u.forwardRef(function(e,t){return u.createElement(f.default,(0,n.default)((0,n.default)({},e),{},{ref:t,icon:c.default}))});t.default=i}}]);
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6559],{10887:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/chat/emojis",function(){return n(21493)}])},21493:function(e,t,n){"use strict";n.r(t);var i=n(85893),o=n(3816),s=n(23657),a=n(56469),l=n(10227),r=n(78021),c=n(8968),u=n(10647),d=n(6468),m=n(67294),h=n(5152),j=n.n(h),f=n(17586),p=n(11992),g=n(35329),y=n(28700),w=n(70869),x=n(25889);let{Meta:k}=o.default,b=j()(()=>Promise.resolve().then(n.t.bind(n,12155,23)),{loadableGenerated:{webpack:()=>[12155]},ssr:!1}),{Title:E,Paragraph:_}=s.default,T=()=>{let[e,t]=(0,m.useState)([]),[n,s]=(0,m.useState)(!1),[h,j]=(0,m.useState)(null),[x,T]=(0,m.useState)(null),v=null,B=()=>{j(null),clearTimeout(v),v=null};async function Z(){s(!0);try{let e=await (0,p.rQ)("/api/emoji");t(e)}catch(e){console.error("error fetching emojis",e)}s(!1)}async function N(e){let t="/".concat(e.split("/").slice(3).join("/"));console.log(t),s(!0),j((0,y.kg)(y.Jk,"Deleting emoji..."));try{let e=await (0,p.rQ)(p.Ff,{method:"POST",data:{name:t}});if(e instanceof Error)throw e;j((0,y.kg)(y.zv,"Emoji deleted")),v=setTimeout(B,w.sI)}catch(e){j((0,y.kg)(y.Un,"".concat(e))),s(!1),v=setTimeout(B,w.sI)}Z()}async function P(){s(!0);try{j((0,y.kg)(y.Jk,"Converting emoji..."));let e=await new Promise((e,t)=>{if(!g.dr.includes(x.type)){let e="File type is not supported: ".concat(x.type);return t(e)}(0,g.y3)(x,t=>e({name:x.name,url:t}))});j((0,y.kg)(y.Jk,"Uploading emoji..."));let t=await (0,p.rQ)(p.Qc,{method:"POST",data:{name:e.name,data:e.url}});if(t instanceof Error)throw t;j((0,y.kg)(y.zv,"Emoji uploaded successfully!")),Z()}catch(e){j((0,y.kg)(y.Un,"".concat(e)))}v=setTimeout(B,w.sI),s(!1)}return(0,m.useEffect)(()=>{Z()},[]),(0,i.jsxs)("div",{children:[(0,i.jsx)(E,{children:"Emojis"}),(0,i.jsx)(_,{children:"Here you can upload new custom emojis for usage in the chat. When uploading a new emoji, the filename without extension will be used as emoji name. Additionally, emoji names are case-insensitive. For best results, ensure all emoji have unique names."}),(0,i.jsx)("br",{}),(0,i.jsx)(d.Z,{name:"emoji",listType:"picture",className:"emoji-uploader",showUploadList:!1,accept:g.dr.join(","),beforeUpload:T,customRequest:P,disabled:n,children:(0,i.jsx)(a.Z,{type:"primary",disabled:n,children:"Upload new emoji"})}),(0,i.jsx)(f.Z,{status:h}),(0,i.jsx)("br",{}),(0,i.jsx)(l.Z,{children:e.map(e=>(0,i.jsx)(r.Z,{style:{padding:"10px"},children:(0,i.jsx)(o.default,{style:{width:120,marginTop:16},actions:[],children:(0,i.jsx)(k,{description:[(0,i.jsxs)("div",{style:{display:"flex",justifyItems:"center",alignItems:"center",flexDirection:"column",gap:"20px"},children:[(0,i.jsx)(c.Z,{title:e.name,children:(0,i.jsx)(u.C,{style:{height:50,width:50},src:e.url})}),(0,i.jsx)(a.Z,{size:"small",type:"ghost",title:"Delete emoji",style:{position:"absolute",right:0,top:0,height:24,width:24,border:"none",color:"gray"},onClick:()=>N(e.url),icon:(0,i.jsx)(b,{})})]})]})})},e.name))}),(0,i.jsx)("br",{})]})};T.getLayout=function(e){return(0,i.jsx)(x.l,{page:e})},t.default=T},35329:function(e,t,n){"use strict";n.d(t,{Z7:function(){return i},dr:function(){return o},kR:function(){return a},y3:function(){return s}});let i=2097152,o=["image/png","image/jpeg","image/gif"];function s(e,t){let n=new FileReader;n.addEventListener("load",()=>t(n.result)),n.readAsDataURL(e)}function a(e){let t=Math.floor(Math.log(e)/Math.log(1024)),n=1*Number((e/Math.pow(1024,t)).toFixed(2));return"".concat(n," ").concat(["B","KB","MB","GB","TB","PB","EB","ZB","YB"][t])}}},function(e){e.O(0,[3247,83,1287,3800,7786,443,9904,3657,6167,2502,9307,2179,6356,1616,5889,2888,9774,179],function(){return e(e.s=10887)}),_N_E=e.O()}]);
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6559],{10887:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/chat/emojis",function(){return n(21493)}])},21493:function(e,t,n){"use strict";n.r(t);var i=n(85893),o=n(3816),s=n(23657),a=n(56469),l=n(10227),r=n(78021),c=n(8968),u=n(10647),d=n(6468),m=n(67294),h=n(5152),j=n.n(h),f=n(17586),p=n(11992),g=n(35329),y=n(28700),w=n(70869),x=n(25889);let{Meta:k}=o.default,b=j()(()=>Promise.resolve().then(n.t.bind(n,12155,23)),{loadableGenerated:{webpack:()=>[12155]},ssr:!1}),{Title:E,Paragraph:_}=s.default,T=()=>{let[e,t]=(0,m.useState)([]),[n,s]=(0,m.useState)(!1),[h,j]=(0,m.useState)(null),[x,T]=(0,m.useState)(null),v=null,B=()=>{j(null),clearTimeout(v),v=null};async function Z(){s(!0);try{let e=await (0,p.rQ)("/api/emoji");t(e)}catch(e){console.error("error fetching emojis",e)}s(!1)}async function N(e){let t="/".concat(e.split("/").slice(3).join("/"));console.log(t),s(!0),j((0,y.kg)(y.Jk,"Deleting emoji..."));try{let e=await (0,p.rQ)(p.Ff,{method:"POST",data:{name:t}});if(e instanceof Error)throw e;j((0,y.kg)(y.zv,"Emoji deleted")),v=setTimeout(B,w.sI)}catch(e){j((0,y.kg)(y.Un,"".concat(e))),s(!1),v=setTimeout(B,w.sI)}Z()}async function P(){s(!0);try{j((0,y.kg)(y.Jk,"Converting emoji..."));let e=await new Promise((e,t)=>{if(!g.dr.includes(x.type)){let e="File type is not supported: ".concat(x.type);return t(e)}(0,g.y3)(x,t=>e({name:x.name,url:t}))});j((0,y.kg)(y.Jk,"Uploading emoji..."));let t=await (0,p.rQ)(p.Qc,{method:"POST",data:{name:e.name,data:e.url}});if(t instanceof Error)throw t;j((0,y.kg)(y.zv,"Emoji uploaded successfully!")),Z()}catch(e){j((0,y.kg)(y.Un,"".concat(e)))}v=setTimeout(B,w.sI),s(!1)}return(0,m.useEffect)(()=>{Z()},[]),(0,i.jsxs)("div",{children:[(0,i.jsx)(E,{children:"Emojis"}),(0,i.jsx)(_,{children:"Here you can upload new custom emojis for usage in the chat. When uploading a new emoji, the filename without extension will be used as emoji name. Additionally, emoji names are case-insensitive. For best results, ensure all emoji have unique names."}),(0,i.jsx)("br",{}),(0,i.jsx)(d.Z,{name:"emoji",listType:"picture",className:"emoji-uploader",showUploadList:!1,accept:g.dr.join(","),beforeUpload:T,customRequest:P,disabled:n,children:(0,i.jsx)(a.Z,{type:"primary",disabled:n,children:"Upload new emoji"})}),(0,i.jsx)(f.Z,{status:h}),(0,i.jsx)("br",{}),(0,i.jsx)(l.Z,{children:e.map(e=>(0,i.jsx)(r.Z,{style:{padding:"10px"},children:(0,i.jsx)(o.default,{style:{width:120,marginTop:16},actions:[],children:(0,i.jsx)(k,{description:[(0,i.jsxs)("div",{style:{display:"flex",justifyItems:"center",alignItems:"center",flexDirection:"column",gap:"20px"},children:[(0,i.jsx)(c.Z,{title:e.name,children:(0,i.jsx)(u.C,{style:{height:50,width:50},src:e.url})}),(0,i.jsx)(a.Z,{size:"small",type:"ghost",title:"Delete emoji",style:{position:"absolute",right:0,top:0,height:24,width:24,border:"none",color:"gray"},onClick:()=>N(e.url),icon:(0,i.jsx)(b,{})})]})]})})},e.name))}),(0,i.jsx)("br",{})]})};T.getLayout=function(e){return(0,i.jsx)(x.l,{page:e})},t.default=T},35329:function(e,t,n){"use strict";n.d(t,{Z7:function(){return i},dr:function(){return o},kR:function(){return a},y3:function(){return s}});let i=2097152,o=["image/png","image/jpeg","image/gif"];function s(e,t){let n=new FileReader;n.addEventListener("load",()=>t(n.result)),n.readAsDataURL(e)}function a(e){let t=Math.floor(Math.log(e)/Math.log(1024)),n=1*Number((e/Math.pow(1024,t)).toFixed(2));return"".concat(n," ").concat(["B","KB","MB","GB","TB","PB","EB","ZB","YB"][t])}}},function(e){e.O(0,[3247,83,1287,3800,7786,443,9904,3657,1175,2502,9307,7987,6356,1616,5889,2888,9774,179],function(){return e(e.s=10887)}),_N_E=e.O()}]);
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[4976],{10203:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/federation/actions",function(){return n(57679)}])},64773:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=n(97685),i=n(67294);function u(){var e=i.useReducer(function(e){return e+1},0);return(0,r.Z)(e,2)[1]}},23061:function(e,t,n){"use strict";var r=n(67294),i=n(64773),u=n(98947);t.Z=function(){var e=!(arguments.length>0)||void 0===arguments[0]||arguments[0],t=(0,r.useRef)({}),n=(0,i.Z)();return(0,r.useEffect)(function(){var r=u.ZP.subscribe(function(r){t.current=r,e&&n()});return function(){return u.ZP.unsubscribe(r)}},[]),t.current}},65765:function(e,t,n){"use strict";var r=n(61185),i=n(59408),u=r.ZP;u.Header=r.h4,u.Footer=r.$_,u.Content=r.VY,u.Sider=i.Z,u._InternalSiderContext=i.D,t.default=u},57679:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return h}});var r=n(85893),i=n(67294),u=n(23657),o=n(27043),c=n(12642),s=n(11992),a=n(74040),l=n(25889);let{Title:f,Paragraph:d}=u.default;function h(){let[e,t]=(0,i.useState)([]),[n,u]=(0,i.useState)(0),[l,h]=(0,i.useState)(0),E=async()=>{try{let e="".concat(s.op,"?offset=").concat(50*l,"&limit=").concat(50),{results:n,total:r}=await (0,s.rQ)(e,{auth:!0});u(r),(0,a.Qr)(n)?t([]):t(n)}catch(e){console.log("==== error",e)}};(0,i.useEffect)(()=>{E()},[l]);let p=[{title:"Action",dataIndex:"type",key:"type",width:50,render:(e,t)=>{let n,i;switch(t.type){case"FEDIVERSE_ENGAGEMENT_REPOST":n="/img/repost.svg",i="Share";break;case"FEDIVERSE_ENGAGEMENT_LIKE":n="/img/like.svg",i="Like";break;case"FEDIVERSE_ENGAGEMENT_FOLLOW":n="/img/follow.svg",i="Follow";break;default:n=""}return(0,r.jsxs)("div",{style:{width:"100%",height:"100%",display:"flex",alignItems:"center",justifyContent:"center",flexDirection:"column"},children:[(0,r.jsx)("img",{src:n,width:"70%",alt:i,title:i}),(0,r.jsx)("div",{style:{fontSize:"0.7rem"},children:i})]})}},{title:"From",dataIndex:"actorIRI",key:"from",render:(e,t)=>(0,r.jsx)("a",{href:t.actorIRI,children:t.actorIRI})},{title:"When",dataIndex:"timestamp",key:"timestamp",render:(e,t)=>{let n=new Date(t.timestamp);return(0,c.WU)(n,"P pp")}}];return(0,r.jsxs)("div",{children:[(0,r.jsx)(f,{level:3,children:"Fediverse Actions"}),(0,r.jsx)(d,{children:"Below is a list of actions that were taken by others in response to your posts as well as people who requested to follow you."}),(0,r.jsx)(o.Z,{dataSource:e,columns:p,size:"small",rowKey:e=>e.iri,pagination:{pageSize:50,hideOnSinglePage:!0,showSizeChanger:!1,total:n},onChange:e=>{h(e.current)}})]})}h.getLayout=function(e){return(0,r.jsx)(l.l,{page:e})}},11163:function(e,t,n){e.exports=n(43079)},55945:function(e,t,n){"use strict";function r(e){return t=>{let n=(e?Math[e]:Math.trunc)(t);return 0===n?0:n}}n.d(t,{u:function(){return r}})},64077:function(e,t,n){"use strict";n.d(t,{_:function(){return i}});var r=n(46042);function i(e,t){return+(0,r.Q)(e)-+(0,r.Q)(t)}},94817:function(e,t,n){"use strict";n.d(t,{c:function(){return u}});var r=n(55945),i=n(64077);function u(e,t,n){let u=(0,i._)(e,t)/1e3;return(0,r.u)(null==n?void 0:n.roundingMethod)(u)}}},function(e){e.O(0,[83,1287,3800,7786,443,9904,3657,6167,2502,7528,9532,449,7043,4065,2642,5889,2888,9774,179],function(){return e(e.s=10203)}),_N_E=e.O()}]);
|
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[4976],{10203:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/admin/federation/actions",function(){return n(57679)}])},64773:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=n(97685),i=n(67294);function u(){var e=i.useReducer(function(e){return e+1},0);return(0,r.Z)(e,2)[1]}},23061:function(e,t,n){"use strict";var r=n(67294),i=n(64773),u=n(98947);t.Z=function(){var e=!(arguments.length>0)||void 0===arguments[0]||arguments[0],t=(0,r.useRef)({}),n=(0,i.Z)();return(0,r.useEffect)(function(){var r=u.ZP.subscribe(function(r){t.current=r,e&&n()});return function(){return u.ZP.unsubscribe(r)}},[]),t.current}},65765:function(e,t,n){"use strict";var r=n(61185),i=n(59408),u=r.ZP;u.Header=r.h4,u.Footer=r.$_,u.Content=r.VY,u.Sider=i.Z,u._InternalSiderContext=i.D,t.default=u},57679:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return h}});var r=n(85893),i=n(67294),u=n(23657),o=n(27043),c=n(12642),s=n(11992),a=n(74040),l=n(25889);let{Title:f,Paragraph:d}=u.default;function h(){let[e,t]=(0,i.useState)([]),[n,u]=(0,i.useState)(0),[l,h]=(0,i.useState)(0),E=async()=>{try{let e="".concat(s.op,"?offset=").concat(50*l,"&limit=").concat(50),{results:n,total:r}=await (0,s.rQ)(e,{auth:!0});u(r),(0,a.Qr)(n)?t([]):t(n)}catch(e){console.log("==== error",e)}};(0,i.useEffect)(()=>{E()},[l]);let p=[{title:"Action",dataIndex:"type",key:"type",width:50,render:(e,t)=>{let n,i;switch(t.type){case"FEDIVERSE_ENGAGEMENT_REPOST":n="/img/repost.svg",i="Share";break;case"FEDIVERSE_ENGAGEMENT_LIKE":n="/img/like.svg",i="Like";break;case"FEDIVERSE_ENGAGEMENT_FOLLOW":n="/img/follow.svg",i="Follow";break;default:n=""}return(0,r.jsxs)("div",{style:{width:"100%",height:"100%",display:"flex",alignItems:"center",justifyContent:"center",flexDirection:"column"},children:[(0,r.jsx)("img",{src:n,width:"70%",alt:i,title:i}),(0,r.jsx)("div",{style:{fontSize:"0.7rem"},children:i})]})}},{title:"From",dataIndex:"actorIRI",key:"from",render:(e,t)=>(0,r.jsx)("a",{href:t.actorIRI,children:t.actorIRI})},{title:"When",dataIndex:"timestamp",key:"timestamp",render:(e,t)=>{let n=new Date(t.timestamp);return(0,c.WU)(n,"P pp")}}];return(0,r.jsxs)("div",{children:[(0,r.jsx)(f,{level:3,children:"Fediverse Actions"}),(0,r.jsx)(d,{children:"Below is a list of actions that were taken by others in response to your posts as well as people who requested to follow you."}),(0,r.jsx)(o.Z,{dataSource:e,columns:p,size:"small",rowKey:e=>e.iri,pagination:{pageSize:50,hideOnSinglePage:!0,showSizeChanger:!1,total:n},onChange:e=>{h(e.current)}})]})}h.getLayout=function(e){return(0,r.jsx)(l.l,{page:e})}},55945:function(e,t,n){"use strict";function r(e){return t=>{let n=(e?Math[e]:Math.trunc)(t);return 0===n?0:n}}n.d(t,{u:function(){return r}})},64077:function(e,t,n){"use strict";n.d(t,{_:function(){return i}});var r=n(46042);function i(e,t){return+(0,r.Q)(e)-+(0,r.Q)(t)}},94817:function(e,t,n){"use strict";n.d(t,{c:function(){return u}});var r=n(55945),i=n(64077);function u(e,t,n){let u=(0,i._)(e,t)/1e3;return(0,r.u)(null==n?void 0:n.roundingMethod)(u)}}},function(e){e.O(0,[83,1287,3800,7786,443,9904,3657,1175,2502,7528,9532,449,7043,4065,2642,5889,2888,9774,179],function(){return e(e.s=10203)}),_N_E=e.O()}]);
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user