135 Commits

Author SHA1 Message Date
Owncast
2c2bf2b5bb Bundle embedded web app 2024-11-25 14:10:56 +00:00
renovate[bot]
32bf67b026 chore(deps): lock file maintenance (#4035)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 14:05:57 +00:00
renovate[bot]
d578f01348 fix(deps): update module github.com/stretchr/testify to v1.10.0 2024-11-23 12:22:10 +00:00
renovate[bot]
8985947d7f chore(deps): update peter-evans/create-or-update-comment digest to 7157823 2024-11-20 07:57:56 +00:00
Owncast
e1acfee49d Bundle embedded web app 2024-11-18 09:18:37 +00:00
renovate[bot]
49c07594fb chore(deps): lock file maintenance (#4018)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 09:13:30 +00:00
dependabot[bot]
dc48e0bca7 Bump jsonpath-plus and artillery in /test/load (#4011)
Bumps [jsonpath-plus](https://github.com/s3u/JSONPath) to 10.1.0 and updates ancestor dependency [artillery](https://github.com/artilleryio/artillery). These dependencies need to be updated together.


Updates `jsonpath-plus` from 7.2.0 to 10.1.0
- [Release notes](https://github.com/s3u/JSONPath/releases)
- [Changelog](https://github.com/JSONPath-Plus/JSONPath/blob/main/CHANGES.md)
- [Commits](https://github.com/s3u/JSONPath/compare/v7.2.0...v10.1.0)

Updates `artillery` from 2.0.10 to 2.0.21
- [Release notes](https://github.com/artilleryio/artillery/releases)
- [Commits](https://github.com/artilleryio/artillery/compare/artillery-2.0.10...artillery-2.0.21)

---
updated-dependencies:
- dependency-name: jsonpath-plus
  dependency-type: indirect
- dependency-name: artillery
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-15 20:04:45 -08:00
Gabe Kangas
0b5d7c8a4d Config repository (#3988)
* WIP

* fix(test): fix ap test failing

* fix: fix unkeyed fields being used

* chore(tests): clean up browser tests by splitting out federation UI tests
2024-11-15 19:20:58 -08:00
Gabe Kangas
56d52c283c chore(tests): clean up browser tests by splitting out federation UI tests 2024-11-15 18:46:18 -08:00
Owncast
8fa2546bef Bundle embedded web app 2024-11-15 21:31:12 +00:00
Gabe Kangas
70bbcb97b8 fix(js): tweak how the vjs hook is fired and url is created 2024-11-15 13:25:06 -08:00
Owncast
d8abe74e5a Bundle embedded web app 2024-11-15 21:00:08 +00:00
Mahlangu
681111ceb2 refactor: update vjs beforeRequest to onRequest (#3945)
* refactor: update vjs onRequst to onRequest

* fix(js): update vjs to use the new onrequest hook for manipulating request uri

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-15 12:55:26 -08:00
Owncast
284833d6a0 Commit updated API documentation 2024-11-15 20:24:14 +00:00
Gabe Kangas
a566b9c9f1 fix(api): define correct inbound api payloads for external requests. Fixes #3966 2024-11-15 12:22:18 -08:00
Owncast
621aebdd3c Commit updated API documentation 2024-11-14 21:44:33 +00:00
Gabe Kangas
2be188a82f Remove API from incorrectly listed as external. For #4015 2024-11-14 13:42:45 -08:00
Owncast
e17b443726 Bundle embedded web app 2024-11-14 18:41:37 +00:00
renovate[bot]
dfc934ce84 chore(deps): update dependency knip to v5.36.6 (#4013)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 10:36:35 -08:00
renovate[bot]
512e4dc575 fix(deps): update dependency date-fns to v4 (#4014)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-14 10:36:10 -08:00
Owncast
421cdd0693 Bundle embedded web app 2024-11-13 16:16:31 +00:00
Gabe Kangas
df06d9ad97 fix(deps): revert emoji-mart to v5.2.2 again 2024-11-13 08:10:45 -08:00
Gabe Kangas
7f42981ba7 chore(deps): add slashes to renovate config regex 2024-11-13 08:09:30 -08:00
Owncast
f3029ca782 Bundle embedded web app 2024-11-12 23:00:59 +00:00
renovate[bot]
58657804cb chore(deps): update dependency emoji-mart to v5.6.0 2024-11-12 20:44:59 +00:00
renovate[bot]
d21b10f118 fix(deps): update module github.com/twin/go-away to v1.6.14 2024-11-12 20:07:28 +00:00
renovate[bot]
ae48128441 chore(config): migrate config renovate.json (#4012)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-12 12:05:52 -08:00
Gabe Kangas
b278027b88 chore(deps): ignore golang and go-fed when updating go deps 2024-11-12 08:43:45 -08:00
renovate[bot]
f45a5b16fa fix(deps): update github.com/go-fed/activity digest to b14b50e 2024-11-12 16:28:25 +00:00
Owncast
f3d660853c Bundle embedded web app 2024-11-12 16:26:43 +00:00
Gabe Kangas
aecf7aa9bb fix(deps): do not allow statically assigned versions to auto-update. See #4010 2024-11-12 08:21:04 -08:00
Owncast
37c18ec8ab Bundle embedded web app 2024-11-12 02:11:50 +00:00
renovate[bot]
4c7ccdbbf9 chore(deps): update dependency emoji-mart to v5.6.0 2024-11-11 21:43:11 +00:00
Owncast
a20a9931f7 Bundle embedded web app 2024-11-11 21:42:28 +00:00
Gabe Kangas
b177333ec6 fix(web): hardcode emoji-mart picker version to a working version. Fixes #4010 2024-11-11 13:36:15 -08:00
Owncast
5042c7ced2 Bundle embedded web app 2024-11-11 13:02:23 +00:00
renovate[bot]
71c88c94a3 chore(deps): lock file maintenance (#4008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 12:57:29 +00:00
renovate[bot]
41a6449836 chore(deps): update dependency chromatic to v11.18.0 2024-11-11 01:35:04 +00:00
Owncast
b656f88417 Bundle embedded web app 2024-11-10 22:07:19 +00:00
renovate[bot]
ee5dc3b7c8 chore(deps): update dependency chromatic to v11.17.0 2024-11-10 18:05:15 +00:00
Owncast
1476405609 Bundle embedded web app 2024-11-10 01:42:18 +00:00
renovate[bot]
7830b3765a chore(deps): update dependency emoji-mart to ~5.6.0 2024-11-09 22:24:06 +00:00
Owncast
f9f61876df Bundle embedded web app 2024-11-09 22:23:11 +00:00
Rafael Passos
17d433749c fix: registerUser request body is invalid (#4002)
I found two issues:
1. This `options` object is being passed down to fetchData,
 where it is deconstructed by the "data" attribute, not body.
2. the `data` object is being transformed into JSON downstream,
 thus the stringification done here makes for a string json object only

Signed-off-by: auyer <rafael@rcpassos.me>
2024-11-09 14:18:05 -08:00
Owncast
9989653d00 Bundle embedded web app 2024-11-09 22:13:50 +00:00
heongle
6399df7f9e fix: fix runtime caching error and adjust rules (#3970)
* fix: fix runtime caching error and adjust rules

* fix: add cache config for ts and m3u8

* revert ts and m3u8 cache config to previous behaviour

* remove unused runtime caching config

* remove all runtime caching config

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-09 14:08:45 -08:00
Owncast
99acc19cee Bundle embedded web app 2024-11-09 22:03:32 +00:00
mahmed2000
eca880ac1f Replace picmo with emoji-mart (#4001)
* Add emoji-mart deps

* Change EmojiPicker to use emoji-mart

* Change ChatTextField to work with the emoji-mart data object

* Remove picmo, commit package-lock

* Fix mutant svgs having a size of 0

* Get the custom emojis to show up earlier in the picker

* Set emoji-mart to exact semver. Later versions break custom category sorting.
2024-11-09 13:58:38 -08:00
renovate[bot]
f215809f1d fix(deps): update module golang.org/x/time to v0.8.0 2024-11-09 20:12:15 +00:00
renovate[bot]
77d2bacbad fix(deps): update module golang.org/x/mod to v0.22.0 (#4006)
* fix(deps): update module golang.org/x/mod to v0.22.0

* chore: go mod tidy

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-11-09 12:11:29 -08:00
renovate[bot]
1b1144c6df fix(deps): update module golang.org/x/net to v0.31.0 (#4007)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 15:08:38 +00:00
renovate[bot]
971e3ea092 fix(deps): update module golang.org/x/crypto to v0.29.0 (#4005)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-09 15:07:31 +00:00
Owncast
24e2552e4c Bundle embedded web app 2024-11-09 12:20:28 +00:00
renovate[bot]
56f6aa8add fix(deps): update dependency @codemirror/lang-markdown to v6.3.1 2024-11-09 10:29:09 +00:00
Owncast
de8579bcd6 Bundle embedded web app 2024-11-09 10:28:04 +00:00
renovate[bot]
56ede52346 chore(deps): update dependency knip to v5.36.3 2024-11-09 07:56:46 +00:00
Owncast
8269ae3209 Bundle embedded web app 2024-11-09 07:56:04 +00:00
renovate[bot]
674c9168ec fix(deps): update nextjs monorepo to v14.2.17 2024-11-09 04:45:09 +00:00
Owncast
801e91d2f1 Bundle embedded web app 2024-11-09 04:43:25 +00:00
renovate[bot]
1e1dc0ff49 chore(deps): update dependency cypress to v13.15.2 (#4004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 20:38:38 -08:00
Owncast
fb75f1bd95 Bundle embedded web app 2024-11-09 03:24:44 +00:00
renovate[bot]
af56597341 chore(deps): update dependency mdx-mermaid to v2.0.2 2024-11-09 01:04:32 +00:00
Owncast
214c202552 Bundle embedded web app 2024-11-09 01:01:59 +00:00
renovate[bot]
ad64956027 chore(deps): update dependency @types/node to v22 (#4003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-08 16:57:04 -08:00
renovate[bot]
13d2ad9079 fix(deps): update module github.com/shirou/gopsutil/v4 to v4.24.10 2024-11-08 21:35:24 +00:00
Gabe Kangas
83ef466ee1 fix(ci): check out repo if event_name is schedule 2024-11-08 13:16:13 -08:00
Owncast
a8026c13bc Bundle embedded web app 2024-11-08 19:48:39 +00:00
mahmed2000
d31d2948c3 Change websocket mock from empty object to empty class (#3998) 2024-11-08 11:43:41 -08:00
Owncast
4d68c7b561 Bundle embedded web app 2024-11-08 18:10:46 +00:00
Gabe Kangas
25cd9b6d53 fix(js): log out unused error value 2024-11-08 10:05:53 -08:00
Owncast
ecba3cc003 Bundle embedded web app 2024-11-08 09:09:49 +00:00
renovate[bot]
89dc1776d7 chore(deps): update storybook monorepo to v8.4.2 2024-11-08 07:11:30 +00:00
Owncast
97fcdfd914 Bundle embedded web app 2024-11-08 03:11:13 +00:00
renovate[bot]
0af864ea8f chore(deps): update dependency chromatic to v11.16.5 2024-11-08 01:54:42 +00:00
Owncast
121cd37c8c Bundle embedded web app 2024-11-08 01:53:07 +00:00
renovate[bot]
06c09f12de chore(deps): update typescript-eslint monorepo to v8.13.0 2024-11-07 21:26:36 +00:00
Owncast
d1117494b0 Bundle embedded web app 2024-11-07 21:24:20 +00:00
renovate[bot]
68a903b83a chore(deps): update dependency eslint-plugin-storybook to v0.11.0 2024-11-07 19:41:56 +00:00
Owncast
c8c91506cd Bundle embedded web app 2024-11-07 19:39:56 +00:00
renovate[bot]
a1a497f5c1 chore(deps): update dependency chromatic to v11.16.4 2024-11-07 16:42:28 +00:00
Owncast
219f76d891 Bundle embedded web app 2024-11-06 14:27:12 +00:00
renovate[bot]
ad9b01f442 chore(deps): update dependency @types/node to v20.17.6 2024-11-06 11:06:34 +00:00
renovate[bot]
962d03710d chore(deps): update peter-evans/create-or-update-comment digest to 213b1f9 2024-11-06 06:21:32 +00:00
dependabot[bot]
2ebc64b3b1 Bump cookie and cookie-parser in /test/load (#3996)
Bumps [cookie](https://github.com/jshttp/cookie) and [cookie-parser](https://github.com/expressjs/cookie-parser). These dependencies needed to be updated together.

Updates `cookie` from 0.4.1 to 0.7.2
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.4.1...v0.7.2)

Updates `cookie-parser` from 1.4.6 to 1.4.7
- [Release notes](https://github.com/expressjs/cookie-parser/releases)
- [Changelog](https://github.com/expressjs/cookie-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/cookie-parser/compare/1.4.6...1.4.7)

---
updated-dependencies:
- dependency-name: cookie
  dependency-type: indirect
- dependency-name: cookie-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-05 16:07:43 -08:00
Owncast
5ca7249388 Bundle embedded web app 2024-11-05 13:19:38 +00:00
renovate[bot]
50a4224224 fix(deps): update dependency react-hotkeys-hook to v4.6.1 2024-11-05 11:18:07 +00:00
Owncast
ac009f574e Bundle embedded web app 2024-11-05 04:06:00 +00:00
renovate[bot]
b9d8fbffe2 chore(deps): update dependency sass to v1.80.6 2024-11-05 00:20:32 +00:00
Owncast
40b97e6bc1 Bundle embedded web app 2024-11-04 18:14:16 +00:00
renovate[bot]
134093b9d2 chore(deps): update dependency eslint-plugin-storybook to v0.10.2 2024-11-04 16:05:27 +00:00
Owncast
d123a2c020 Bundle embedded web app 2024-11-04 12:45:50 +00:00
renovate[bot]
fbd501f57e chore(deps): lock file maintenance 2024-11-04 12:40:56 +00:00
Owncast
ed7bd001ee Bundle embedded web app 2024-11-04 03:13:29 +00:00
renovate[bot]
83b3a0da5e chore(deps): update dependency @storybook/addon-styling-webpack to v1.0.1 2024-11-04 01:21:15 +00:00
Owncast
e04e079243 Bundle embedded web app 2024-11-04 01:19:34 +00:00
renovate[bot]
d4fd75b9c7 chore(deps): update dependency knip to v5.36.0 2024-11-03 21:52:49 +00:00
Owncast
6fbb3b9565 Bundle embedded web app 2024-11-03 21:51:32 +00:00
renovate[bot]
42005608fa chore(deps): update dependency @types/node to v20.17.5 2024-11-03 18:37:38 +00:00
Owncast
538ebaf7bb Bundle embedded web app 2024-11-03 16:22:21 +00:00
renovate[bot]
2be0aebd6d chore(deps): update storybook monorepo to v8.4.0 2024-11-03 13:37:37 +00:00
Owncast
fdaf25d92e Bundle embedded web app 2024-11-03 09:54:53 +00:00
renovate[bot]
5fc4e56bf9 chore(deps): update dependency @types/node to v20.17.4 2024-11-03 07:52:34 +00:00
Owncast
392e437b9a Bundle embedded web app 2024-11-02 19:28:48 +00:00
renovate[bot]
3066d6b82e chore(deps): update dependency mermaid to v11.4.0 2024-11-02 16:46:03 +00:00
Owncast
2f941be5c0 Bundle embedded web app 2024-11-02 16:44:41 +00:00
renovate[bot]
ff058ca777 chore(deps): update dependency knip to v5.34.4 2024-11-02 13:57:16 +00:00
Owncast
3f321d3a1c Bundle embedded web app 2024-11-02 09:46:18 +00:00
renovate[bot]
490c38bd31 fix(deps): update workbox monorepo to v7.3.0 2024-11-02 07:41:14 +00:00
Owncast
019cdb169c Bundle embedded web app 2024-11-02 07:40:21 +00:00
renovate[bot]
9d0c7c1edb chore(deps): update dependency chromatic to v11.16.3 2024-11-02 05:09:06 +00:00
Owncast
88a96f0eae Bundle embedded web app 2024-11-02 05:07:54 +00:00
renovate[bot]
b7d8a5ea99 chore(deps): update dependency sass to v1.80.5 2024-11-02 01:35:53 +00:00
Owncast
5d8c6348bc Bundle embedded web app 2024-11-02 01:34:40 +00:00
renovate[bot]
a934b89a53 chore(deps): update dependency knip to v5.34.3 2024-11-01 22:05:10 +00:00
Owncast
9ccf19eb64 Bundle embedded web app 2024-11-01 22:03:57 +00:00
renovate[bot]
de3eac2b2f chore(deps): update dependency @types/node to v20.17.3 2024-11-01 19:23:53 +00:00
Owncast
76abc0bbfb Bundle embedded web app 2024-11-01 19:22:57 +00:00
renovate[bot]
3bb2ba18e5 chore(deps): update typescript-eslint monorepo to v8.12.2 2024-11-01 16:18:21 +00:00
Owncast
e71144a68d Bundle embedded web app 2024-11-01 03:07:33 +00:00
renovate[bot]
7a367a933b chore(deps): update typescript-eslint monorepo to v8.12.1 2024-11-01 01:12:00 +00:00
Owncast
c1cb96fa57 Bundle embedded web app 2024-10-31 22:35:12 +00:00
renovate[bot]
28c6947a43 chore(deps): update dependency @types/node to v20.17.2 2024-10-31 20:48:45 +00:00
Owncast
00b9dec218 Bundle embedded web app 2024-10-31 05:20:24 +00:00
nekojanai
8e89dfb345 fix: username wrapping (#3975) 2024-10-30 22:13:28 -07:00
renovate[bot]
ed21911288 chore(deps): update peter-evans/create-or-update-comment digest to 6f2ce0e 2024-10-30 08:31:18 +00:00
Owncast
d9aeb32852 Bundle embedded web app 2024-10-29 06:34:21 +00:00
renovate[bot]
3262d3b875 chore(deps): update dependency eslint-plugin-jsx-a11y to v6.10.2 2024-10-29 05:04:08 +00:00
Owncast
af88ee386b Bundle embedded web app 2024-10-29 05:03:00 +00:00
dependabot[bot]
50f39284ce Bump elliptic from 6.5.7 to 6.6.0 in /web (#3990)
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-28 21:58:03 -07:00
Owncast
9c36e196b7 Bundle embedded web app 2024-10-29 02:01:49 +00:00
renovate[bot]
274c85ac2b chore(deps): lock file maintenance 2024-10-29 01:56:42 +00:00
Owncast
5eaa06cf15 Bundle embedded web app 2024-10-28 21:46:57 +00:00
renovate[bot]
86c20e0a39 chore(deps): update dependency chromatic to v11.16.1 2024-10-28 18:52:58 +00:00
Owncast
90a8ebbfa4 Bundle embedded web app 2024-10-28 18:50:58 +00:00
renovate[bot]
e995836ee4 chore(deps): update dependency @babel/core to v7.26.0 2024-10-28 15:58:07 +00:00
241 changed files with 48070 additions and 45559 deletions

View File

@@ -11,7 +11,7 @@ jobs:
issues: write
steps:
- name: Add comment
uses: peter-evans/create-or-update-comment@d5aa8cd1ea450824d5ae23e44f55efc071b98b44
uses: peter-evans/create-or-update-comment@7157823c0f1cb7170b464dc3ffb1555a01ce94c3
with:
issue-number: ${{ github.event.issue.number }}
body: |

View File

@@ -45,7 +45,7 @@ jobs:
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
if: github.event_name == 'push' || github.event_name == 'schedule'
- name: Build and push
if: ${{ github.event_name == 'schedule' && env.GH_CR_PAT != null }}

View File

@@ -6,6 +6,7 @@ import (
"github.com/owncast/owncast/activitypub/outbox"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/workerpool"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
@@ -14,15 +15,16 @@ import (
// Start will initialize and start the federation support.
func Start(datastore *data.Datastore) {
configRepository := configrepository.Get()
persistence.Setup(datastore)
workerpool.InitOutboundWorkerPool()
inbox.InitInboxWorkerPool()
// Generate the keys for signing federated activity if needed.
if data.GetPrivateKey() == "" {
if configRepository.GetPrivateKey() == "" {
privateKey, publicKey, err := crypto.GenerateKeys()
_ = data.SetPrivateKey(string(privateKey))
_ = data.SetPublicKey(string(publicKey))
_ = configRepository.SetPrivateKey(string(privateKey))
_ = configRepository.SetPublicKey(string(publicKey))
if err != nil {
log.Errorln("Unable to get private key", err)
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
// PrivacyAudience represents the audience for an activity.
@@ -87,8 +87,10 @@ func MakeActivityDirect(activity vocab.ActivityStreamsCreate, toIRI *url.URL) vo
// MakeActivityPublic sets the required properties to make this activity
// seen as public.
func MakeActivityPublic(activity vocab.ActivityStreamsCreate) vocab.ActivityStreamsCreate {
configRepository := configrepository.Get()
// TO the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() {
if !configRepository.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC)
to := streams.NewActivityStreamsToProperty()
@@ -121,7 +123,9 @@ func MakeUpdateActivity(activityID *url.URL) vocab.ActivityStreamsUpdate {
activity.SetJSONLDId(id)
// CC the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() {
configRepository := configrepository.Get()
if !configRepository.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC)
cc := streams.NewActivityStreamsCcProperty()
cc.AppendIRI(public)

View File

@@ -9,8 +9,8 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -101,11 +101,13 @@ func MakeActorPropertyWithID(idIRI *url.URL) vocab.ActivityStreamsActorProperty
// MakeServiceForAccount will create a new local actor service with the the provided username.
func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
configRepository := configrepository.Get()
actorIRI := MakeLocalIRIForAccount(accountName)
person := streams.NewActivityStreamsService()
nameProperty := streams.NewActivityStreamsNameProperty()
nameProperty.AppendXMLSchemaString(data.GetServerName())
nameProperty.AppendXMLSchemaString(configRepository.GetServerName())
person.SetActivityStreamsName(nameProperty)
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
@@ -119,7 +121,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
person.SetActivityStreamsInbox(inboxProp)
needsFollowApprovalProperty := streams.NewActivityStreamsManuallyApprovesFollowersProperty()
needsFollowApprovalProperty.Set(data.GetFederationIsPrivate())
needsFollowApprovalProperty.Set(configRepository.GetFederationIsPrivate())
person.SetActivityStreamsManuallyApprovesFollowers(needsFollowApprovalProperty)
outboxIRI := MakeLocalIRIForResource("/user/" + accountName + "/outbox")
@@ -152,7 +154,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKeyType)
person.SetW3IDSecurityV1PublicKey(publicKeyProp)
if t, err := data.GetServerInitTime(); t != nil {
if t, err := configRepository.GetServerInitTime(); t != nil {
publishedDateProp := streams.NewActivityStreamsPublishedProperty()
publishedDateProp.Set(t.Time)
person.SetActivityStreamsPublished(publishedDateProp)
@@ -163,8 +165,8 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Profile properties
// Avatar
uniquenessString := data.GetLogoUniquenessString()
userAvatarURLString := data.GetServerURL() + "/logo/external"
uniquenessString := configRepository.GetLogoUniquenessString()
userAvatarURLString := configRepository.GetServerURL() + "/logo/external"
userAvatarURL, err := url.Parse(userAvatarURLString)
userAvatarURL.RawQuery = "uc=" + uniquenessString
if err != nil {
@@ -195,14 +197,14 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Profile bio
summaryProperty := streams.NewActivityStreamsSummaryProperty()
summaryProperty.AppendXMLSchemaString(data.GetServerSummary())
summaryProperty.AppendXMLSchemaString(configRepository.GetServerSummary())
person.SetActivityStreamsSummary(summaryProperty)
// Links
if serverURL := data.GetServerURL(); serverURL != "" {
if serverURL := configRepository.GetServerURL(); serverURL != "" {
addMetadataLinkToProfile(person, "Stream", serverURL)
}
for _, link := range data.GetSocialHandles() {
for _, link := range configRepository.GetSocialHandles() {
addMetadataLinkToProfile(person, link.Platform, link.URL)
}
@@ -220,7 +222,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Tags
tagProp := streams.NewActivityStreamsTagProperty()
for _, tagString := range data.GetServerMetadataTags() {
for _, tagString := range configRepository.GetServerMetadataTags() {
hashtag := MakeHashtag(tagString)
tagProp.AppendTootHashtag(hashtag)
}
@@ -229,7 +231,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Work around an issue where a single attachment will not serialize
// as an array, so add another item to the mix.
if len(data.GetSocialHandles()) == 1 {
if len(configRepository.GetSocialHandles()) == 1 {
addMetadataLinkToProfile(person, "Owncast", "https://owncast.online")
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func makeFakeService() vocab.ActivityStreamsService {
@@ -55,9 +56,11 @@ func TestMain(m *testing.M) {
if err != nil {
panic(err)
}
data.SetupPersistence(dbFile.Name())
data.SetServerURL("https://my.cool.site.biz")
configRepository := configrepository.Get()
configRepository.SetServerURL("https://my.cool.site.biz")
m.Run()
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -27,7 +27,9 @@ func MakeRemoteIRIForResource(resourcePath string, host string) (*url.URL, error
// MakeLocalIRIForResource will create an IRI for the local server.
func MakeLocalIRIForResource(resourcePath string) *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI url", host, err)
@@ -41,7 +43,9 @@ func MakeLocalIRIForResource(resourcePath string) *url.URL {
// MakeLocalIRIForAccount will return a full IRI for the local server account username.
func MakeLocalIRIForAccount(account string) *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI account server url", err)
@@ -64,7 +68,9 @@ func Serialize(obj vocab.Type) ([]byte, error) {
// MakeLocalIRIForStreamURL will return a full IRI for the local server stream url.
func MakeLocalIRIForStreamURL() *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI stream url", err)
@@ -78,7 +84,9 @@ func MakeLocalIRIForStreamURL() *url.URL {
// MakeLocalIRIforLogo will return a full IRI for the local server logo.
func MakeLocalIRIforLogo() *url.URL {
host := data.GetServerURL()
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI stream url", err)
@@ -93,7 +101,9 @@ func MakeLocalIRIforLogo() *url.URL {
// GetLogoType will return the rel value for the webfinger response and
// the default static image is of type png.
func GetLogoType() string {
imageFilename := data.GetLogoPath()
configRepository := configrepository.Get()
imageFilename := configRepository.GetLogoPath()
if imageFilename == "" {
return "image/png"
}

View File

@@ -9,12 +9,14 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
// ActorHandler handles requests for a single actor.
func ActorHandler(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -22,7 +24,7 @@ func ActorHandler(w http.ResponseWriter, r *http.Request) {
pathComponents := strings.Split(r.URL.Path, "/")
accountName := pathComponents[3]
if _, valid := data.GetFederatedInboxMap()[accountName]; !valid {
if _, valid := configRepository.GetFederatedInboxMap()[accountName]; !valid {
// User is not valid
w.WriteHeader(http.StatusNotFound)
return

View File

@@ -16,7 +16,7 @@ import (
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
const (
@@ -145,7 +145,9 @@ func getFollowersPage(page string, r *http.Request) (vocab.ActivityStreamsOrdere
}
func createPageURL(r *http.Request, page *string) (*url.URL, error) {
domain := data.GetServerURL()
configRepository := configrepository.Get()
domain := configRepository.GetServerURL()
if domain == "" {
return nil, errors.New("unable to get server URL")
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/inbox"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -22,7 +22,9 @@ func InboxHandler(w http.ResponseWriter, r *http.Request) {
}
func acceptInboxRequest(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -39,7 +41,7 @@ func acceptInboxRequest(w http.ResponseWriter, r *http.Request) {
// The account this request is for must match the account name we have set
// for federation.
if forLocalAccount != data.GetFederationUsername() {
if forLocalAccount != configRepository.GetFederationUsername() {
w.WriteHeader(http.StatusNotFound)
return
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -25,12 +25,14 @@ func NodeInfoController(w http.ResponseWriter, r *http.Request) {
Links []links `json:"links"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -89,7 +91,9 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) {
Metadata metadata `json:"metadata"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -117,7 +121,7 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) {
OpenRegistrations: false,
Protocols: []string{"activitypub"},
Metadata: metadata{
ChatEnabled: !data.GetChatDisabled(),
ChatEnabled: !configRepository.GetChatDisabled(),
},
}
@@ -163,12 +167,14 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) {
OpenRegistrations bool `json:"openRegistrations"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -178,7 +184,7 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) {
res := &response{
Organization: Organization{
Name: data.GetServerName(),
Name: configRepository.GetServerName(),
Contact: serverURL,
},
Server: Server{
@@ -232,12 +238,14 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
InvitesEnabled bool `json:"invites_enabled"`
}
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -254,9 +262,9 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
res := response{
URI: serverURL,
Title: data.GetServerName(),
ShortDescription: data.GetServerSummary(),
Description: data.GetServerSummary(),
Title: configRepository.GetServerName(),
ShortDescription: configRepository.GetServerSummary(),
Description: configRepository.GetServerSummary(),
Version: config.GetReleaseString(),
Stats: Stats{
UserCount: 1,
@@ -275,7 +283,9 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
}
func writeResponse(payload interface{}, w http.ResponseWriter) error {
accountName := data.GetDefaultFederationUsername()
configRepository := configrepository.Get()
accountName := configRepository.GetDefaultFederationUsername()
actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
publicKey := crypto.GetPublicKey(actorIRI)
@@ -284,13 +294,15 @@ func writeResponse(payload interface{}, w http.ResponseWriter) error {
// HostMetaController points to webfinger.
func HostMetaController(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
log.Debugln("host meta request rejected! Federation is not enabled")
return
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return

View File

@@ -8,31 +8,33 @@ import (
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
// ObjectHandler handles requests for a single federated ActivityPub object.
func ObjectHandler(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// If private federation mode is enabled do not allow access to objects.
if data.GetFederationIsPrivate() {
if configRepository.GetFederationIsPrivate() {
w.WriteHeader(http.StatusNotFound)
return
}
iri := strings.Join([]string{strings.TrimSuffix(data.GetServerURL(), "/"), r.URL.Path}, "")
iri := strings.Join([]string{strings.TrimSuffix(configRepository.GetServerURL(), "/"), r.URL.Path}, "")
object, _, _, err := persistence.GetObjectByIRI(iri)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
accountName := data.GetDefaultFederationUsername()
accountName := configRepository.GetDefaultFederationUsername()
actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
publicKey := crypto.GetPublicKey(actorIRI)

View File

@@ -6,20 +6,22 @@ import (
"strings"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
// WebfingerHandler will handle webfinger lookup requests.
func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
if !data.GetFederationEnabled() {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
log.Debugln("webfinger request rejected! Federation is not enabled")
return
}
instanceHostURL := data.GetServerURL()
instanceHostURL := configRepository.GetServerURL()
if instanceHostURL == "" {
w.WriteHeader(http.StatusNotFound)
log.Warnln("webfinger request rejected! Federation is enabled but server URL is empty.")
@@ -29,7 +31,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
instanceHostString := utils.GetHostnameFromURLString(instanceHostURL)
if instanceHostString == "" {
w.WriteHeader(http.StatusNotFound)
log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + data.GetServerURL())
log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + configRepository.GetServerURL())
return
}
@@ -51,7 +53,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
host := userComponents[1]
user := userComponents[0]
if _, valid := data.GetFederatedInboxMap()[user]; !valid {
if _, valid := configRepository.GetFederatedInboxMap()[user]; !valid {
w.WriteHeader(http.StatusNotFound)
log.Debugln("webfinger request rejected! Invalid user: " + user)
return

View File

@@ -8,13 +8,15 @@ import (
"errors"
"net/url"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
// GetPublicKey will return the public key for the provided actor.
func GetPublicKey(actorIRI *url.URL) PublicKey {
key := data.GetPublicKey()
configRepository := configrepository.Get()
key := configRepository.GetPublicKey()
idURL, err := url.Parse(actorIRI.String() + "#main-key")
if err != nil {
log.Errorln("unable to parse actor iri string", idURL, err)
@@ -29,7 +31,9 @@ func GetPublicKey(actorIRI *url.URL) PublicKey {
// GetPrivateKey will return the internal server private key.
func GetPrivateKey() *rsa.PrivateKey {
key := data.GetPrivateKey()
configRepository := configrepository.Get()
key := configRepository.GetPrivateKey()
block, _ := pem.Decode([]byte(key))
if block == nil {

View File

@@ -7,17 +7,19 @@ import (
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func handleEngagementActivity(eventType events.EventType, isLiveNotification bool, actorReference vocab.ActivityStreamsActorProperty, action string) error {
configRepository := configrepository.Get()
// Do nothing if displaying engagement actions has been turned off.
if !data.GetFederationShowEngagement() {
if !configRepository.GetFederationShowEngagement() {
return nil
}
// Do nothing if chat is disabled
if data.GetChatDisabled() {
if configRepository.GetChatDisabled() {
return nil
}
@@ -36,11 +38,11 @@ func handleEngagementActivity(eventType events.EventType, isLiveNotification boo
if isLiveNotification && action == events.FediverseEngagementLike {
suffix = "liked that this stream went live."
} else if action == events.FediverseEngagementLike {
suffix = fmt.Sprintf("liked a post from %s.", data.GetServerName())
suffix = fmt.Sprintf("liked a post from %s.", configRepository.GetServerName())
} else if isLiveNotification && action == events.FediverseEngagementRepost {
suffix = "shared this stream with their followers."
} else if action == events.FediverseEngagementRepost {
suffix = fmt.Sprintf("shared a post from %s.", data.GetServerName())
suffix = fmt.Sprintf("shared a post from %s.", configRepository.GetServerName())
} else if action == events.FediverseEngagementFollow {
suffix = "followed this stream."
} else {

View File

@@ -10,13 +10,15 @@ import (
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsFollow) error {
configRepository := configrepository.Get()
follow, err := resolvers.MakeFollowRequest(c, activity)
if err != nil {
log.Errorln("unable to create follow inbox request", err)
@@ -27,7 +29,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
return fmt.Errorf("unable to handle request")
}
approved := !data.GetFederationIsPrivate()
approved := !configRepository.GetFederationIsPrivate()
followRequest := *follow
@@ -36,7 +38,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
return err
}
localAccountName := data.GetDefaultFederationUsername()
localAccountName := configRepository.GetDefaultFederationUsername()
if approved {
if err := requests.SendFollowAccept(follow.Inbox, activity, localAccountName); err != nil {

View File

@@ -15,7 +15,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -131,7 +131,9 @@ func Verify(request *http.Request) (bool, error) {
}
func isBlockedDomain(domain string) bool {
blockedDomains := data.GetBlockedFederatedDomains()
configRepository := configrepository.Get()
blockedDomains := configRepository.GetBlockedFederatedDomains()
for _, blockedDomain := range blockedDomains {
if strings.Contains(domain, blockedDomain) {

View File

@@ -9,6 +9,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func makeFakePerson() vocab.ActivityStreamsPerson {
@@ -49,21 +50,24 @@ func makeFakePerson() vocab.ActivityStreamsPerson {
func TestMain(m *testing.M) {
data.SetupPersistence(":memory:")
data.SetServerURL("https://my.cool.site.biz")
configRepository := configrepository.Get()
configRepository.SetServerURL("https://my.cool.site.biz")
persistence.Setup(data.GetDatastore())
m.Run()
}
func TestBlockedDomains(t *testing.T) {
configRepository := configrepository.Get()
person := makeFakePerson()
data.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"})
configRepository.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"})
if len(data.GetBlockedFederatedDomains()) != 2 {
if len(configRepository.GetBlockedFederatedDomains()) != 2 {
t.Error("Blocked federated domains is not set correctly")
}
for _, domain := range data.GetBlockedFederatedDomains() {
for _, domain := range configRepository.GetBlockedFederatedDomains() {
if domain == person.GetJSONLDId().GetIRI().Host {
return
}

View File

@@ -16,10 +16,10 @@ import (
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/activitypub/webfinger"
"github.com/owncast/owncast/activitypub/workerpool"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
"github.com/teris-io/shortid"
@@ -27,7 +27,9 @@ import (
// SendLive will send all followers the message saying you started a live stream.
func SendLive() error {
textContent := data.GetFederationGoLiveMessage()
configRepository := configrepository.Get()
textContent := configRepository.GetFederationGoLiveMessage()
// If the message is empty then do not send it.
if textContent == "" {
@@ -38,7 +40,7 @@ func SendLive() error {
reg := regexp.MustCompile("[^a-zA-Z0-9]+")
tagProp := streams.NewActivityStreamsTagProperty()
for _, tagString := range data.GetServerMetadataTags() {
for _, tagString := range configRepository.GetServerMetadataTags() {
tagWithoutSpecialCharacters := reg.ReplaceAllString(tagString, "")
hashtag := apmodels.MakeHashtag(tagWithoutSpecialCharacters)
tagProp.AppendTootHashtag(hashtag)
@@ -57,15 +59,15 @@ func SendLive() error {
tagsString := strings.Join(tagStrings, " ")
var streamTitle string
if title := data.GetStreamTitle(); title != "" {
if title := configRepository.GetStreamTitle(); title != "" {
streamTitle = fmt.Sprintf("<p>%s</p>", title)
}
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", textContent, streamTitle, tagsString, data.GetServerURL(), data.GetServerURL())
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", textContent, streamTitle, tagsString, configRepository.GetServerURL(), configRepository.GetServerURL())
activity, _, note, noteID := createBaseOutboundMessage(textContent)
// To the public if we're not treating ActivityPub as "private".
if !data.GetFederationIsPrivate() {
if !configRepository.GetFederationIsPrivate() {
note = apmodels.MakeNotePublic(note)
activity = apmodels.MakeActivityPublic(activity)
}
@@ -73,7 +75,7 @@ func SendLive() error {
note.SetActivityStreamsTag(tagProp)
// Attach an image along with the Federated message.
previewURL, err := url.Parse(data.GetServerURL())
previewURL, err := url.Parse(configRepository.GetServerURL())
if err == nil {
var imageToAttach string
var mediaType string
@@ -94,7 +96,7 @@ func SendLive() error {
}
}
if data.GetNSFW() {
if configRepository.GetNSFW() {
// Mark content as sensitive.
sensitive := streams.NewActivityStreamsSensitiveProperty()
sensitive.AppendXMLSchemaBoolean(true)
@@ -151,6 +153,8 @@ func SendDirectMessageToAccount(textContent, account string) error {
// SendPublicMessage will send a public message to all followers.
func SendPublicMessage(textContent string) error {
configRepository := configrepository.Get()
originalContent := textContent
textContent = utils.RenderSimpleMarkdown(textContent)
@@ -173,7 +177,7 @@ func SendPublicMessage(textContent string) error {
activity, _, note, noteID := createBaseOutboundMessage(textContent)
note.SetActivityStreamsTag(tagProp)
if !data.GetFederationIsPrivate() {
if !configRepository.GetFederationIsPrivate() {
note = apmodels.MakeNotePublic(note)
activity = apmodels.MakeActivityPublic(activity)
}
@@ -197,7 +201,8 @@ func SendPublicMessage(textContent string) error {
// nolint: unparam
func createBaseOutboundMessage(textContent string) (vocab.ActivityStreamsCreate, string, vocab.ActivityStreamsNote, string) {
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
noteID := shortid.MustGenerate()
noteIRI := apmodels.MakeLocalIRIForResource(noteID)
id := shortid.MustGenerate()
@@ -218,7 +223,8 @@ func getHashtagLinkHTMLFromTagString(baseHashtag string) string {
// SendToFollowers will send an arbitrary payload to all follower inboxes.
func SendToFollowers(payload []byte) error {
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
followers, _, err := persistence.GetFederationFollowers(-1, 0)
if err != nil {
@@ -241,7 +247,8 @@ func SendToFollowers(payload []byte) error {
// SendToUser will send a payload to a single specific inbox.
func SendToUser(inbox *url.URL, payload []byte) error {
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
req, err := requests.CreateSignedRequest(payload, inbox, localActor)
if err != nil {
@@ -255,8 +262,10 @@ func SendToUser(inbox *url.URL, payload []byte) error {
// UpdateFollowersWithAccountUpdates will send an update to all followers alerting of a profile update.
func UpdateFollowersWithAccountUpdates() error {
configRepository := configrepository.Get()
// Don't do anything if federation is disabled.
if !data.GetFederationEnabled() {
if !configRepository.GetFederationEnabled() {
return nil
}
@@ -265,7 +274,7 @@ func UpdateFollowersWithAccountUpdates() error {
activity := apmodels.MakeUpdateActivity(objectID)
actor := streams.NewActivityStreamsPerson()
actorID := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
actorID := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
actorIDProperty := streams.NewJSONLDIdProperty()
actorIDProperty.Set(actorID)
actor.SetJSONLDId(actorIDProperty)

View File

@@ -10,7 +10,7 @@ import (
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
@@ -47,11 +47,12 @@ func Resolve(c context.Context, data []byte, callbacks ...interface{}) error {
// ResolveIRI will resolve an IRI ahd call the correct callback for the resolved type.
func ResolveIRI(c context.Context, iri string, callbacks ...interface{}) error {
configRepository := configrepository.Get()
log.Debugln("Resolving", iri)
req, _ := http.NewRequest(http.MethodGet, iri, nil)
actor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
actor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
if err := crypto.SignRequest(req, nil, actor); err != nil {
return err
}

View File

@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@@ -47,6 +47,8 @@ func setupExpiredRequestPruner() {
// StartAuthFlow will begin the IndieAuth flow by generating an auth request.
func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL, error) {
configRepository := configrepository.Get()
// Limit the number of pending requests
if len(pendingAuthRequests) >= maxPendingRequests {
return nil, errors.New("Please try again later. Too many pending requests.")
@@ -68,7 +70,7 @@ func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL,
return nil, errors.New("only servers secured with https are supported")
}
serverURL := data.GetServerURL()
serverURL := configRepository.GetServerURL()
if serverURL == "" {
return nil, errors.New("Owncast server URL must be set when using auth")
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
"github.com/teris-io/shortid"
)
@@ -70,6 +70,8 @@ func StartServerAuth(clientID, redirectURI, codeChallenge, state, me string) (*S
// CompleteServerAuth will verify that the values provided in the final step
// of the IndieAuth flow are correct, and return some basic profile info.
func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string) (*ServerProfileResponse, error) {
configRepository := configrepository.Get()
request, pending := pendingServerAuthRequests[code]
if !pending {
return nil, errors.New("no pending authentication request")
@@ -89,11 +91,11 @@ func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string)
}
response := ServerProfileResponse{
Me: data.GetServerURL(),
Me: configRepository.GetServerURL(),
Profile: ServerProfile{
Name: data.GetServerName(),
URL: data.GetServerURL(),
Photo: fmt.Sprintf("%s/%s", data.GetServerURL(), data.GetLogoPath()),
Name: configRepository.GetServerName(),
URL: configRepository.GetServerURL(),
Photo: fmt.Sprintf("%s/%s", configRepository.GetServerURL(), configRepository.GetLogoPath()),
},
}

View File

@@ -7,8 +7,8 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
log "github.com/sirupsen/logrus"
@@ -23,6 +23,8 @@ var (
func Start(getStatusFunc func() models.Status) error {
setupPersistence()
configRepository := configrepository.Get()
getStatus = getStatusFunc
_server = NewChat()
@@ -35,7 +37,7 @@ func Start(getStatusFunc func() models.Status) error {
Help: "The number of chat messages incremented over time.",
ConstLabels: map[string]string{
"version": config.VersionNumber,
"host": data.GetServerURL(),
"host": configRepository.GetServerURL(),
},
})

View File

@@ -13,8 +13,8 @@ import (
"github.com/gorilla/websocket"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/services/geoip"
)
@@ -133,7 +133,9 @@ func (c *Client) readPump() {
}
// Check if this message passes the optional language filter
if data.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) {
configRepository := configrepository.Get()
if configRepository.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) {
c.sendAction("Sorry, that message contained language that is not allowed in this chat.")
continue
}
@@ -209,9 +211,11 @@ func (c *Client) close() {
}
func (c *Client) passesRateLimit() bool {
configRepository := configrepository.Get()
// If spam rate limiting is disabled, or the user is a moderator, always
// allow the message.
if !data.GetChatSpamProtectionEnabled() || c.User.IsModerator() {
if !configRepository.GetChatSpamProtectionEnabled() || c.User.IsModerator() {
return true
}

View File

@@ -8,8 +8,8 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
@@ -22,10 +22,12 @@ func (s *Server) userNameChanged(eventData chatClientEvent) {
return
}
configRepository := configrepository.Get()
proposedUsername := receivedEvent.NewName
// Check if name is on the blocklist
blocklist := data.GetForbiddenUsernameList()
blocklist := configRepository.GetForbiddenUsernameList()
// Names have a max length
proposedUsername = utils.MakeSafeStringOfLength(proposedUsername, config.MaxChatDisplayNameLength)

View File

@@ -1,6 +1,8 @@
package events
import "github.com/owncast/owncast/core/data"
import (
"github.com/owncast/owncast/persistence/configrepository"
)
// FediverseEngagementEvent is a message displayed in chat on representing an action on the Fediverse.
type FediverseEngagementEvent struct {
@@ -13,6 +15,8 @@ type FediverseEngagementEvent struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload {
configRepository := configrepository.Get()
return EventPayload{
"id": e.ID,
"timestamp": e.Timestamp,
@@ -22,7 +26,7 @@ func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload {
"title": e.UserAccountName,
"link": e.Link,
"user": EventPayload{
"displayName": data.GetServerName(),
"displayName": configRepository.GetServerName(),
},
}
}

View File

@@ -1,6 +1,8 @@
package events
import "github.com/owncast/owncast/core/data"
import (
"github.com/owncast/owncast/persistence/configrepository"
)
// SystemMessageEvent is a message displayed in chat on behalf of the server.
type SystemMessageEvent struct {
@@ -10,13 +12,15 @@ type SystemMessageEvent struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload {
configRepository := configrepository.Get()
return EventPayload{
"id": e.ID,
"timestamp": e.Timestamp,
"body": e.Body,
"type": SystemMessageSent,
"user": EventPayload{
"displayName": data.GetServerName(),
"displayName": configRepository.GetServerName(),
},
}
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/authrepository"
"github.com/owncast/owncast/persistence/tables"
log "github.com/sirupsen/logrus"
@@ -24,7 +25,9 @@ const (
func setupPersistence() {
_datastore = data.GetDatastore()
tables.CreateMessagesTable(_datastore.DB)
data.CreateBanIPTable(_datastore.DB)
authRepository := authrepository.Get()
authRepository.CreateBanIPTable(_datastore.DB)
chatDataPruner := time.NewTicker(5 * time.Minute)
go func() {

View File

@@ -13,9 +13,10 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/authrepository"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/services/geoip"
"github.com/owncast/owncast/utils"
@@ -95,7 +96,9 @@ func (s *Server) Addclient(conn *websocket.Conn, user *models.User, accessToken
ConnectedAt: time.Now(),
}
shouldSendJoinedMessages := data.GetChatJoinPartMessagesEnabled()
configRepository := configrepository.Get()
shouldSendJoinedMessages := configRepository.GetChatJoinPartMessagesEnabled()
// If there are existing clients connected for this user do not send
// a user joined message. Do not put this under a mutex, as
@@ -186,8 +189,10 @@ func (s *Server) sendUserPartedMessage(c *Client) {
userPartEvent.User = c.User
userPartEvent.ClientID = c.Id
configRepository := configrepository.Get()
// If part messages are disabled.
if data.GetChatJoinPartMessagesEnabled() {
if configRepository.GetChatJoinPartMessagesEnabled() {
if err := s.Broadcast(userPartEvent.GetBroadcastPayload()); err != nil {
log.Errorln("error sending chat part message", err)
}
@@ -198,14 +203,17 @@ func (s *Server) sendUserPartedMessage(c *Client) {
// HandleClientConnection is fired when a single client connects to the websocket.
func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request) {
if data.GetChatDisabled() {
configRepository := configrepository.Get()
authRepository := authrepository.Get()
if configRepository.GetChatDisabled() {
_, _ = w.Write([]byte(events.ChatDisabled))
return
}
ipAddress := utils.GetIPAddressFromRequest(r)
// Check if this client's IP address is banned. If so send a rejection.
if blocked, err := data.IsIPAddressBanned(ipAddress); blocked {
if blocked, err := authRepository.IsIPAddressBanned(ipAddress); blocked {
log.Debugln("Client ip address has been blocked. Rejecting.")
w.WriteHeader(http.StatusForbidden)
@@ -377,12 +385,14 @@ func SendActionToUser(userID string, text string) error {
}
func (s *Server) eventReceived(event chatClientEvent) {
configRepository := configrepository.Get()
c := event.client
u := c.User
// If established chat user only mode is enabled and the user is not old
// enough then reject this event and send them an informative message.
if u != nil && data.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
if u != nil && configRepository.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
s.sendActionToClient(c, "You have not been an established chat participant long enough to take part in chat. Please enjoy the stream and try again later.")
return
}
@@ -409,10 +419,12 @@ func (s *Server) eventReceived(event chatClientEvent) {
}
func (s *Server) sendWelcomeMessageToClient(c *Client) {
configRepository := configrepository.Get()
// Add an artificial delay so people notice this message come in.
time.Sleep(7 * time.Second)
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage())
welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
if welcomeMessage != "" {
s.sendSystemMessageToClient(c, welcomeMessage)
@@ -420,7 +432,9 @@ func (s *Server) sendWelcomeMessageToClient(c *Client) {
}
func (s *Server) sendAllWelcomeMessage() {
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage())
configRepository := configrepository.Get()
welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
if welcomeMessage != "" {
clientMessage := events.SystemMessageEvent{

View File

@@ -16,6 +16,7 @@ import (
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/utils"
"github.com/owncast/owncast/yp"
@@ -34,10 +35,10 @@ var (
// Start starts up the core processing.
func Start() error {
resetDirectories()
configRepository := configrepository.Get()
// configRepository.PopulateDefaults()
data.PopulateDefaults()
if err := data.VerifySettings(); err != nil {
if err := configRepository.VerifySettings(); err != nil {
log.Error(err)
return err
}
@@ -75,7 +76,7 @@ func Start() error {
// start the rtmp server
go rtmp.Start(setStreamAsConnected, setBroadcaster)
rtmpPort := data.GetRTMPPortNumber()
rtmpPort := configRepository.GetRTMPPortNumber()
if rtmpPort != 1935 {
log.Infof("RTMP is accepting inbound streams on port %d.", rtmpPort)
}
@@ -113,7 +114,8 @@ func transitionToOfflineVideoStreamContent() {
go _transcoder.Start(false)
// Copy the logo to be the thumbnail
logo := data.GetLogoPath()
configRepository := configrepository.Get()
logo := configRepository.GetLogoPath()
dst := filepath.Join(config.TempDir, "thumbnail.jpg")
if err = utils.Copy(filepath.Join("data", logo), dst); err != nil {
log.Warnln(err)
@@ -130,7 +132,8 @@ func resetDirectories() {
utils.CleanupDirectory(config.HLSStoragePath)
// Remove the previous thumbnail
logo := data.GetLogoPath()
configRepository := configrepository.Get()
logo := configRepository.GetLogoPath()
if utils.DoesFileExists(logo) {
err := utils.Copy(path.Join("data", logo), filepath.Join(config.DataDirectory, "thumbnail.jpg"))
if err != nil {

View File

@@ -1,13 +0,0 @@
package data
// GetFederatedInboxMap is a mapping between account names and their outbox.
func GetFederatedInboxMap() map[string]string {
return map[string]string{
GetDefaultFederationUsername(): GetDefaultFederationUsername(),
}
}
// GetDefaultFederationUsername will return the username used for sending federation activities.
func GetDefaultFederationUsername() string {
return GetFederationUsername()
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +0,0 @@
package data
// GetPublicKey will return the public key.
func GetPublicKey() string {
value, _ := _datastore.GetString(publicKeyKey)
return value
}
// SetPublicKey will save the public key.
func SetPublicKey(key string) error {
return _datastore.SetString(publicKeyKey, key)
}
// GetPrivateKey will return the private key.
func GetPrivateKey() string {
value, _ := _datastore.GetString(privateKeyKey)
return value
}
// SetPrivateKey will save the private key.
func SetPrivateKey(key string) error {
return _datastore.SetString(privateKeyKey, key)
}

View File

@@ -4,6 +4,8 @@ import (
"fmt"
"os"
"testing"
"github.com/owncast/owncast/models"
)
func TestMain(m *testing.M) {
@@ -89,7 +91,7 @@ func TestCustomType(t *testing.T) {
}
// Save config entry to the database
if err := _datastore.Save(ConfigEntry{&testStruct, testKey}); err != nil {
if err := _datastore.Save(models.ConfigEntry{&testStruct, testKey}); err != nil {
t.Error(err)
}
@@ -101,7 +103,7 @@ func TestCustomType(t *testing.T) {
// Get a typed struct out of it
var testResult TestStruct
if err := entryResult.getObject(&testResult); err != nil {
if err := entryResult.GetObject(&testResult); err != nil {
t.Error(err)
}
@@ -121,7 +123,7 @@ func TestStringMap(t *testing.T) {
}
// Save config entry to the database
if err := _datastore.Save(ConfigEntry{&testMap, testKey}); err != nil {
if err := _datastore.Save(models.ConfigEntry{Value: &testMap, Key: testKey}); err != nil {
t.Error(err)
}
@@ -131,7 +133,7 @@ func TestStringMap(t *testing.T) {
t.Error(err)
}
testResult, err := entryResult.getStringMap()
testResult, err := entryResult.GetStringMap()
if err != nil {
t.Error(err)
}

View File

@@ -5,12 +5,11 @@ import (
"database/sql"
"encoding/gob"
"sync"
"time"
// sqlite requires a blank import.
_ "github.com/mattn/go-sqlite3"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
@@ -21,7 +20,8 @@ type Datastore struct {
DbLock *sync.Mutex
}
func (ds *Datastore) warmCache() {
// WarmCache pre-caches all configuration values in memory.
func (ds *Datastore) WarmCache() {
log.Traceln("Warming config value cache")
res, err := ds.DB.Query("SELECT key, value FROM datastore")
@@ -46,10 +46,10 @@ func (ds *Datastore) GetQueries() *db.Queries {
}
// Get will query the database for the key and return the entry.
func (ds *Datastore) Get(key string) (ConfigEntry, error) {
func (ds *Datastore) Get(key string) (models.ConfigEntry, error) {
cachedValue, err := ds.GetCachedValue(key)
if err == nil {
return ConfigEntry{
return models.ConfigEntry{
Key: key,
Value: cachedValue,
}, nil
@@ -60,10 +60,10 @@ func (ds *Datastore) Get(key string) (ConfigEntry, error) {
row := ds.DB.QueryRow("SELECT key, value FROM datastore WHERE key = ? LIMIT 1", key)
if err := row.Scan(&resultKey, &resultValue); err != nil {
return ConfigEntry{}, err
return models.ConfigEntry{}, err
}
result := ConfigEntry{
result := models.ConfigEntry{
Key: resultKey,
Value: resultValue,
}
@@ -73,7 +73,7 @@ func (ds *Datastore) Get(key string) (ConfigEntry, error) {
}
// Save will save the ConfigEntry to the database.
func (ds *Datastore) Save(e ConfigEntry) error {
func (ds *Datastore) Save(e models.ConfigEntry) error {
ds.DbLock.Lock()
defer ds.DbLock.Unlock()
@@ -93,7 +93,6 @@ func (ds *Datastore) Save(e ConfigEntry) error {
return err
}
_, err = stmt.Exec(e.Key, dataGob.Bytes())
if err != nil {
return err
}
@@ -121,26 +120,6 @@ func (ds *Datastore) Setup() {
);`
ds.MustExec(createTableSQL)
if !HasPopulatedDefaults() {
PopulateDefaults()
}
if !hasPopulatedFederationDefaults() {
if err := SetFederationGoLiveMessage(config.GetDefaults().FederationGoLiveMessage); err != nil {
log.Errorln(err)
}
if err := _datastore.SetBool("HAS_POPULATED_FEDERATION_DEFAULTS", true); err != nil {
log.Errorln(err)
}
}
// Set the server initialization date if needed.
if hasSetInitDate, _ := GetServerInitTime(); hasSetInitDate == nil || !hasSetInitDate.Valid {
_ = SetServerInitTime(time.Now())
}
migrateDatastoreValues(_datastore)
}
// Reset will delete all config entries in the datastore and start over.
@@ -156,8 +135,6 @@ func (ds *Datastore) Reset() {
if _, err = stmt.Exec(); err != nil {
log.Fatalln(err)
}
PopulateDefaults()
}
// GetDatastore returns the shared instance of the owncast datastore.

View File

@@ -1,54 +0,0 @@
package data
import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/models"
)
// HasPopulatedDefaults will determine if the defaults have been inserted into the database.
func HasPopulatedDefaults() bool {
hasPopulated, err := _datastore.GetBool("HAS_POPULATED_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}
func hasPopulatedFederationDefaults() bool {
hasPopulated, err := _datastore.GetBool("HAS_POPULATED_FEDERATION_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}
// PopulateDefaults will set default values in the database.
func PopulateDefaults() {
_datastore.warmCache()
defaults := config.GetDefaults()
if HasPopulatedDefaults() {
return
}
_ = SetAdminPassword(defaults.AdminPassword)
_ = SetStreamKeys(defaults.StreamKeys)
_ = SetHTTPPortNumber(float64(defaults.WebServerPort))
_ = SetRTMPPortNumber(float64(defaults.RTMPServerPort))
_ = SetLogoPath(defaults.Logo)
_ = SetServerMetadataTags([]string{"owncast", "streaming"})
_ = SetServerSummary(defaults.Summary)
_ = SetServerWelcomeMessage("")
_ = SetServerName(defaults.Name)
_ = SetExtraPageBodyContent(defaults.PageBodyContent)
_ = SetFederationGoLiveMessage(defaults.FederationGoLiveMessage)
_ = SetSocialHandles([]models.SocialHandle{
{
Platform: "github",
URL: "https://github.com/owncast/owncast",
},
})
_ = _datastore.SetBool("HAS_POPULATED_DEFAULTS", true)
}

View File

@@ -1,14 +1,5 @@
package data
import (
"context"
"database/sql"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
// GetMessagesCount will return the number of messages in the database.
func GetMessagesCount() int64 {
query := `SELECT COUNT(*) FROM messages`
@@ -25,58 +16,3 @@ func GetMessagesCount() int64 {
}
return count
}
// CreateBanIPTable will create the IP ban table if needed.
func CreateBanIPTable(db *sql.DB) {
createTableSQL := ` CREATE TABLE IF NOT EXISTS ip_bans (
"ip_address" TEXT NOT NULL PRIMARY KEY,
"notes" TEXT,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`
stmt, err := db.Prepare(createTableSQL)
if err != nil {
log.Fatal("error creating ip ban table", err)
}
defer stmt.Close()
if _, err := stmt.Exec(); err != nil {
log.Fatal("error creating ip ban table", err)
}
}
// BanIPAddress will persist a new IP address ban to the datastore.
func BanIPAddress(address, note string) error {
return _datastore.GetQueries().BanIPAddress(context.Background(), db.BanIPAddressParams{
IpAddress: address,
Notes: sql.NullString{String: note, Valid: true},
})
}
// IsIPAddressBanned will return if an IP address has been previously blocked.
func IsIPAddressBanned(address string) (bool, error) {
blocked, error := _datastore.GetQueries().IsIPAddressBlocked(context.Background(), address)
return blocked > 0, error
}
// GetIPAddressBans will return all the banned IP addresses.
func GetIPAddressBans() ([]models.IPAddress, error) {
result, err := _datastore.GetQueries().GetIPAddressBans(context.Background())
if err != nil {
return nil, err
}
response := []models.IPAddress{}
for _, ip := range result {
response = append(response, models.IPAddress{
IPAddress: ip.IpAddress,
Notes: ip.Notes.String,
CreatedAt: ip.CreatedAt.Time,
})
}
return response, err
}
// RemoveIPAddressBan will remove a previously banned IP address.
func RemoveIPAddressBan(address string) error {
return _datastore.GetQueries().RemoveIPAddressBan(context.Background(), address)
}

View File

@@ -1,17 +1,19 @@
package data
import "github.com/owncast/owncast/models"
// GetStringSlice will return the string slice value for a key.
func (ds *Datastore) GetStringSlice(key string) ([]string, error) {
configEntry, err := ds.Get(key)
if err != nil {
return []string{}, err
}
return configEntry.getStringSlice()
return configEntry.GetStringSlice()
}
// SetStringSlice will set the string slice value for a key.
func (ds *Datastore) SetStringSlice(key string, value []string) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -21,12 +23,12 @@ func (ds *Datastore) GetString(key string) (string, error) {
if err != nil {
return "", err
}
return configEntry.getString()
return configEntry.GetString()
}
// SetString will set the string value for a key.
func (ds *Datastore) SetString(key string, value string) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -36,12 +38,12 @@ func (ds *Datastore) GetNumber(key string) (float64, error) {
if err != nil {
return 0, err
}
return configEntry.getNumber()
return configEntry.GetNumber()
}
// SetNumber will set the numeric value for a key.
func (ds *Datastore) SetNumber(key string, value float64) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -51,12 +53,12 @@ func (ds *Datastore) GetBool(key string) (bool, error) {
if err != nil {
return false, err
}
return configEntry.getBool()
return configEntry.GetBool()
}
// SetBool will set the boolean value for a key.
func (ds *Datastore) SetBool(key string, value bool) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}
@@ -66,11 +68,11 @@ func (ds *Datastore) GetStringMap(key string) (map[string]string, error) {
if err != nil {
return map[string]string{}, err
}
return configEntry.getStringMap()
return configEntry.GetStringMap()
}
// SetStringMap will set the string map value for a key.
func (ds *Datastore) SetStringMap(key string, value map[string]string) error {
configEntry := ConfigEntry{value, key}
configEntry := models.ConfigEntry{Value: value, Key: key}
return ds.Save(configEntry)
}

View File

@@ -12,8 +12,8 @@ import (
"github.com/nareix/joy5/format/rtmp"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
var _hasInboundRTMPConnection = false
@@ -33,7 +33,9 @@ func Start(setStreamAsConnected func(*io.PipeReader), setBroadcaster func(models
_setStreamAsConnected = setStreamAsConnected
_setBroadcaster = setBroadcaster
port := data.GetRTMPPortNumber()
configRepository := configrepository.Get()
port := configRepository.GetRTMPPortNumber()
s := rtmp.NewServer()
var lis net.Listener
var err error
@@ -78,8 +80,10 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) {
return
}
configRepository := configrepository.Get()
accessGranted := false
validStreamingKeys := data.GetStreamKeys()
validStreamingKeys := configRepository.GetStreamKeys()
// If a stream key override was specified then use that instead.
if config.TemporaryStreamKey != "" {

View File

@@ -7,8 +7,8 @@ import (
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/services/geoip"
)
@@ -48,7 +48,8 @@ func IsStreamConnected() bool {
// Kind of a hack. It takes a handful of seconds between a RTMP connection and when HLS data is available.
// So account for that with an artificial buffer of four segments.
timeSinceLastConnected := time.Since(_stats.LastConnectTime.Time).Seconds()
waitTime := math.Max(float64(data.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7)
configRepository := configrepository.Get()
waitTime := math.Max(float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7)
if timeSinceLastConnected < waitTime {
return false
}
@@ -75,7 +76,7 @@ func SetViewerActive(viewer *models.Viewer) {
l.Lock()
defer l.Unlock()
// Asynchronously, optionally, fetch GeoIP data.
// Asynchronously, optionally, fetch GeoIP configRepository.
go func(viewer *models.Viewer) {
viewer.Geo = _geoIPClient.GetGeoFromIP(viewer.IPAddress)
}(viewer)
@@ -111,27 +112,29 @@ func pruneViewerCount() {
}
func saveStats() {
if err := data.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil {
configRepository := configrepository.Get()
if err := configRepository.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil {
log.Errorln("error saving viewer count", err)
}
if err := data.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil {
if err := configRepository.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil {
log.Errorln("error saving viewer count", err)
}
if _stats.LastDisconnectTime != nil && _stats.LastDisconnectTime.Valid {
if err := data.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil {
if err := configRepository.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil {
log.Errorln("error saving disconnect time", err)
}
}
}
func getSavedStats() models.Stats {
savedLastDisconnectTime, _ := data.GetLastDisconnectTime()
configRepository := configrepository.Get()
savedLastDisconnectTime, _ := configRepository.GetLastDisconnectTime()
result := models.Stats{
ChatClients: make(map[string]models.Client),
Viewers: make(map[string]*models.Viewer),
SessionMaxViewerCount: data.GetPeakSessionViewerCount(),
OverallMaxViewerCount: data.GetPeakOverallViewerCount(),
SessionMaxViewerCount: configRepository.GetPeakSessionViewerCount(),
OverallMaxViewerCount: configRepository.GetPeakOverallViewerCount(),
LastDisconnectTime: savedLastDisconnectTime,
}

View File

@@ -2,8 +2,8 @@ package core
import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
// GetStatus gets the status of the system.
@@ -17,6 +17,7 @@ func GetStatus() models.Status {
viewerCount = len(_stats.Viewers)
}
configRepository := configrepository.Get()
return models.Status{
Online: IsStreamConnected(),
ViewerCount: viewerCount,
@@ -25,7 +26,7 @@ func GetStatus() models.Status {
LastDisconnectTime: _stats.LastDisconnectTime,
LastConnectTime: _stats.LastConnectTime,
VersionNumber: config.VersionNumber,
StreamTitle: data.GetStreamTitle(),
StreamTitle: configRepository.GetStreamTitle(),
}
}

View File

@@ -1,12 +1,13 @@
package core
import (
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/storageproviders"
"github.com/owncast/owncast/persistence/configrepository"
)
func setupStorage() error {
s3Config := data.GetS3Config()
configRepository := configrepository.Get()
s3Config := configRepository.GetS3Config()
if s3Config.Enabled {
_storage = storageproviders.NewS3Storage()

View File

@@ -5,9 +5,8 @@ import (
"path/filepath"
"sort"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
)
// LocalStorage represents an instance of the local storage provider for HLS video.
@@ -22,7 +21,8 @@ func NewLocalStorage() *LocalStorage {
// Setup configures this storage provider.
func (s *LocalStorage) Setup() error {
s.host = data.GetVideoServingEndpoint()
configRepository := configrepository.Get()
s.host = configRepository.GetVideoServingEndpoint()
return nil
}
@@ -63,7 +63,8 @@ func (s *LocalStorage) Save(filePath string, retryCount int) (string, error) {
// Cleanup will remove old files from the storage provider.
func (s *LocalStorage) Cleanup() error {
// Determine how many files we should keep on disk
maxNumber := data.GetStreamLatencyLevel().SegmentCount
configRepository := configrepository.Get()
maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount
buffer := 10
return localCleanup(maxNumber + buffer)
}

View File

@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@@ -64,9 +64,9 @@ func NewS3Storage() *S3Storage {
// Setup sets up the s3 storage for saving the video to s3.
func (s *S3Storage) Setup() error {
log.Trace("Setting up S3 for external storage of video...")
s3Config := data.GetS3Config()
customVideoServingEndpoint := data.GetVideoServingEndpoint()
configRepository := configrepository.Get()
s3Config := configRepository.GetS3Config()
customVideoServingEndpoint := configRepository.GetVideoServingEndpoint()
if customVideoServingEndpoint != "" {
s.host = customVideoServingEndpoint
@@ -106,8 +106,9 @@ func (s *S3Storage) SegmentWritten(localFilePath string) {
averagePerformance := utils.GetAveragePerformance(performanceMonitorKey)
// Warn the user about long-running save operations
configRepository := configrepository.Get()
if averagePerformance != 0 {
if averagePerformance > float64(data.GetStreamLatencyLevel().SecondsPerSegment)*0.9 {
if averagePerformance > float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*0.9 {
log.Warnln("Possible slow uploads: average upload S3 save duration", averagePerformance, "s. troubleshoot this issue by visiting https://owncast.online/docs/troubleshooting/")
}
}
@@ -220,7 +221,8 @@ func (s *S3Storage) Cleanup() error {
// RemoteCleanup will remove old files from the remote storage provider.
func (s *S3Storage) RemoteCleanup() error {
// Determine how many files we should keep on S3 storage
maxNumber := data.GetStreamLatencyLevel().SegmentCount
configRepository := configrepository.Get()
maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount
buffer := 20
keys, err := s.getDeletableVideoSegmentsWithOffset(maxNumber + buffer)

View File

@@ -16,6 +16,7 @@ import (
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -39,9 +40,11 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) {
_stats.LastConnectTime = &now
_stats.SessionMaxViewerCount = 0
configRepository := configrepository.Get()
_currentBroadcast = &models.CurrentBroadcast{
LatencyLevel: data.GetStreamLatencyLevel(),
OutputSettings: data.GetStreamOutputVariants(),
LatencyLevel: configRepository.GetStreamLatencyLevel(),
OutputSettings: configRepository.GetStreamOutputVariants(),
}
StopOfflineCleanupTimer()
@@ -69,7 +72,7 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) {
}()
go webhooks.SendStreamStatusEvent(models.StreamStarted)
selectedThumbnailVideoQualityIndex, isVideoPassthrough := data.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings)
selectedThumbnailVideoQualityIndex, isVideoPassthrough := configRepository.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings)
transcoder.StartThumbnailGenerator(segmentPath, selectedThumbnailVideoQualityIndex, isVideoPassthrough)
_ = chat.SendSystemAction("Stay tuned, the stream is **starting**!", true)
@@ -176,8 +179,9 @@ func startLiveStreamNotificationsTimer() context.CancelFunc {
return
}
configRepository := configrepository.Get()
// Send Fediverse message.
if data.GetFederationEnabled() {
if configRepository.GetFederationEnabled() {
log.Traceln("Sending Federated Go Live message.")
if err := activitypub.SendLive(); err != nil {
log.Errorln(err)

View File

@@ -11,7 +11,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -88,9 +88,9 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
if len(names) == 0 {
return nil
}
configRepository := configrepository.Get()
mostRecentFile := path.Join(framePath, names[0])
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg")
thumbnailCmdFlags := []string{
@@ -120,7 +120,8 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
}
func makeAnimatedGifPreview(sourceFile string, outputFile string) {
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
configRepository := configrepository.Get()
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
outputFileTemp := path.Join(config.TempDir, "temppreview.gif")
// Filter is pulled from https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/

View File

@@ -12,9 +12,9 @@ import (
"github.com/teris-io/shortid"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/logging"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -275,15 +275,16 @@ func getVariantFromConfigQuality(quality models.StreamOutputVariant, index int)
// NewTranscoder will return a new Transcoder, populated by the config.
func NewTranscoder() *Transcoder {
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
configRepository := configrepository.Get()
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
transcoder := new(Transcoder)
transcoder.ffmpegPath = ffmpegPath
transcoder.internalListenerPort = config.InternalHLSListenerPort
transcoder.currentStreamOutputSettings = data.GetStreamOutputVariants()
transcoder.currentLatencyLevel = data.GetStreamLatencyLevel()
transcoder.codec = getCodec(data.GetVideoCodec())
transcoder.currentStreamOutputSettings = configRepository.GetStreamOutputVariants()
transcoder.currentLatencyLevel = configRepository.GetStreamLatencyLevel()
transcoder.codec = getCodec(configRepository.GetVideoCodec())
transcoder.segmentOutputPath = config.HLSStoragePath
transcoder.playlistOutputPath = config.HLSStoragePath

View File

@@ -8,7 +8,7 @@ import (
"sync"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
@@ -99,9 +99,9 @@ func handleTranscoderMessage(message string) {
func createVariantDirectories() {
// Create private hls data dirs
utils.CleanupDirectory(config.HLSStoragePath)
if len(data.GetStreamOutputVariants()) != 0 {
for index := range data.GetStreamOutputVariants() {
configRepository := configrepository.Get()
if len(configRepository.GetStreamOutputVariants()) != 0 {
for index := range configRepository.GetStreamOutputVariants() {
if err := os.MkdirAll(path.Join(config.HLSStoragePath, strconv.Itoa(index)), 0o750); err != nil {
log.Fatalln(err)
}

View File

@@ -3,8 +3,8 @@ package webhooks
import (
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/teris-io/shortid"
)
@@ -14,13 +14,15 @@ func SendStreamStatusEvent(eventType models.EventType) {
}
func sendStreamStatusEvent(eventType models.EventType, id string, timestamp time.Time) {
configRepository := configrepository.Get()
SendEventToWebhooks(WebhookEvent{
Type: eventType,
EventData: map[string]interface{}{
"id": id,
"name": data.GetServerName(),
"summary": data.GetServerSummary(),
"streamTitle": data.GetStreamTitle(),
"name": configRepository.GetServerName(),
"summary": configRepository.GetServerSummary(),
"streamTitle": configRepository.GetStreamTitle(),
"status": getStatus(),
"timestamp": timestamp,
},

View File

@@ -5,14 +5,16 @@ import (
"time"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
func TestSendStreamStatusEvent(t *testing.T) {
data.SetServerName("my server")
data.SetServerSummary("my server where I stream")
data.SetStreamTitle("my stream")
configRepository := configrepository.Get()
configRepository.SetServerName("my server")
configRepository.SetServerSummary("my server where I stream")
configRepository.SetStreamTitle("my stream")
checkPayload(t, models.StreamStarted, func() {
sendStreamStatusEvent(events.StreamStarted, "id", time.Unix(72, 6).UTC())

File diff suppressed because one or more lines are too long

24
go.mod
View File

@@ -7,11 +7,11 @@ toolchain go1.23.1
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/SherClockHolmes/webpush-go v1.3.0
github.com/TwiN/go-away v1.6.13
github.com/TwiN/go-away v1.6.14
github.com/andybalholm/cascadia v1.3.2
github.com/aws/aws-sdk-go v1.55.5
github.com/go-chi/chi/v5 v5.1.0
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f
github.com/go-fed/activity v1.0.1-0.20220119073622-b14b50eecad0
github.com/go-fed/httpsig v1.1.0
github.com/gorilla/websocket v1.5.3
github.com/grafov/m3u8 v0.12.0
@@ -28,16 +28,16 @@ require (
github.com/prometheus/client_golang v1.20.5
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/schollz/sqlite3dump v1.3.1
github.com/shirou/gopsutil/v4 v4.24.9
github.com/shirou/gopsutil/v4 v4.24.10
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-emoji v1.0.4
golang.org/x/crypto v0.28.0
golang.org/x/mod v0.21.0
golang.org/x/net v0.30.0
golang.org/x/time v0.7.0
golang.org/x/crypto v0.29.0
golang.org/x/mod v0.22.0
golang.org/x/net v0.31.0
golang.org/x/time v0.8.0
gopkg.in/evanphx/json-patch.v5 v5.9.0
mvdan.cc/xurls/v2 v2.5.0
)
@@ -49,7 +49,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/ebitengine/purego v0.8.0 // indirect
github.com/ebitengine/purego v0.8.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-test/deep v1.0.4 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
@@ -70,9 +70,9 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

62
go.sum
View File

@@ -5,6 +5,8 @@ github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKl
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw=
github.com/TwiN/go-away v1.6.13 h1:aB6l/FPXmA5ds+V7I9zdhxzpsLLUvVtEuS++iU/ZmgE=
github.com/TwiN/go-away v1.6.13/go.mod h1:MpvIC9Li3minq+CGgbgUDvQ9tDaeW35k5IXZrF9MVas=
github.com/TwiN/go-away v1.6.14 h1:gjFP+6/A36gmj0NpYX0Sz9hrdU0KtHwtNWYnsJgV4fo=
github.com/TwiN/go-away v1.6.14/go.mod h1:d+Gv3XuqjIeFqXYuAIzlyNoDzr1vNsP5B/hRY3u/VLs=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
@@ -24,8 +26,8 @@ github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhr
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-fed/httpsig v0.1.1-0.20190914113940-c2de3672e5b5/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE=
@@ -80,8 +82,6 @@ github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR7
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
@@ -111,8 +111,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
@@ -127,8 +125,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI=
github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
github.com/shirou/gopsutil/v4 v4.24.10 h1:7VOzPtfw/5YDU+jLEoBwXwxJbQetULywoSV4RYY7HkM=
github.com/shirou/gopsutil/v4 v4.24.10/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
@@ -145,6 +143,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@@ -156,14 +156,8 @@ github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4=
github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90=
github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
@@ -175,14 +169,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -191,15 +183,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -214,30 +204,26 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

15
main.go
View File

@@ -6,6 +6,7 @@ import (
"strconv"
"github.com/owncast/owncast/logging"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/config"
@@ -111,8 +112,10 @@ func main() {
}
func handleCommandLineFlags() {
configRepository := configrepository.Get()
if *newAdminPassword != "" {
if err := data.SetAdminPassword(*newAdminPassword); err != nil {
if err := configRepository.SetAdminPassword(*newAdminPassword); err != nil {
log.Errorln("Error setting your admin password.", err)
log.Exit(1)
} else {
@@ -134,25 +137,25 @@ func handleCommandLineFlags() {
}
log.Println("Saving new web server port number to", portNumber)
if err := data.SetHTTPPortNumber(float64(portNumber)); err != nil {
if err := configRepository.SetHTTPPortNumber(float64(portNumber)); err != nil {
log.Errorln(err)
}
}
config.WebServerPort = data.GetHTTPPortNumber()
config.WebServerPort = configRepository.GetHTTPPortNumber()
// Set the web server ip
if *webServerIPOverride != "" {
log.Println("Saving new web server listen IP address to", *webServerIPOverride)
if err := data.SetHTTPListenAddress(*webServerIPOverride); err != nil {
if err := configRepository.SetHTTPListenAddress(*webServerIPOverride); err != nil {
log.Errorln(err)
}
}
config.WebServerIP = data.GetHTTPListenAddress()
config.WebServerIP = configRepository.GetHTTPListenAddress()
// Set the rtmp server port
if *rtmpPortOverride > 0 {
log.Println("Saving new RTMP server port number to", *rtmpPortOverride)
if err := data.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil {
if err := configRepository.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil {
log.Errorln(err)
}
}

View File

@@ -5,8 +5,8 @@ import (
"sort"
"github.com/owncast/owncast/core"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -68,8 +68,8 @@ func networkSpeedHealthOverviewMessage() string {
isVideoPassthrough bool
bitrate int
}
outputVariants := data.GetStreamOutputVariants()
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
streamSortVariants := make([]singleVariant, len(outputVariants))
for i, variant := range outputVariants {
@@ -155,7 +155,8 @@ func wastefulBitrateOverviewMessage() string {
return ""
}
outputVariants := data.GetStreamOutputVariants()
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
type singleVariant struct {
isVideoPassthrough bool
@@ -229,7 +230,8 @@ func errorCountHealthOverviewMessage() string {
healthyPercentage := utils.IntPercentage(clientsWithErrors, totalNumberOfClients)
isUsingPassthrough := false
outputVariants := data.GetStreamOutputVariants()
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
for _, variant := range outputVariants {
if variant.IsVideoPassthrough {
isUsingPassthrough = true

View File

@@ -5,8 +5,8 @@ import (
"time"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
// How often we poll for updates.
@@ -56,8 +56,9 @@ var _getStatus func() models.Status
// Start will begin the metrics collection and alerting.
func Start(getStatus func() models.Status) {
configRepository := configrepository.Get()
_getStatus = getStatus
host := data.GetServerURL()
host := configRepository.GetServerURL()
if host == "" {
host = "unknown"
}

View File

@@ -1,4 +1,4 @@
package data
package models
import (
"bytes"
@@ -12,48 +12,48 @@ type ConfigEntry struct {
Key string
}
func (c *ConfigEntry) getStringSlice() ([]string, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetStringSlice() ([]string, error) {
decoder := c.GetDecoder()
var result []string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getStringMap() (map[string]string, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetStringMap() (map[string]string, error) {
decoder := c.GetDecoder()
var result map[string]string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getString() (string, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetString() (string, error) {
decoder := c.GetDecoder()
var result string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getNumber() (float64, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetNumber() (float64, error) {
decoder := c.GetDecoder()
var result float64
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getBool() (bool, error) {
decoder := c.getDecoder()
func (c *ConfigEntry) GetBool() (bool, error) {
decoder := c.GetDecoder()
var result bool
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) getObject(result interface{}) error {
decoder := c.getDecoder()
func (c *ConfigEntry) GetObject(result interface{}) error {
decoder := c.GetDecoder()
err := decoder.Decode(result)
return err
}
func (c *ConfigEntry) getDecoder() *gob.Decoder {
func (c *ConfigEntry) GetDecoder() *gob.Decoder {
valueBytes := c.Value.([]byte)
decoder := gob.NewDecoder(bytes.NewBuffer(valueBytes))
return decoder

View File

@@ -8,6 +8,7 @@ import (
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/notifications/browser"
@@ -18,9 +19,10 @@ import (
// Notifier is an instance of the live stream notifier.
type Notifier struct {
datastore *data.Datastore
browser *browser.Browser
discord *discord.Discord
datastore *data.Datastore
browser *browser.Browser
discord *discord.Discord
configRepository configrepository.ConfigRepository
}
// Setup will perform any pre-use setup for the notifier.
@@ -30,8 +32,10 @@ func Setup(datastore *data.Datastore) {
}
func initializeBrowserPushIfNeeded() {
pubKey, _ := data.GetBrowserPushPublicKey()
privKey, _ := data.GetBrowserPushPrivateKey()
configRepository := configrepository.Get()
pubKey, _ := configRepository.GetBrowserPushPublicKey()
privKey, _ := configRepository.GetBrowserPushPrivateKey()
// We need browser push keys so people can register for pushes.
if pubKey == "" || privKey == "" {
@@ -40,26 +44,27 @@ func initializeBrowserPushIfNeeded() {
log.Errorln("unable to initialize browser push notification keys", err)
}
if err := data.SetBrowserPushPrivateKey(browserPrivateKey); err != nil {
if err := configRepository.SetBrowserPushPrivateKey(browserPrivateKey); err != nil {
log.Errorln("unable to set browser push private key", err)
}
if err := data.SetBrowserPushPublicKey(browserPublicKey); err != nil {
if err := configRepository.SetBrowserPushPublicKey(browserPublicKey); err != nil {
log.Errorln("unable to set browser push public key", err)
}
}
// Enable browser push notifications by default.
if !data.GetHasPerformedInitialNotificationsConfig() {
_ = data.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage})
_ = data.SetHasPerformedInitialNotificationsConfig(true)
if !configRepository.GetHasPerformedInitialNotificationsConfig() {
_ = configRepository.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage})
_ = configRepository.SetHasPerformedInitialNotificationsConfig(true)
}
}
// New creates a new instance of the Notifier.
func New(datastore *data.Datastore) (*Notifier, error) {
notifier := Notifier{
datastore: datastore,
datastore: datastore,
configRepository: configrepository.Get(),
}
if err := notifier.setupBrowserPush(); err != nil {
@@ -73,13 +78,13 @@ func New(datastore *data.Datastore) (*Notifier, error) {
}
func (n *Notifier) setupBrowserPush() error {
if data.GetBrowserPushConfig().Enabled {
publicKey, err := data.GetBrowserPushPublicKey()
if n.configRepository.GetBrowserPushConfig().Enabled {
publicKey, err := n.configRepository.GetBrowserPushPublicKey()
if err != nil || publicKey == "" {
return errors.Wrap(err, "browser notifier disabled, failed to get browser push public key")
}
privateKey, err := data.GetBrowserPushPrivateKey()
privateKey, err := n.configRepository.GetBrowserPushPrivateKey()
if err != nil || privateKey == "" {
return errors.Wrap(err, "browser notifier disabled, failed to get browser push private key")
}
@@ -99,7 +104,7 @@ func (n *Notifier) notifyBrowserPush() {
log.Errorln("error getting browser push notification destinations", err)
}
for _, destination := range destinations {
unsubscribed, err := n.browser.Send(destination, data.GetServerName(), data.GetBrowserPushConfig().GoLiveMessage)
unsubscribed, err := n.browser.Send(destination, n.configRepository.GetServerName(), n.configRepository.GetBrowserPushConfig().GoLiveMessage)
if unsubscribed {
// If the error is "unsubscribed", then remove the destination from the database.
if err := RemoveNotificationForChannel(BrowserPushNotification, destination); err != nil {
@@ -112,14 +117,14 @@ func (n *Notifier) notifyBrowserPush() {
}
func (n *Notifier) setupDiscord() error {
discordConfig := data.GetDiscordConfig()
discordConfig := n.configRepository.GetDiscordConfig()
if discordConfig.Enabled && discordConfig.Webhook != "" {
var image string
if serverURL := data.GetServerURL(); serverURL != "" {
if serverURL := n.configRepository.GetServerURL(); serverURL != "" {
image = serverURL + "/logo"
}
discordNotifier, err := discord.New(
data.GetServerName(),
n.configRepository.GetServerName(),
image,
discordConfig.Webhook,
)
@@ -132,12 +137,12 @@ func (n *Notifier) setupDiscord() error {
}
func (n *Notifier) notifyDiscord() {
goLiveMessage := data.GetDiscordConfig().GoLiveMessage
streamTitle := data.GetStreamTitle()
goLiveMessage := n.configRepository.GetDiscordConfig().GoLiveMessage
streamTitle := n.configRepository.GetStreamTitle()
if streamTitle != "" {
goLiveMessage += "\n" + streamTitle
}
message := fmt.Sprintf("%s\n\n%s", goLiveMessage, data.GetServerURL())
message := fmt.Sprintf("%s\n\n%s", goLiveMessage, n.configRepository.GetServerURL())
if err := n.discord.Send(message); err != nil {
log.Errorln("error sending discord message", err)
@@ -158,6 +163,7 @@ func (n *Notifier) Notify() {
// RemoveNotificationForChannel removes a notification destination.
func RemoveNotificationForChannel(channel, destination string) error {
log.Debugln("Removing notification for channel", channel)
return data.GetDatastore().GetQueries().RemoveNotificationDestinationForChannel(context.Background(), db.RemoveNotificationDestinationForChannelParams{
Channel: channel,
Destination: destination,

View File

@@ -2948,7 +2948,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SystemMessage'
$ref: '#/components/schemas/MessageEvent'
responses:
'200':
description: Message sent successfully
@@ -3045,7 +3045,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/UserMessage'
$ref: '#/components/schemas/MessageEvent'
responses:
'200':
description: Message sent successfully
@@ -3079,7 +3079,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SystemActionEvent'
$ref: '#/components/schemas/MessageEvent'
responses:
'200':
description: Action sent successfully
@@ -3221,7 +3221,7 @@ paths:
get:
summary: Get a user's details
operationId: GetUserDetails
tags: ['External', 'Chat']
tags: ['Chat']
parameters:
- in: path
name: userId

View File

@@ -0,0 +1,15 @@
package authrepository
import (
"database/sql"
"github.com/owncast/owncast/models"
)
type AuthRepository interface {
CreateBanIPTable(db *sql.DB)
BanIPAddress(address, note string) error
IsIPAddressBanned(address string) (bool, error)
GetIPAddressBans() ([]models.IPAddress, error)
RemoveIPAddressBan(address string) error
}

View File

@@ -0,0 +1,65 @@
package authrepository
import (
"context"
"database/sql"
"log"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
)
// CreateBanIPTable will create the IP ban table if needed.
func (r *SqlAuthRepository) CreateBanIPTable(db *sql.DB) {
createTableSQL := ` CREATE TABLE IF NOT EXISTS ip_bans (
"ip_address" TEXT NOT NULL PRIMARY KEY,
"notes" TEXT,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`
stmt, err := db.Prepare(createTableSQL)
if err != nil {
log.Fatal("error creating ip ban table", err)
}
defer stmt.Close()
if _, err := stmt.Exec(); err != nil {
log.Fatal("error creating ip ban table", err)
}
}
// BanIPAddress will persist a new IP address ban to the datastore.
func (r *SqlAuthRepository) BanIPAddress(address, note string) error {
return r.datastore.GetQueries().BanIPAddress(context.Background(), db.BanIPAddressParams{
IpAddress: address,
Notes: sql.NullString{String: note, Valid: true},
})
}
// IsIPAddressBanned will return if an IP address has been previously blocked.
func (r *SqlAuthRepository) IsIPAddressBanned(address string) (bool, error) {
blocked, error := r.datastore.GetQueries().IsIPAddressBlocked(context.Background(), address)
return blocked > 0, error
}
// GetIPAddressBans will return all the banned IP addresses.
func (r *SqlAuthRepository) GetIPAddressBans() ([]models.IPAddress, error) {
result, err := r.datastore.GetQueries().GetIPAddressBans(context.Background())
if err != nil {
return nil, err
}
response := []models.IPAddress{}
for _, ip := range result {
response = append(response, models.IPAddress{
IPAddress: ip.IpAddress,
Notes: ip.Notes.String,
CreatedAt: ip.CreatedAt.Time,
})
}
return response, err
}
// RemoveIPAddressBan will remove a previously banned IP address.
func (r *SqlAuthRepository) RemoveIPAddressBan(address string) error {
return r.datastore.GetQueries().RemoveIPAddressBan(context.Background(), address)
}

View File

@@ -0,0 +1,30 @@
package authrepository
import (
"github.com/owncast/owncast/core/data"
)
type SqlAuthRepository struct {
datastore *data.Datastore
}
// NOTE: This is temporary during the transition period.
var temporaryGlobalInstance AuthRepository
// Get will return the user repository.
func Get() AuthRepository {
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) *SqlAuthRepository {
r := &SqlAuthRepository{
datastore: datastore,
}
return r
}

View File

@@ -0,0 +1,13 @@
package configrepository
// GetFederatedInboxMap is a mapping between account names and their outbox.
func (r SqlConfigRepository) GetFederatedInboxMap() map[string]string {
return map[string]string{
r.GetDefaultFederationUsername(): r.GetDefaultFederationUsername(),
}
}
// GetDefaultFederationUsername will return the username used for sending federation activities.
func (r *SqlConfigRepository) GetDefaultFederationUsername() string {
return r.GetFederationUsername()
}

View File

@@ -1,8 +1,9 @@
package data
package configrepository
import (
"strings"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
@@ -12,8 +13,8 @@ const (
datastoreValueVersionKey = "DATA_STORE_VERSION"
)
func migrateDatastoreValues(datastore *Datastore) {
currentVersion, _ := _datastore.GetNumber(datastoreValueVersionKey)
func migrateDatastoreValues(datastore *data.Datastore) {
currentVersion, _ := datastore.GetNumber(datastoreValueVersionKey)
if currentVersion == 0 {
currentVersion = datastoreValuesVersion
}
@@ -33,12 +34,12 @@ func migrateDatastoreValues(datastore *Datastore) {
log.Fatalln("missing datastore values migration step")
}
}
if err := _datastore.SetNumber(datastoreValueVersionKey, datastoreValuesVersion); err != nil {
if err := datastore.SetNumber(datastoreValueVersionKey, datastoreValuesVersion); err != nil {
log.Errorln("error setting datastore value version:", err)
}
}
func migrateToDatastoreValues1(datastore *Datastore) {
func migrateToDatastoreValues1(datastore *data.Datastore) {
// Migrate the forbidden usernames to be a slice instead of a string.
forbiddenUsernamesString, _ := datastore.GetString(blockedUsernamesKey)
if forbiddenUsernamesString != "" {
@@ -58,28 +59,32 @@ func migrateToDatastoreValues1(datastore *Datastore) {
}
}
func migrateToDatastoreValues2(datastore *Datastore) {
func migrateToDatastoreValues2(datastore *data.Datastore) {
configRepository := Get()
oldAdminPassword, _ := datastore.GetString("stream_key")
// Avoids double hashing the password
_ = datastore.SetString("admin_password_key", oldAdminPassword)
_ = SetStreamKeys([]models.StreamKey{
_ = configRepository.SetStreamKeys([]models.StreamKey{
{Key: oldAdminPassword, Comment: "Default stream key"},
})
}
func migrateToDatastoreValues3ServingEndpoint3(_ *Datastore) {
s3Config := GetS3Config()
func migrateToDatastoreValues3ServingEndpoint3(_ *data.Datastore) {
configRepository := Get()
s3Config := configRepository.GetS3Config()
if !s3Config.Enabled {
return
}
_ = SetVideoServingEndpoint(s3Config.ServingEndpoint)
_ = configRepository.SetVideoServingEndpoint(s3Config.ServingEndpoint)
}
func migrateToDatastoreValues4(datastore *Datastore) {
func migrateToDatastoreValues4(datastore *data.Datastore) {
configRepository := Get()
unhashed_pass, _ := datastore.GetString("admin_password_key")
err := SetAdminPassword(unhashed_pass)
err := configRepository.SetAdminPassword(unhashed_pass)
if err != nil {
log.Fatalln("error migrating admin password:", err)
}

View File

@@ -0,0 +1,62 @@
package configrepository
const (
extraContentKey = "extra_page_content"
streamTitleKey = "stream_title"
adminPasswordKey = "admin_password_key"
logoPathKey = "logo_path"
logoUniquenessKey = "logo_uniqueness"
serverSummaryKey = "server_summary"
serverWelcomeMessageKey = "server_welcome_message"
serverNameKey = "server_name"
serverURLKey = "server_url"
httpPortNumberKey = "http_port_number"
httpListenAddressKey = "http_listen_address"
websocketHostOverrideKey = "websocket_host_override"
rtmpPortNumberKey = "rtmp_port_number"
serverMetadataTagsKey = "server_metadata_tags"
directoryEnabledKey = "directory_enabled"
directoryRegistrationKeyKey = "directory_registration_key"
socialHandlesKey = "social_handles"
peakViewersSessionKey = "peak_viewers_session"
peakViewersOverallKey = "peak_viewers_overall"
lastDisconnectTimeKey = "last_disconnect_time"
ffmpegPathKey = "ffmpeg_path"
nsfwKey = "nsfw"
s3StorageConfigKey = "s3_storage_config"
videoLatencyLevel = "video_latency_level"
videoStreamOutputVariantsKey = "video_stream_output_variants"
chatDisabledKey = "chat_disabled"
externalActionsKey = "external_actions"
customStylesKey = "custom_styles"
customJavascriptKey = "custom_javascript"
videoCodecKey = "video_codec"
blockedUsernamesKey = "blocked_usernames"
publicKeyKey = "public_key"
privateKeyKey = "private_key"
serverInitDateKey = "server_init_date"
federationEnabledKey = "federation_enabled"
federationUsernameKey = "federation_username"
federationPrivateKey = "federation_private"
federationGoLiveMessageKey = "federation_go_live_message"
federationShowEngagementKey = "federation_show_engagement"
federationBlockedDomainsKey = "federation_blocked_domains"
suggestedUsernamesKey = "suggested_usernames"
chatJoinMessagesEnabledKey = "chat_join_messages_enabled"
chatEstablishedUsersOnlyModeKey = "chat_established_users_only_mode"
chatSpamProtectionEnabledKey = "chat_spam_protection_enabled"
chatSlurFilterEnabledKey = "chat_slur_filter_enabled"
notificationsEnabledKey = "notifications_enabled"
discordConfigurationKey = "discord_configuration"
browserPushConfigurationKey = "browser_push_configuration"
browserPushPublicKeyKey = "browser_push_public_key"
// nolint:gosec
browserPushPrivateKeyKey = "browser_push_private_key"
hasConfiguredInitialNotificationsKey = "has_configured_initial_notifications"
hideViewerCountKey = "hide_viewer_count"
customOfflineMessageKey = "custom_offline_message"
customColorVariableValuesKey = "custom_color_variable_values"
streamKeysKey = "stream_keys"
disableSearchIndexingKey = "disable_search_indexing"
videoServingEndpointKey = "video_serving_endpoint"
)

View File

@@ -0,0 +1,129 @@
package configrepository
import (
"time"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/utils"
)
type ConfigRepository interface {
GetExtraPageBodyContent() string
SetExtraPageBodyContent(content string) error
GetStreamTitle() string
SetStreamTitle(title string) error
GetAdminPassword() string
SetAdminPassword(key string) error
GetLogoPath() string
SetLogoPath(logo string) error
SetLogoUniquenessString(uniqueness string) error
GetLogoUniquenessString() string
GetServerSummary() string
SetServerSummary(summary string) error
GetServerWelcomeMessage() string
SetServerWelcomeMessage(welcomeMessage string) error
GetServerName() string
SetServerName(name string) error
GetServerURL() string
SetServerURL(url string) error
GetHTTPPortNumber() int
SetWebsocketOverrideHost(host string) error
GetWebsocketOverrideHost() string
SetHTTPPortNumber(port float64) error
GetHTTPListenAddress() string
SetHTTPListenAddress(address string) error
GetRTMPPortNumber() int
SetRTMPPortNumber(port float64) error
GetServerMetadataTags() []string
SetServerMetadataTags(tags []string) error
GetDirectoryEnabled() bool
SetDirectoryEnabled(enabled bool) error
SetDirectoryRegistrationKey(key string) error
GetDirectoryRegistrationKey() string
GetSocialHandles() []models.SocialHandle
SetSocialHandles(socialHandles []models.SocialHandle) error
GetPeakSessionViewerCount() int
SetPeakSessionViewerCount(count int) error
GetPeakOverallViewerCount() int
SetPeakOverallViewerCount(count int) error
GetLastDisconnectTime() (*utils.NullTime, error)
SetLastDisconnectTime(disconnectTime time.Time) error
SetNSFW(isNSFW bool) error
GetNSFW() bool
SetFfmpegPath(path string) error
GetFfMpegPath() string
GetS3Config() models.S3
SetS3Config(config models.S3) error
GetStreamLatencyLevel() models.LatencyLevel
SetStreamLatencyLevel(level float64) error
GetStreamOutputVariants() []models.StreamOutputVariant
SetStreamOutputVariants(variants []models.StreamOutputVariant) error
SetChatDisabled(disabled bool) error
GetChatDisabled() bool
SetChatEstablishedUsersOnlyMode(enabled bool) error
GetChatEstbalishedUsersOnlyMode() bool
SetChatSpamProtectionEnabled(enabled bool) error
GetChatSpamProtectionEnabled() bool
SetChatSlurFilterEnabled(enabled bool) error
GetChatSlurFilterEnabled() bool
GetExternalActions() []models.ExternalAction
SetExternalActions(actions []models.ExternalAction) error
SetCustomStyles(styles string) error
GetCustomStyles() string
SetCustomJavascript(styles string) error
GetCustomJavascript() string
SetVideoCodec(codec string) error
GetVideoCodec() string
VerifySettings() error
FindHighestVideoQualityIndex(qualities []models.StreamOutputVariant) (int, bool)
GetForbiddenUsernameList() []string
SetForbiddenUsernameList(usernames []string) error
GetSuggestedUsernamesList() []string
SetSuggestedUsernamesList(usernames []string) error
GetServerInitTime() (*utils.NullTime, error)
SetServerInitTime(t time.Time) error
SetFederationEnabled(enabled bool) error
GetFederationEnabled() bool
SetFederationUsername(username string) error
GetFederationUsername() string
SetFederationGoLiveMessage(message string) error
GetFederationGoLiveMessage() string
SetFederationIsPrivate(isPrivate bool) error
GetFederationIsPrivate() bool
SetFederationShowEngagement(showEngagement bool) error
GetFederationShowEngagement() bool
SetBlockedFederatedDomains(domains []string) error
GetBlockedFederatedDomains() []string
SetChatJoinMessagesEnabled(enabled bool) error
GetChatJoinPartMessagesEnabled() bool
SetNotificationsEnabled(enabled bool) error
GetNotificationsEnabled() bool
GetDiscordConfig() models.DiscordConfiguration
SetDiscordConfig(config models.DiscordConfiguration) error
GetBrowserPushConfig() models.BrowserNotificationConfiguration
SetBrowserPushConfig(config models.BrowserNotificationConfiguration) error
SetBrowserPushPublicKey(key string) error
GetBrowserPushPublicKey() (string, error)
SetBrowserPushPrivateKey(key string) error
GetBrowserPushPrivateKey() (string, error)
SetHasPerformedInitialNotificationsConfig(hasConfigured bool) error
GetHasPerformedInitialNotificationsConfig() bool
GetHideViewerCount() bool
SetHideViewerCount(hide bool) error
GetCustomOfflineMessage() string
SetCustomOfflineMessage(message string) error
SetCustomColorVariableValues(variables map[string]string) error
GetCustomColorVariableValues() map[string]string
GetStreamKeys() []models.StreamKey
SetStreamKeys(actions []models.StreamKey) error
SetDisableSearchIndexing(disableSearchIndexing bool) error
GetDisableSearchIndexing() bool
GetVideoServingEndpoint() string
SetVideoServingEndpoint(message string) error
GetFederatedInboxMap() map[string]string
GetDefaultFederationUsername() string
GetPublicKey() string
GetPrivateKey() string
SetPublicKey(key string) error
SetPrivateKey(key string) error
}

View File

@@ -0,0 +1,23 @@
package configrepository
// GetPublicKey will return the public key.
func (r *SqlConfigRepository) GetPublicKey() string {
value, _ := r.datastore.GetString(publicKeyKey)
return value
}
// SetPublicKey will save the public key.
func (r *SqlConfigRepository) SetPublicKey(key string) error {
return r.datastore.SetString(publicKeyKey, key)
}
// GetPrivateKey will return the private key.
func (r *SqlConfigRepository) GetPrivateKey() string {
value, _ := r.datastore.GetString(privateKeyKey)
return value
}
// SetPrivateKey will save the private key.
func (r *SqlConfigRepository) SetPrivateKey(key string) error {
return r.datastore.SetString(privateKeyKey, key)
}

View File

@@ -0,0 +1,62 @@
package configrepository
import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
// PopulateDefaults will set default values in the database.
func (r *SqlConfigRepository) PopulateDefaults() {
key := "HAS_POPULATED_DEFAULTS"
r.datastore.WarmCache()
defaults := config.GetDefaults()
_ = r.SetAdminPassword(defaults.AdminPassword)
_ = r.SetStreamKeys(defaults.StreamKeys)
_ = r.SetHTTPPortNumber(float64(defaults.WebServerPort))
_ = r.SetRTMPPortNumber(float64(defaults.RTMPServerPort))
_ = r.SetLogoPath(defaults.Logo)
_ = r.SetServerMetadataTags([]string{"owncast", "streaming"})
_ = r.SetServerSummary(defaults.Summary)
_ = r.SetServerWelcomeMessage("")
_ = r.SetServerName(defaults.Name)
_ = r.SetExtraPageBodyContent(defaults.PageBodyContent)
_ = r.SetFederationGoLiveMessage(defaults.FederationGoLiveMessage)
_ = r.SetSocialHandles([]models.SocialHandle{
{
Platform: "github",
URL: "https://github.com/owncast/owncast",
},
})
if !r.HasPopulatedFederationDefaults() {
if err := r.SetFederationGoLiveMessage(config.GetDefaults().FederationGoLiveMessage); err != nil {
log.Errorln(err)
}
if err := r.datastore.SetBool("HAS_POPULATED_FEDERATION_DEFAULTS", true); err != nil {
log.Errorln(err)
}
}
_ = r.datastore.SetBool(key, true)
}
// HasPopulatedDefaults will determine if the defaults have been inserted into the database.
func (r *SqlConfigRepository) HasPopulatedDefaults() bool {
hasPopulated, err := r.datastore.GetBool("HAS_POPULATED_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}
func (r *SqlConfigRepository) HasPopulatedFederationDefaults() bool {
hasPopulated, err := r.datastore.GetBool("HAS_POPULATED_FEDERATION_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"extends": [
"config:base"
"config:recommended"
],
"baseBranches": [
"develop"
@@ -12,7 +12,7 @@
"platformAutomerge": true
},
"npm": {
"stabilityDays": 3
"minimumReleaseAge": "3 days"
},
"dependencyDashboard": true,
"major": {
@@ -38,13 +38,6 @@
"platformAutomerge": true,
"dependencyDashboardApproval": false
},
{
"description": "Require approval for every Go language update",
"dependencyDashboardApproval": true,
"matchPackagePatterns": [
"go"
]
},
{
"description": "Ignore the old pre-0.1.0 web packages",
"matchPackageNames": [
@@ -56,9 +49,21 @@
"postcss-cli",
"@videojs/themes",
"@joeattardi/emoji-button",
"preact"
"preact",
"github.com/go-fed/activity",
"go"
],
"enabled": false
},
{
"description": "Do not auto-merge statically assigned versions in npm",
"matchManagers": [
"npm"
],
"matchCurrentVersion": "/^[0-9]+\\.[0-9]+\\.[0-9]+$/",
"automerge": false,
"platformAutomerge": false,
"dependencyDashboardApproval": true
}
]
}
}

2
static/web/404.html vendored

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

View File

@@ -0,0 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1913],{55945:function(t,n,e){e.d(n,{u:function(){return r}});function r(t){return n=>{let e=(t?Math[t]:Math.trunc)(n);return 0===e?0:e}}},98172:function(t,n,e){e.d(n,{U:function(){return u}});var r=e(46042);function u(t,n){let e=+(0,r.Q)(t)-+(0,r.Q)(n);return e<0?-1:e>0?1:e}},64077:function(t,n,e){e.d(n,{_:function(){return u}});var r=e(46042);function u(t,n){return+(0,r.Q)(t)-+(0,r.Q)(n)}},4349:function(t,n,e){e.d(n,{d:function(){return o}});var r=e(29675),u=e(98172),i=e(46042);function o(t,n,e){let[o,a,f]=(0,r.d)(null==e?void 0:e.in,t,t,n),s=(0,u.U)(a,f),l=Math.abs(function(t,n,e){let[u,i]=(0,r.d)(void 0,t,n);return 12*(u.getFullYear()-i.getFullYear())+(u.getMonth()-i.getMonth())}(a,f));if(l<1)return 0;1===a.getMonth()&&a.getDate()>27&&a.setDate(30),a.setMonth(a.getMonth()-s*l);let c=(0,u.U)(a,f)===-s;(function(t,n){let e=(0,i.Q)(t,void 0);return+function(t,n){let e=(0,i.Q)(t,null==n?void 0:n.in);return e.setHours(23,59,59,999),e}(e,void 0)==+function(t,n){let e=(0,i.Q)(t,null==n?void 0:n.in),r=e.getMonth();return e.setFullYear(e.getFullYear(),r+1,0),e.setHours(23,59,59,999),e}(e,void 0)})(o)&&1===l&&1===(0,u.U)(o,f)&&(c=!1);let d=s*(l-+c);return 0===d?0:d}},94817:function(t,n,e){e.d(n,{c:function(){return i}});var r=e(55945),u=e(64077);function i(t,n,e){let i=(0,u._)(t,n)/1e3;return(0,r.u)(null==e?void 0:e.roundingMethod)(i)}},61913:function(t,n,e){e.d(n,{Q:function(){return d}});var r=e(16393),u=e(71397),i=e(45662),o=e(98422),a=e(29675),f=e(98172),s=e(96707),l=e(4349),c=e(94817);function d(t,n){return function(t,n,e){var r,d;let h;let D=(0,i.j)(),M=null!==(d=null!==(r=null==e?void 0:e.locale)&&void 0!==r?r:D.locale)&&void 0!==d?d:u._,v=(0,f.U)(t,n);if(isNaN(v))throw RangeError("Invalid time value");let m=Object.assign({},e,{addSuffix:null==e?void 0:e.addSuffix,comparison:v}),[g,X]=(0,a.d)(null==e?void 0:e.in,...v>0?[n,t]:[t,n]),H=(0,c.c)(X,g),_=Math.round((H-((0,o.D)(X)-(0,o.D)(g))/1e3)/60);if(_<2){if(null==e?void 0:e.includeSeconds){if(H<5)return M.formatDistance("lessThanXSeconds",5,m);if(H<10)return M.formatDistance("lessThanXSeconds",10,m);if(H<20)return M.formatDistance("lessThanXSeconds",20,m);if(H<40)return M.formatDistance("halfAMinute",0,m);else if(H<60)return M.formatDistance("lessThanXMinutes",1,m);else return M.formatDistance("xMinutes",1,m)}return 0===_?M.formatDistance("lessThanXMinutes",1,m):M.formatDistance("xMinutes",_,m)}if(_<45)return M.formatDistance("xMinutes",_,m);if(_<90)return M.formatDistance("aboutXHours",1,m);if(_<s.H_)return M.formatDistance("aboutXHours",Math.round(_/60),m);if(_<2520)return M.formatDistance("xDays",1,m);if(_<s.fH){let t=Math.round(_/s.H_);return M.formatDistance("xDays",t,m)}if(_<2*s.fH)return h=Math.round(_/s.fH),M.formatDistance("aboutXMonths",h,m);if((h=(0,l.d)(X,g))<12){let t=Math.round(_/s.fH);return M.formatDistance("xMonths",t,m)}{let t=h%12,n=Math.trunc(h/12);return t<3?M.formatDistance("aboutXYears",n,m):t<9?M.formatDistance("overXYears",n,m):M.formatDistance("almostXYears",n+1,m)}}(t,(0,r.L)(t,Date.now()),n)}}}]);

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

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3883],{23883:function(e,t,n){function r(e){for(var t={},n=e.split(" "),r=0;r<n.length;++r)t[n[r]]=!0;return t}n.r(t),n.d(t,{groovy:function(){return y}});var i,a=r("abstract as assert boolean break byte case catch char class const continue def default do double else enum extends final finally float for goto if implements import in instanceof int interface long native new package private protected public return short static strictfp super switch synchronized threadsafe throw throws trait transient try void volatile while"),o=r("catch class def do else enum finally for if interface switch trait try while"),l=r("return break continue"),s=r("null true false this");function u(e,t){var n=e.next();if('"'==n||"'"==n)return f(n,e,t);if(/[\[\]{}\(\),;\:\.]/.test(n))return i=n,null;if(/\d/.test(n))return e.eatWhile(/[\w\.]/),e.eat(/eE/)&&(e.eat(/\+\-/),e.eatWhile(/\d/)),"number";if("/"==n){if(e.eat("*"))return t.tokenize.push(p),p(e,t);if(e.eat("/"))return e.skipToEnd(),"comment";if(k(t.lastToken,!1))return f(n,e,t)}if("-"==n&&e.eat(">"))return i="->",null;if(/[+\-*&%=<>!?|\/~]/.test(n))return e.eatWhile(/[+\-*&%=<>|~]/),"operator";if(e.eatWhile(/[\w\$_]/),"@"==n)return e.eatWhile(/[\w\$_\.]/),"meta";if("."==t.lastToken)return"property";if(e.eat(":"))return i="proplabel","property";var r=e.current();return s.propertyIsEnumerable(r)?"atom":a.propertyIsEnumerable(r)?(o.propertyIsEnumerable(r)?i="newstatement":l.propertyIsEnumerable(r)&&(i="standalone"),"keyword"):"variable"}function f(e,t,n){var r=!1;if("/"!=e&&t.eat(e)){if(!t.eat(e))return"string";r=!0}function i(t,n){for(var i,a=!1,o=!r;null!=(i=t.next());){if(i==e&&!a){if(!r)break;if(t.match(e+e)){o=!0;break}}if('"'==e&&"$"==i&&!a){if(t.eat("{"))return n.tokenize.push(function(){var e=1;function t(t,n){if("}"==t.peek()){if(0==--e)return n.tokenize.pop(),n.tokenize[n.tokenize.length-1](t,n)}else"{"==t.peek()&&e++;return u(t,n)}return t.isBase=!0,t}()),"string";if(t.match(/^\w/,!1))return n.tokenize.push(c),"string"}a=!a&&"\\"==i}return o&&n.tokenize.pop(),"string"}return n.tokenize.push(i),i(t,n)}function c(e,t){var n=e.match(/^(\.|[\w\$_]+)/);return n?"."==n[0]?null:"variable":(t.tokenize.pop(),t.tokenize[t.tokenize.length-1](e,t))}function p(e,t){for(var n,r=!1;n=e.next();){if("/"==n&&r){t.tokenize.pop();break}r="*"==n}return"comment"}function k(e,t){return!e||"operator"==e||"->"==e||/[\.\[\{\(,;:]/.test(e)||"newstatement"==e||"keyword"==e||"proplabel"==e||"standalone"==e&&!t}function m(e,t,n,r,i){this.indented=e,this.column=t,this.type=n,this.align=r,this.prev=i}function h(e,t,n){return e.context=new m(e.indented,t,n,null,e.context)}function d(e){var t=e.context.type;return(")"==t||"]"==t||"}"==t)&&(e.indented=e.context.indented),e.context=e.context.prev}u.isBase=!0;let y={name:"groovy",startState:function(e){return{tokenize:[u],context:new m(-e,0,"top",!1),indented:0,startOfLine:!0,lastToken:null}},token:function(e,t){var n=t.context;if(e.sol()&&(null==n.align&&(n.align=!1),t.indented=e.indentation(),t.startOfLine=!0,"statement"!=n.type||k(t.lastToken,!0)||(d(t),n=t.context)),e.eatSpace())return null;i=null;var r=t.tokenize[t.tokenize.length-1](e,t);if("comment"==r)return r;if(null==n.align&&(n.align=!0),(";"==i||":"==i)&&"statement"==n.type)d(t);else if("->"==i&&"statement"==n.type&&"}"==n.prev.type)d(t),t.context.align=!1;else if("{"==i)h(t,e.column(),"}");else if("["==i)h(t,e.column(),"]");else if("("==i)h(t,e.column(),")");else if("}"==i){for(;"statement"==n.type;)n=d(t);for("}"==n.type&&(n=d(t));"statement"==n.type;)n=d(t)}else i==n.type?d(t):("}"==n.type||"top"==n.type||"statement"==n.type&&"newstatement"==i)&&h(t,e.column(),"statement");return t.startOfLine=!1,t.lastToken=i||r,r},indent:function(e,t,n){if(!e.tokenize[e.tokenize.length-1].isBase)return null;var r=t&&t.charAt(0),i=e.context;"statement"!=i.type||k(e.lastToken,!0)||(i=i.prev);var a=r==i.type;return"statement"==i.type?i.indented+("{"==r?0:n.unit):i.align?i.column+(a?0:1):i.indented+(a?0:n.unit)},languageData:{indentOnInput:/^\s*[{}]$/,commentTokens:{line:"//",block:{open:"/*",close:"*/"}},closeBrackets:{brackets:["(","[","{","'",'"',"'''",'"""']}}}}}]);

View File

@@ -0,0 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3883],{23883:function(e,t,n){function r(e){for(var t={},n=e.split(" "),r=0;r<n.length;++r)t[n[r]]=!0;return t}n.r(t),n.d(t,{groovy:function(){return y}});var i,a=r("abstract as assert boolean break byte case catch char class const continue def default do double else enum extends final finally float for goto if implements import in instanceof int interface long native new package private protected public return short static strictfp super switch synchronized threadsafe throw throws trait transient try void volatile while"),o=r("catch class def do else enum finally for if interface switch trait try while"),l=r("return break continue"),s=r("null true false this");function u(e,t){var n=e.next();if('"'==n||"'"==n)return c(n,e,t);if(/[\[\]{}\(\),;\:\.]/.test(n))return i=n,null;if(/\d/.test(n))return e.eatWhile(/[\w\.]/),e.eat(/eE/)&&(e.eat(/\+\-/),e.eatWhile(/\d/)),"number";if("/"==n){if(e.eat("*"))return t.tokenize.push(p),p(e,t);if(e.eat("/"))return e.skipToEnd(),"comment";if(k(t.lastToken,!1))return c(n,e,t)}if("-"==n&&e.eat(">"))return i="->",null;if(/[+\-*&%=<>!?|\/~]/.test(n))return e.eatWhile(/[+\-*&%=<>|~]/),"operator";if(e.eatWhile(/[\w\$_]/),"@"==n)return e.eatWhile(/[\w\$_\.]/),"meta";if("."==t.lastToken)return"property";if(e.eat(":"))return i="proplabel","property";var r=e.current();return s.propertyIsEnumerable(r)?"atom":a.propertyIsEnumerable(r)?(o.propertyIsEnumerable(r)?i="newstatement":l.propertyIsEnumerable(r)&&(i="standalone"),"keyword"):"variable"}function c(e,t,n){var r=!1;if("/"!=e&&t.eat(e)){if(!t.eat(e))return"string";r=!0}function i(t,n){for(var i,a=!1,o=!r;null!=(i=t.next());){if(i==e&&!a){if(!r)break;if(t.match(e+e)){o=!0;break}}if('"'==e&&"$"==i&&!a){if(t.eat("{"))return n.tokenize.push(function(){var e=1;function t(t,n){if("}"==t.peek()){if(0==--e)return n.tokenize.pop(),n.tokenize[n.tokenize.length-1](t,n)}else"{"==t.peek()&&e++;return u(t,n)}return t.isBase=!0,t}()),"string";if(t.match(/^\w/,!1))return n.tokenize.push(f),"string"}a=!a&&"\\"==i}return o&&n.tokenize.pop(),"string"}return n.tokenize.push(i),i(t,n)}function f(e,t){var n=e.match(/^(\.|[\w\$_]+)/);return(n&&e.match("."==n[0]?/^[\w$_]/:/^\./)||t.tokenize.pop(),n)?"."==n[0]?null:"variable":t.tokenize[t.tokenize.length-1](e,t)}function p(e,t){for(var n,r=!1;n=e.next();){if("/"==n&&r){t.tokenize.pop();break}r="*"==n}return"comment"}function k(e,t){return!e||"operator"==e||"->"==e||/[\.\[\{\(,;:]/.test(e)||"newstatement"==e||"keyword"==e||"proplabel"==e||"standalone"==e&&!t}function m(e,t,n,r,i){this.indented=e,this.column=t,this.type=n,this.align=r,this.prev=i}function h(e,t,n){return e.context=new m(e.indented,t,n,null,e.context)}function d(e){var t=e.context.type;return(")"==t||"]"==t||"}"==t)&&(e.indented=e.context.indented),e.context=e.context.prev}u.isBase=!0;let y={name:"groovy",startState:function(e){return{tokenize:[u],context:new m(-e,0,"top",!1),indented:0,startOfLine:!0,lastToken:null}},token:function(e,t){var n=t.context;if(e.sol()&&(null==n.align&&(n.align=!1),t.indented=e.indentation(),t.startOfLine=!0,"statement"!=n.type||k(t.lastToken,!0)||(d(t),n=t.context)),e.eatSpace())return null;i=null;var r=t.tokenize[t.tokenize.length-1](e,t);if("comment"==r)return r;if(null==n.align&&(n.align=!0),(";"==i||":"==i)&&"statement"==n.type)d(t);else if("->"==i&&"statement"==n.type&&"}"==n.prev.type)d(t),t.context.align=!1;else if("{"==i)h(t,e.column(),"}");else if("["==i)h(t,e.column(),"]");else if("("==i)h(t,e.column(),")");else if("}"==i){for(;"statement"==n.type;)n=d(t);for("}"==n.type&&(n=d(t));"statement"==n.type;)n=d(t)}else i==n.type?d(t):("}"==n.type||"top"==n.type||"statement"==n.type&&"newstatement"==i)&&h(t,e.column(),"statement");return t.startOfLine=!1,t.lastToken=i||r,r},indent:function(e,t,n){if(!e.tokenize[e.tokenize.length-1].isBase)return null;var r=t&&t.charAt(0),i=e.context;"statement"!=i.type||k(e.lastToken,!0)||(i=i.prev);var a=r==i.type;return"statement"==i.type?i.indented+("{"==r?0:n.unit):i.align?i.column+(a?0:1):i.indented+(a?0:n.unit)},languageData:{indentOnInput:/^\s*[{}]$/,commentTokens:{line:"//",block:{open:"/*",close:"*/"}},closeBrackets:{brackets:["(","[","{","'",'"',"'''",'"""']}}}}}]);

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

View File

@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6166],{1554:function(t,n,e){e.d(n,{u:function(){return r}});function r(t){return n=>{let e=(t?Math[t]:Math.trunc)(n);return 0===e?0:e}}},67364:function(t,n,e){e.d(n,{U:function(){return u}});var r=e(63923);function u(t,n){let e=(0,r.Q)(t),u=(0,r.Q)(n),a=e.getTime()-u.getTime();return a<0?-1:a>0?1:a}},58005:function(t,n,e){e.d(n,{_:function(){return u}});var r=e(63923);function u(t,n){return+(0,r.Q)(t)-+(0,r.Q)(n)}},76732:function(t,n,e){e.d(n,{d:function(){return a}});var r=e(67364),u=e(63923);function a(t,n){let e;let a=(0,u.Q)(t),o=(0,u.Q)(n),i=(0,r.U)(a,o),f=Math.abs(function(t,n){let e=(0,u.Q)(t),r=(0,u.Q)(n);return 12*(e.getFullYear()-r.getFullYear())+(e.getMonth()-r.getMonth())}(a,o));if(f<1)e=0;else{1===a.getMonth()&&a.getDate()>27&&a.setDate(30),a.setMonth(a.getMonth()-i*f);let n=(0,r.U)(a,o)===-i;(function(t){let n=(0,u.Q)(t);return+function(t){let n=(0,u.Q)(t);return n.setHours(23,59,59,999),n}(n)==+function(t){let n=(0,u.Q)(t),e=n.getMonth();return n.setFullYear(n.getFullYear(),e+1,0),n.setHours(23,59,59,999),n}(n)})((0,u.Q)(t))&&1===f&&1===(0,r.U)(t,o)&&(n=!1),e=i*(f-Number(n))}return 0===e?0:e}},65476:function(t,n,e){e.d(n,{c:function(){return a}});var r=e(1554),u=e(58005);function a(t,n,e){let a=(0,u._)(t,n)/1e3;return(0,r.u)(null==e?void 0:e.roundingMethod)(a)}},46166:function(t,n,e){e.d(n,{Q:function(){return d}});var r=e(75696),u=e(67364),a=e(69005),o=e(76732),i=e(65476),f=e(63923),s=e(5360),c=e(67751),l=e(17956);function d(t,n){return function(t,n,e){var r,d;let h,D,m;let M=(0,c.j)(),Q=null!==(d=null!==(r=null==e?void 0:e.locale)&&void 0!==r?r:M.locale)&&void 0!==d?d:s._,v=(0,u.U)(t,n);if(isNaN(v))throw RangeError("Invalid time value");let g=Object.assign({},e,{addSuffix:null==e?void 0:e.addSuffix,comparison:v});v>0?(h=(0,f.Q)(n),D=(0,f.Q)(t)):(h=(0,f.Q)(t),D=(0,f.Q)(n));let X=(0,i.c)(D,h),H=Math.round((X-((0,l.D)(D)-(0,l.D)(h))/1e3)/60);if(H<2){if(null==e?void 0:e.includeSeconds){if(X<5)return Q.formatDistance("lessThanXSeconds",5,g);if(X<10)return Q.formatDistance("lessThanXSeconds",10,g);if(X<20)return Q.formatDistance("lessThanXSeconds",20,g);if(X<40)return Q.formatDistance("halfAMinute",0,g);else if(X<60)return Q.formatDistance("lessThanXMinutes",1,g);else return Q.formatDistance("xMinutes",1,g)}return 0===H?Q.formatDistance("lessThanXMinutes",1,g):Q.formatDistance("xMinutes",H,g)}if(H<45)return Q.formatDistance("xMinutes",H,g);if(H<90)return Q.formatDistance("aboutXHours",1,g);if(H<a.H_)return Q.formatDistance("aboutXHours",Math.round(H/60),g);if(H<2520)return Q.formatDistance("xDays",1,g);if(H<a.fH){let t=Math.round(H/a.H_);return Q.formatDistance("xDays",t,g)}if(H<2*a.fH)return m=Math.round(H/a.fH),Q.formatDistance("aboutXMonths",m,g);if((m=(0,o.d)(D,h))<12){let t=Math.round(H/a.fH);return Q.formatDistance("xMonths",t,g)}{let t=m%12,n=Math.trunc(m/12);return t<3?Q.formatDistance("aboutXYears",n,g):t<9?Q.formatDistance("overXYears",n,g):Q.formatDistance("almostXYears",n+1,g)}}(t,(0,r.L)(t,Date.now()),n)}}}]);

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

Some files were not shown because too many files have changed in this diff Show More