Compare commits

...
Author SHA1 Message Date
LogalDeveloper d8344da524 Reformatted directory cleanup function. 2024-07-06 12:18:56 -04:00
LogalDeveloper a9d1217db7 Resolved use of deprecated package in directory cleanup function. 2024-07-06 11:35:48 -04:00
LogalDeveloper 6558c0eab6 Modified errors in directory cleanup function to provide more information. 2024-07-05 20:16:22 -04:00
LogalDeveloper e1d09b7572 Fixed regression by ensuring directory cleanup creates directory if it does not exist. 2024-07-04 20:13:16 -04:00
LogalDeveloper d206429bab Refactored directory cleanup function to remove contents instead of recreating directory. 2024-07-04 19:41:45 -04:00
Owncast b4239af403 Bundle embedded web app 2024-07-04 21:15:40 +00:00
renovate[bot]andGitHub a6bf93f525 chore(deps): update typescript-eslint monorepo to v7.15.0 2024-07-04 18:34:21 +00:00
renovate[bot]andGitHub 6636b686cd fix(deps): update module github.com/microcosm-cc/bluemonday to v1.0.27 2024-07-04 16:18:17 +00:00
Owncast 8b4922abec Commit screenshots 2024-07-04 04:10:47 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2173aa219d fix(deps): update module github.com/shirou/gopsutil/v3 to v4 (#3814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-03 13:44:48 -07:00
renovate[bot]andGitHub 9636fd4619 fix(deps): update module golang.org/x/net to v0.26.0 2024-07-03 18:33:50 +00:00
renovate[bot]andGitHub 2c88db62a5 fix(deps): update module golang.org/x/mod to v0.18.0 2024-07-03 15:59:50 +00:00
renovate[bot]andGitHub df2f548de8 fix(deps): update module golang.org/x/crypto to v0.24.0 2024-07-03 14:21:16 +00:00
Germaine LeeandGitHub 264876cbd3 Update video passthrough warning banner styling (#3810)
Update styling for better contrast and legibility.

Closes #3798
2024-07-03 07:19:46 -07:00
Germaine LeeandGitHub a382a895d0 Update patreon logo to blob from website (#3808)
Updating the Patreon Logo to the blob looking P logo. 

Closes #3807
2024-07-03 07:12:01 -07:00
renovate[bot]andGitHub 3fa7984d3f fix(deps): update module github.com/oschwald/geoip2-golang to v1.11.0 2024-07-03 10:51:32 +00:00
renovate[bot]andGitHub 08abb4f635 fix(deps): update module github.com/go-chi/chi/v5 to v5.1.0 2024-07-03 08:11:32 +00:00
renovate[bot]andGitHub bce170f47c fix(deps): update module github.com/yuin/goldmark-emoji to v1.0.3 2024-07-03 05:10:02 +00:00
Gabe Kangas c963512934 chore: move geoip into services directory 2024-07-02 21:26:43 -07:00
Owncast 228e821a5f Commit screenshots 2024-07-03 04:17:21 +00:00
Gabe Kangas 15d0e3dd1f chore(api): move more types to generated ones 2024-07-02 20:37:58 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
8f9229d47d fix(deps): update module mvdan.cc/xurls to v2 (#3806)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-02 17:34:16 -07:00
renovate[bot]andGitHub 29c4e4a350 fix(deps): update module github.com/yuin/goldmark to v1.7.4 2024-07-02 21:47:26 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
914fd924d1 fix(deps): update module github.com/gorilla/websocket to v1.5.3 (#3803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-02 14:45:42 -07:00
Gabe Kangas dd5fa8dd57 chore(docs): add redocly config file 2024-07-02 14:22:04 -07:00
Owncast dddd6b8f8d Commit updated API documentation 2024-07-02 21:13:05 +00:00
Gabe Kangas e26525549c chore(docs): attempt to hide opts by marking 204options as internal 2024-07-02 14:09:27 -07:00
Owncast ca825e8819 Commit updated API documentation 2024-07-02 21:06:52 +00:00
Gabe Kangas 995235eba7 chore(docs): hide opts methods from docs + add logo 2024-07-02 14:04:46 -07:00
Gabe KangasandGitHub 7b40006841 chore(docs): Update docs gen path + cli 2024-07-02 14:03:45 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
dea1af6990 fix(deps): update module github.com/shirou/gopsutil/v3 to v3.24.5 (#3804)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-02 13:25:30 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
8c31cca4d1 fix(deps): update module github.com/aws/aws-sdk-go to v1.54.13 (#3805)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-02 13:03:17 -07:00
G2G2G2GandGitHub 4c64fa26ac fixing grammar (#3801)
I guess California no speak english
2024-07-02 11:55:25 -07:00
Gabe Kangas 96119efa41 chore(api): start moving some types to the codegen types 2024-07-01 22:17:10 -07:00
Gabe Kangas 93c0a20935 chore(api): reorganize handlers into webserver package 2024-07-01 21:44:51 -07:00
Owncast e200692502 Commit screenshots 2024-07-02 04:11:50 +00:00
Gabe Kangas 5cb4850fce chore(api): reorganize web assets and codegen types+handlers 2024-07-01 20:14:17 -07:00
Gabe KangasandGitHub 2ccd3aad87 User repository (#3795)
* It builds with the new user repository

* fix(test): fix broken test

* fix(api): fix registration endpoint that was broken after the change

* fix(test): update test to reflect new user repository

* fix: use interface type instead of concrete type

* fix: restore commented out code
2024-07-01 18:58:50 -07:00
Owncast 76be78d1b8 Bundle embedded web app 2024-07-01 12:27:41 +00:00
renovate[bot] 41ec8294b2 chore(deps): lock file maintenance 2024-07-01 12:21:50 +00:00
Owncast 8bb9a026b2 Commit screenshots 2024-07-01 04:11:22 +00:00
Owncast 1ad8dc15d6 Bundle embedded web app 2024-07-01 02:59:02 +00:00
mahmed2000andGitHub 43fdadd361 Override default videojs volume panel width (#3796) 2024-06-30 19:53:27 -07:00
Owncast 94f42910da Bundle embedded web app 2024-06-30 16:14:16 +00:00
renovate[bot]andGitHub a80bdf8ea1 chore(deps): update dependency knip to v5.23.1 2024-06-30 12:15:14 +00:00
Owncast d9af841b9d Commit screenshots 2024-06-30 04:11:21 +00:00
Owncast dfdc6cc353 Bundle embedded web app 2024-06-29 08:22:22 +00:00
renovate[bot]andGitHub b9f400c809 chore(deps): update dependency @types/node to v20.14.9 2024-06-29 04:31:47 +00:00
Owncast 7e1d9616b6 Commit screenshots 2024-06-29 04:12:07 +00:00
Owncast 8d5c61b72e Bundle embedded web app 2024-06-28 16:52:15 +00:00
renovate[bot]andGitHub 1c803f636b chore(deps): update dependency knip to v5.23.0 2024-06-28 13:14:09 +00:00
Owncast 9aff5089b9 Commit screenshots 2024-06-28 04:10:55 +00:00
Owncast 9ad0b5c364 Bundle embedded web app 2024-06-27 21:14:11 +00:00
renovate[bot]andGitHub 75ec8e4fce chore(deps): update typescript-eslint monorepo to v7.14.1 2024-06-27 19:11:01 +00:00
Owncast e8a8311a33 Bundle embedded web app 2024-06-27 19:10:08 +00:00
renovate[bot]andGitHub eb11659940 chore(deps): update dependency knip to v5.22.3 2024-06-27 15:40:06 +00:00
Owncast bafff45e72 Bundle embedded web app 2024-06-27 15:38:29 +00:00
renovate[bot]andGitHub 94bb3f3567 chore(deps): update storybook monorepo to v7.6.20 2024-06-27 13:36:04 +00:00
Owncast 0dce87ec5a Bundle embedded web app 2024-06-27 04:25:55 +00:00
Owncast 846ff17221 Commit screenshots 2024-06-27 04:21:33 +00:00
a7e5f20337 Implement admin password hashing with bcrypt (#3754)
* Add bcrypt hashing helpers

* SetAdminPassword now hashes the password before saving it

* BasicAuth now compares the bcrypt hash for the password

* Modify migration2 to avoid a double password hash when upgrading

* Add migration for bcrypt hashed password

* Do not show admin password hash as initial value

* Update api tests to compare the bcrypt hash of the admin password instead

* Remove old admin password api tests

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-06-26 21:20:22 -07:00
renovate[bot]andGitHub 51cd16dcc1 chore(deps): update peter-evans/create-or-update-comment digest to 71ac479 2024-06-26 06:05:38 +00:00
Owncast 07fc051086 Bundle embedded web app 2024-06-26 05:48:16 +00:00
d10aa36317 added span tag to text (#3789)
Co-authored-by: Begench <forgithubtobegench@gmail.com>
Co-authored-by: Begench <93909709+begench-g@users.noreply.github.com>
2024-06-25 22:42:22 -07:00
Owncast 6b7e686707 Commit screenshots 2024-06-26 04:11:49 +00:00
Owncast 67ad3172d3 Bundle embedded web app 2024-06-25 13:54:16 +00:00
renovate[bot]andGitHub 28a9daf8b3 chore(deps): update dependency @types/node to v20.14.8 2024-06-25 10:31:52 +00:00
Owncast 4258353d1b Commit screenshots 2024-06-25 04:11:26 +00:00
Owncast d1a89a8b22 Bundle embedded web app 2024-06-24 15:24:28 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
b367ffcff7 chore(deps): lock file maintenance (#3794)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-24 08:18:45 -07:00
Owncast e2b4929dd1 Bundle embedded web app 2024-06-24 14:15:51 +00:00
renovate[bot]andGitHub 775b4ff6dc chore(deps): update dependency knip to v5.22.2 2024-06-24 10:15:41 +00:00
Owncast 102a8ff168 Bundle embedded web app 2024-06-24 10:14:50 +00:00
renovate[bot]andGitHub 2526b4fd2e chore(deps): update dependency stylelint-config-standard to v36.0.1 2024-06-24 08:35:59 +00:00
Owncast 2ef5d15dfe Commit screenshots 2024-06-24 04:13:23 +00:00
Owncast c4d49280bb Bundle embedded web app 2024-06-24 02:09:04 +00:00
renovate[bot]andGitHub 28b1614769 chore(deps): update dependency @types/node to v20.14.7 2024-06-23 21:19:55 +00:00
Owncast d663b9ff82 Bundle embedded web app 2024-06-23 21:18:29 +00:00
renovate[bot]andGitHub 355bfbf338 chore(deps): update dependency typescript to v5.5.2 2024-06-23 18:38:36 +00:00
Owncast e1431a0afd Bundle embedded web app 2024-06-23 14:18:32 +00:00
renovate[bot]andGitHub 7337382394 chore(deps): update dependency eslint-plugin-jsx-a11y to v6.9.0 2024-06-23 10:01:57 +00:00
Owncast 6b8e50b5bd Commit screenshots 2024-06-23 04:10:52 +00:00
Owncast e0cf689396 Bundle embedded web app 2024-06-22 18:49:41 +00:00
renovate[bot]andGitHub cac299302f chore(deps): update dependency @types/node to v20.14.6 2024-06-22 16:13:48 +00:00
Owncast cba7dcf237 Bundle embedded web app 2024-06-22 16:12:26 +00:00
renovate[bot]andGitHub 2d3d7f284c chore(deps): update dependency knip to v5.22.0 2024-06-22 13:08:24 +00:00
Owncast f914186a85 Bundle embedded web app 2024-06-22 07:10:25 +00:00
renovate[bot]andGitHub 4d540628c3 chore(deps): update dependency cypress to v13.12.0 2024-06-22 05:33:45 +00:00
Owncast 35eb4a42de Commit screenshots 2024-06-22 04:11:16 +00:00
Owncast e2aab760fb Bundle embedded web app 2024-06-22 00:57:11 +00:00
renovate[bot]andGitHub fe9aacc1fd chore(deps): update dependency eslint-plugin-react to v7.34.3 2024-06-21 22:50:41 +00:00
Owncast 1281eceac3 Bundle embedded web app 2024-06-21 22:49:26 +00:00
renovate[bot]andGitHub aeb674e0c3 chore(deps): update dependency knip to v5.21.2 2024-06-21 19:16:41 +00:00
Owncast eaa8434902 Bundle embedded web app 2024-06-21 19:15:09 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
623463f117 chore(deps): update typescript-eslint monorepo to v7.13.1 (#3792)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-21 12:07:47 -07:00
Owncast ac909963c1 Bundle embedded web app 2024-06-21 18:55:06 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
63e0f6da3d chore(deps): update dependency @types/node to v20.14.5 (#3791)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-21 11:47:09 -07:00
Owncast 4c0233a601 Bundle embedded web app 2024-06-21 06:53:48 +00:00
renovate[bot]andGitHub e0d484f2e2 chore(deps): update dependency sass to v1.77.6 2024-06-21 04:26:43 +00:00
Owncast 97e31e63c2 Commit screenshots 2024-06-21 04:12:43 +00:00
renovate[bot]andGitHub e7a6c36055 chore(deps): update alpine docker tag to v3.20.1 2024-06-20 18:42:39 +00:00
Owncast e3c23af080 Bundle embedded web app 2024-06-20 18:42:02 +00:00
renovate[bot]andGitHub 6ebc28c524 chore(deps): update dependency knip to v5.21.0 2024-06-20 17:07:24 +00:00
Owncast 4fcce62342 Commit screenshots 2024-06-20 04:12:09 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4a5f771220 Bump ws from 8.2.3 to 8.17.1 in /test (#3790)
Bumps [ws](https://github.com/websockets/ws) from 8.2.3 to 8.17.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.2.3...8.17.1)

---
updated-dependencies:
- dependency-name: ws
  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-06-19 09:23:01 -07:00
renovate[bot]andGitHub 933d349095 chore(deps): update peter-evans/create-or-update-comment digest to b9ec247 2024-06-19 09:00:50 +00:00
Owncast 28604ab644 Commit screenshots 2024-06-19 04:12:52 +00:00
renovate[bot]andGitHub 5f943b4f46 chore(deps): update peter-evans/create-or-update-comment digest to 1f5fc38 2024-06-18 20:04:24 +00:00
Owncast 9ce1b0eaaf Commit screenshots 2024-06-18 04:12:03 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ef9d36bd03 Bump ws from 7.4.6 to 7.5.10 in /test/automated/hls (#3788)
Bumps [ws](https://github.com/websockets/ws) from 7.4.6 to 7.5.10.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.6...7.5.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 17:12:35 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c23726786a Bump ws from 7.4.6 to 7.5.10 in /test/load (#3787)
Bumps [ws](https://github.com/websockets/ws) from 7.4.6 to 7.5.10.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.6...7.5.10)

---
updated-dependencies:
- dependency-name: ws
  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-06-17 17:00:18 -07:00
Owncast 0ee21156d8 Bundle embedded web app 2024-06-17 12:34:10 +00:00
renovate[bot] a42425e195 chore(deps): lock file maintenance 2024-06-17 12:28:16 +00:00
Owncast 6113ab8573 Commit screenshots 2024-06-17 04:42:51 +00:00
Owncast b54c372489 Commit screenshots 2024-06-16 04:11:36 +00:00
Owncast 246c207ca3 Bundle embedded web app 2024-06-16 00:10:28 +00:00
renovate[bot]andGitHub d7f3628800 chore(deps): update dependency chromatic to v11.5.4 2024-06-15 18:12:48 +00:00
Owncast 1cacdcd8a7 Bundle embedded web app 2024-06-15 18:11:57 +00:00
renovate[bot]andGitHub 8e19719f59 chore(deps): update dependency knip to v5.19.0 2024-06-15 16:02:51 +00:00
Owncast db681b5fa8 Bundle embedded web app 2024-06-15 10:40:33 +00:00
renovate[bot]andGitHub 217d54815b fix(deps): update nextjs monorepo to v14.2.4 2024-06-15 07:09:03 +00:00
Owncast 4f75a3e258 Commit screenshots 2024-06-15 04:22:34 +00:00
Owncast efce6c1f54 Bundle embedded web app 2024-06-15 03:38:15 +00:00
renovate[bot]andGitHub f3e1e14e21 chore(deps): update dependency sass to v1.77.5 2024-06-15 01:41:09 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>mahmed2000
7b867af8c8 Bump braces and jest in /test/automated/api (#3781)
* Bump braces and jest in /test/automated/api

Bumps [braces](https://github.com/micromatch/braces) to 3.0.3 and updates ancestor dependency [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest). These dependencies need to be updated together.


Updates `braces` from 3.0.2 to 3.0.3
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

Updates `jest` from 26.6.3 to 29.7.0
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v29.7.0/packages/jest)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
- dependency-name: jest
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add previous testRunner jasmine2, add ws dependency explicitly (#3784)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mahmed2000 <mahmad2000@protonmail.com>
2024-06-14 11:20:50 -07:00
Owncast b8f4ba211b Bundle embedded web app 2024-06-14 09:27:47 +00:00
renovate[bot]andGitHub 6387896c30 chore(deps): update dependency prettier to v3.3.2 2024-06-14 08:27:55 +00:00
Owncast 7171b621c1 Commit screenshots 2024-06-14 04:12:25 +00:00
Owncast be5bd6f6c2 Bundle embedded web app 2024-06-13 21:29:32 +00:00
renovate[bot]andGitHub 26b12faabf chore(deps): update typescript-eslint monorepo to v7.13.0 2024-06-13 20:39:54 +00:00
Owncast f11cc9eb16 Bundle embedded web app 2024-06-13 04:41:53 +00:00
Owncast 16a1de12db Commit updated Storybook stories 2024-06-13 04:38:58 +00:00
mahmed2000andGitHub f6045fbd63 Add modal state for browser notifications being denied (#3771) 2024-06-12 21:34:53 -07:00
Owncast 23b9c3226e Commit screenshots 2024-06-13 04:21:59 +00:00
Owncast a5f14217f2 Bundle embedded web app 2024-06-12 15:48:15 +00:00
renovate[bot]andGitHub dbff9dd50d chore(deps): update dependency knip to v5.18.2 2024-06-12 14:53:27 +00:00
renovate[bot]andGitHub f274a21271 chore(deps): update peter-evans/create-or-update-comment digest to 86dc0e1 2024-06-12 10:16:46 +00:00
Owncast 3832ea3852 Commit screenshots 2024-06-12 04:11:27 +00:00
Owncast 1b3accf9a6 Bundle embedded web app 2024-06-12 00:54:37 +00:00
renovate[bot]andGitHub f1290f70a2 fix(deps): update dependency yaml to v2.4.5 2024-06-11 22:33:11 +00:00
Owncast 7377ebc798 Bundle embedded web app 2024-06-11 18:16:39 +00:00
renovate[bot]andGitHub 6312e1de98 fix(deps): update dependency yaml to v2.4.4 2024-06-11 16:38:24 +00:00
Owncast 23ecb58471 Bundle embedded web app 2024-06-11 16:37:25 +00:00
renovate[bot]andGitHub fb5464a8c5 chore(deps): update dependency knip to v5.18.1 2024-06-11 13:45:01 +00:00
Owncast a65dd748ea Commit screenshots 2024-06-11 04:11:18 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c77759a4e2 Bump braces from 3.0.2 to 3.0.3 in /test/load (#3777)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 15:05:03 -07:00
Owncast b22033049a Bundle embedded web app 2024-06-10 21:56:50 +00:00
renovate[bot]andGitHub 9d539b6db8 chore(deps): update dependency knip to v5.18.0 2024-06-10 19:29:06 +00:00
Owncast 3f4af08ac3 Bundle embedded web app 2024-06-10 15:21:17 +00:00
renovate[bot]andGitHub 78e1e69161 fix(deps): update dependency @uiw/react-codemirror to v4.22.2 2024-06-10 12:23:49 +00:00
Owncast 93b8bafd8a Bundle embedded web app 2024-06-10 12:22:47 +00:00
renovate[bot]andGitHub aed8e2056c fix(deps): update dependency @uiw/codemirror-theme-bbedit to v4.22.2 2024-06-10 06:04:49 +00:00
Owncast 399c7d38c2 Commit screenshots 2024-06-10 04:12:35 +00:00
Owncast 619489ef19 Bundle embedded web app 2024-06-09 13:42:44 +00:00
renovate[bot]andGitHub 23e2604c22 chore(deps): update dependency chromatic to v11.5.3 2024-06-09 11:12:06 +00:00
Owncast 78757a406b Commit screenshots 2024-06-09 04:12:13 +00:00
Owncast b41fdf848a Bundle embedded web app 2024-06-08 18:10:59 +00:00
renovate[bot]andGitHub cc458f4987 chore(deps): update dependency prettier to v3.3.1 2024-06-08 16:31:17 +00:00
Owncast 721a7826cc Bundle embedded web app 2024-06-08 16:30:17 +00:00
renovate[bot]andGitHub fc7ff3b59f chore(deps): update dependency @babel/core to v7.24.7 2024-06-08 13:20:52 +00:00
Owncast e688ac964c Bundle embedded web app 2024-06-08 13:20:02 +00:00
renovate[bot]andGitHub 115b22df7c chore(deps): update dependency @types/node to v20.14.2 2024-06-08 10:09:43 +00:00
Owncast bd720e32e2 Commit screenshots 2024-06-08 04:11:08 +00:00
Owncast d266f562c9 Bundle embedded web app 2024-06-08 00:55:46 +00:00
renovate[bot]andGitHub 71ba8b13f2 chore(deps): update dependency cypress to v13.11.0 2024-06-07 21:42:06 +00:00
Owncast 513bac52cf Bundle embedded web app 2024-06-07 06:50:04 +00:00
renovate[bot]andGitHub e2b3141058 chore(deps): update dependency @types/node to v20.14.1 2024-06-07 05:16:47 +00:00
Owncast ac9dfa2160 Commit screenshots 2024-06-07 04:11:51 +00:00
Owncast 92da934ddf Bundle embedded web app 2024-06-07 00:30:06 +00:00
renovate[bot]andGitHub 465518282f chore(deps): update dependency chromatic to v11.5.1 2024-06-06 22:30:53 +00:00
Owncast 4d9893d40d Bundle embedded web app 2024-06-06 22:29:27 +00:00
renovate[bot]andGitHub d60c680b7d chore(deps): update typescript-eslint monorepo to v7.12.0 2024-06-06 18:23:53 +00:00
Owncast b8f4c6d4a6 Bundle embedded web app 2024-06-06 12:27:04 +00:00
renovate[bot]andGitHub 3d2304dac8 chore(deps): update dependency knip to v5.17.4 2024-06-06 10:40:24 +00:00
Owncast a78eab0c2d Commit screenshots 2024-06-06 04:15:10 +00:00
Owncast 3e719f2726 Bundle embedded web app 2024-06-06 00:45:44 +00:00
renovate[bot]andGitHub f25ec59220 chore(deps): update dependency @types/node to v20.14.0 2024-06-05 21:14:35 +00:00
Owncast 1dc84d00d6 Bundle embedded web app 2024-06-05 13:05:39 +00:00
renovate[bot]andGitHub 97e318e1c5 fix(deps): update dependency yaml to v2.4.3 2024-06-05 10:11:27 +00:00
renovate[bot]andGitHub 43ee6ef4dd chore(deps): update peter-evans/create-or-update-comment digest to 9bb5d83 2024-06-05 07:47:31 +00:00
Owncast a0d3409422 Commit screenshots 2024-06-05 04:13:04 +00:00
Owncast 732863ec35 Bundle embedded web app 2024-06-04 22:42:12 +00:00
renovate[bot]andGitHub e2e84fb516 chore(deps): update dependency prettier to v3.3.0 2024-06-04 18:25:44 +00:00
Owncast 95c3b205a3 Commit screenshots 2024-06-04 04:11:08 +00:00
Owncast bb70a5c62d Bundle embedded web app 2024-06-04 02:22:51 +00:00
renovate[bot]andGitHub a2c987ee9b chore(deps): update dependency chromatic to v11.5.0 2024-06-03 21:42:44 +00:00
Owncast 882f292aeb Bundle embedded web app 2024-06-03 21:41:04 +00:00
renovate[bot]andGitHub 5a19cd4897 chore(deps): update dependency @types/node to v20.13.0 2024-06-03 20:02:56 +00:00
Owncast c41f83ff94 Bundle embedded web app 2024-06-03 16:53:05 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
63c6484c55 chore(deps): update dependency stylelint-config-standard to v36 (#3676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-03 09:47:11 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Owncast
79fb0d4e17 chore(deps): update dependency stylelint to v16 (#3491)
* chore(deps): update dependency stylelint to v16

* Commit updated API documentation

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Owncast <owncast@owncast.online>
2024-06-03 09:38:59 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Owncast
072fe57daf chore(deps): update dependency stylelint-config-standard-scss to v13 (#3677)
* chore(deps): update dependency stylelint-config-standard-scss to v13

* Commit updated API documentation

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Owncast <owncast@owncast.online>
2024-06-03 09:00:35 -07:00
Owncast 96a40fbc51 Bundle embedded web app 2024-06-03 06:23:44 +00:00
renovate[bot]andGitHub c9d3cd662d chore(deps): update dependency sass to v1.77.4 2024-06-03 04:21:22 +00:00
Owncast 92bc671282 Commit screenshots 2024-06-03 04:13:17 +00:00
Owncast f971b1851b Bundle embedded web app 2024-06-03 01:22:52 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
afdbf3a831 chore(deps): lock file maintenance (#3745)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-02 18:17:13 -07:00
Owncast 499dcaa734 Bundle embedded web app 2024-06-02 22:08:12 +00:00
Gabe Kangas 3f6d7affe8 chore(docs): update readme to call out Lambda test for #3734 2024-06-02 15:06:02 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Gabe Kangas
fa7e0c6ee1 chore(deps): update dependency knip to v5.17.3 (#3751)
* chore(deps): update dependency knip to v5.17.3

* fix(js): resolve errors with new version of knip

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-06-02 15:02:24 -07:00
Owncast 77fac8bd27 Commit screenshots 2024-06-02 04:11:28 +00:00
Owncast d129c33143 Bundle embedded web app 2024-06-02 04:01:05 +00:00
renovate[bot] 6a343272cf chore(deps): update dependency npm to v10.8.1 2024-06-02 03:55:10 +00:00
Owncast 41b39a7863 Bundle embedded web app 2024-06-02 00:09:57 +00:00
renovate[bot]andGitHub c6cc8b7931 chore(deps): update dependency sass to v1.77.3 2024-06-01 23:13:19 +00:00
Owncast 390c20e812 Bundle embedded web app 2024-06-01 23:11:15 +00:00
renovate[bot]andGitHub 5ea6ca5a91 chore(deps): update dependency @types/node to v20.12.13 2024-06-01 20:25:54 +00:00
Owncast e5e526cbe0 Commit screenshots 2024-06-01 04:16:12 +00:00
Owncast e500bd7305 Bundle embedded web app 2024-05-31 18:58:42 +00:00
Germaine LeeandGitHub ef72021f87 Update README.md (#3756)
- Add MIT license badge
- Update TOC
- Update Contributing and add Backer section that lists backers from Open Collective

(PS. practising my html so that they don't go rusty)
2024-05-31 11:55:40 -07:00
renovate[bot]andGitHub 81773e568c fix(deps): update dependency ua-parser-js to v1.0.38 2024-05-31 15:11:55 +00:00
Germaine LeeandGitHub 2a0296116d Update DESIGN.md (#3755)
Removed duplicates of "is a" in sentences.
2024-05-31 08:10:46 -07:00
Owncast 6ac641168d Bundle embedded web app 2024-05-31 13:39:02 +00:00
renovate[bot]andGitHub 74d51ebaa7 chore(deps): update dependency ts-jest to v29.1.4 2024-05-31 10:13:55 +00:00
Owncast 7339d23c0c Bundle embedded web app 2024-05-31 10:13:07 +00:00
renovate[bot]andGitHub 27708665b6 chore(deps): update dependency eslint-plugin-react to v7.34.2 2024-05-31 07:01:46 +00:00
Owncast 2bbe497faf Commit screenshots 2024-05-31 04:11:53 +00:00
Owncast 46143e4722 Bundle embedded web app 2024-05-31 02:13:27 +00:00
renovate[bot]andGitHub 874dc4445a chore(deps): update typescript-eslint monorepo to v7.11.0 2024-05-30 23:02:06 +00:00
mahmed2000andGitHub 414a8aeed8 Rework utils/restendpointhelper to use the new chi router functionality (#3750)
* Remove old implementation, add new function to work with the chi router

* Use new URL Param function to get clientID instead

* Remove usage of old restendpoint functions

* Fix typo in url param name

* Remove unused tests
2024-05-30 12:31:07 -07:00
Owncast a529502809 Bundle embedded web app 2024-05-30 15:26:47 +00:00
renovate[bot]andGitHub d245cc1ebd chore(deps): update dependency chromatic to v11.4.1 2024-05-30 13:14:56 +00:00
Owncast eec8743c18 Commit screenshots 2024-05-30 04:11:37 +00:00
Owncast eb223599f0 Commit screenshots 2024-05-29 04:12:55 +00:00
Owncast 5a773df0d5 Commit screenshots 2024-05-28 04:12:00 +00:00
Owncast 03a7eb9359 Bundle embedded web app 2024-05-27 19:33:42 +00:00
renovate[bot]andGitHub 21fe8b8831 fix(deps): update dependency react-virtuoso to v4.7.11 2024-05-27 15:54:46 +00:00
Owncast 57d569f7d9 Bundle embedded web app 2024-05-27 15:53:31 +00:00
renovate[bot]andGitHub 5af8338a92 chore(deps): update dependency @babel/core to v7.24.6 2024-05-27 14:15:53 +00:00
Owncast 7922610442 Commit screenshots 2024-05-27 04:11:08 +00:00
Owncast cd76c3a22f Bundle embedded web app 2024-05-27 02:29:35 +00:00
renovate[bot]andGitHub 002107f1c8 chore(deps): update dependency @types/react to v18.3.3 2024-05-26 21:44:37 +00:00
Owncast d1035a2967 Commit screenshots 2024-05-26 04:10:21 +00:00
Owncast 0022399cd6 Bundle embedded web app 2024-05-25 19:08:41 +00:00
renovate[bot]andGitHub a796c2e8fe chore(deps): update dependency css-loader to v7.1.2 2024-05-25 16:03:49 +00:00
Owncast 597a88ec09 Commit screenshots 2024-05-25 04:10:26 +00:00
Owncast 8dccb1f641 Bundle embedded web app 2024-05-25 01:52:09 +00:00
renovate[bot]andGitHub 52bbbd85e4 chore(deps): update dependency cypress to v13.10.0 2024-05-24 21:09:14 +00:00
Owncast e2b92d88b2 Bundle embedded web app 2024-05-24 21:07:50 +00:00
renovate[bot]andGitHub 065ba49821 chore(deps): update dependency chromatic to v11.4.0 2024-05-24 15:24:05 +00:00
Owncast ac4019ec0f Bundle embedded web app 2024-05-24 09:38:47 +00:00
renovate[bot]andGitHub 40a05c2dd5 chore(deps): update dependency ts-jest to v29.1.3 2024-05-24 06:54:35 +00:00
Owncast 2caf1de850 Commit screenshots 2024-05-24 04:10:42 +00:00
Owncast 4612540b10 Bundle embedded web app 2024-05-24 00:20:31 +00:00
renovate[bot]andGitHub 6bf3e12b0a chore(deps): update typescript-eslint monorepo to v7.10.0 2024-05-23 21:29:30 +00:00
Owncast c4157bcdfd Commit screenshots 2024-05-23 04:11:06 +00:00
renovate[bot]andGitHub 8f618148c0 chore(deps): update alpine docker tag to v3.20.0 2024-05-22 22:11:57 +00:00
Owncast 6fd81b536a Bundle embedded web app 2024-05-22 13:55:02 +00:00
renovate[bot]andGitHub 61a6f59838 chore(deps): update dependency npm to v10.8.0 2024-05-22 11:50:49 +00:00
Owncast ebb7f539b9 Commit screenshots 2024-05-22 04:17:03 +00:00
Owncast 486e89cd87 Bundle embedded web app 2024-05-22 01:56:32 +00:00
renovate[bot]andGitHub a0446a51e6 fix(deps): update dependency chart.js to v4.4.3 2024-05-21 15:05:49 +00:00
Owncast 77b98bc6bc Commit screenshots 2024-05-21 04:11:01 +00:00
Gabe KangasandGitHub 1f4506d43f Remove link to old broadcast server 2024-05-20 12:44:56 -07:00
Owncast 46369e104b Bundle embedded web app 2024-05-20 17:10:51 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
c9de3d3cc1 chore(deps): update dependency sass to v1.77.2 (#3742)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-20 10:03:10 -07:00
Owncast 5b1069cedb Commit screenshots 2024-05-20 04:11:46 +00:00
Owncast 531c5ca1c7 Bundle embedded web app 2024-05-20 03:21:13 +00:00
renovate[bot]andGitHub bcb4d53195 fix(deps): update dependency sharp to v0.33.4 2024-05-19 22:34:05 +00:00
Owncast 065efc50db Bundle embedded web app 2024-05-19 22:32:42 +00:00
renovate[bot]andGitHub 5dc52158d8 fix(deps): update dependency @uiw/react-codemirror to v4.22.1 2024-05-19 16:27:52 +00:00
Owncast 095e83eecd Bundle embedded web app 2024-05-19 16:26:28 +00:00
renovate[bot]andGitHub 13c3481970 fix(deps): update dependency @uiw/codemirror-theme-bbedit to v4.22.1 2024-05-19 09:41:34 +00:00
Owncast b26ad4e48c Commit screenshots 2024-05-19 04:42:13 +00:00
renovate[bot]andGitHub d62a198ab5 fix(deps): update module golang.org/x/net to v0.25.0 2024-05-18 21:39:19 +00:00
Owncast 193dfdffb5 Bundle embedded web app 2024-05-18 21:37:57 +00:00
renovate[bot]andGitHub 5b88238147 chore(deps): update dependency chromatic to v11.3.5 2024-05-18 16:34:59 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ae977bc748 chore(deps): update golangci/golangci-lint-action action to v6 (#3739)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-18 09:33:27 -07:00
renovate[bot]andGitHub a8c44facfc fix(deps): update module github.com/aws/aws-sdk-go to v1.53.5 2024-05-18 11:12:57 +00:00
renovate[bot]andGitHub bbccbd102e fix(deps): update module github.com/shirou/gopsutil/v3 to v3.24.4 2024-05-18 08:02:23 +00:00
Owncast 34c59d3c34 Commit screenshots 2024-05-18 04:10:29 +00:00
renovate[bot]andGitHub 4e13fdb296 fix(deps): update module github.com/prometheus/client_golang to v1.19.1 2024-05-18 00:09:45 +00:00
Owncast 65a1c6835b Bundle embedded web app 2024-05-18 00:08:20 +00:00
renovate[bot]andGitHub f4c6b2d4a0 chore(deps): update dependency knip to v5.16.0 2024-05-17 23:26:03 +00:00
Owncast e48732acbe Bundle embedded web app 2024-05-17 23:24:42 +00:00
renovate[bot]andGitHub 2fb6ff1637 chore(deps): update dependency mermaid to v10.9.1 2024-05-17 16:33:00 +00:00
Owncast c11fcb550d Bundle embedded web app 2024-05-17 16:31:26 +00:00
renovate[bot]andGitHub d6cd1b0f12 chore(deps): update dependency @types/node to v20.12.12 2024-05-17 06:59:54 +00:00
Owncast 4bca9dd958 Commit screenshots 2024-05-17 04:11:31 +00:00
Owncast 7b9f516305 Bundle embedded web app 2024-05-17 02:55:51 +00:00
renovate[bot]andGitHub 874bb9511e chore(deps): update typescript-eslint monorepo to v7.9.0 2024-05-16 19:59:41 +00:00
Owncast 070e06718c Bundle embedded web app 2024-05-16 19:58:08 +00:00
renovate[bot]andGitHub a740961cd6 chore(deps): update dependency knip to v5.15.1 2024-05-16 07:28:50 +00:00
Owncast 0846e457a1 Commit screenshots 2024-05-16 04:10:35 +00:00
renovate[bot]andGitHub d8fe5dc70e chore(deps): update peter-evans/create-or-update-comment digest to 8da4c50 2024-05-15 06:02:04 +00:00
Owncast 929d27a3b7 Commit screenshots 2024-05-15 04:20:45 +00:00
Owncast dd89543aec Bundle embedded web app 2024-05-14 20:53:08 +00:00
renovate[bot]andGitHub 7d5551c702 chore(deps): update dependency @types/react to v18.3.2 2024-05-14 12:00:04 +00:00
Owncast 1bcdad7502 Bundle embedded web app 2024-05-14 11:58:43 +00:00
renovate[bot]andGitHub f018adb842 chore(deps): update dependency knip to v5.15.0 2024-05-14 07:34:01 +00:00
Owncast 95760cf9b7 Bundle embedded web app 2024-05-14 07:32:47 +00:00
renovate[bot]andGitHub 3e44d29b7a chore(deps): update dependency sass to v1.77.1 2024-05-14 05:12:24 +00:00
Owncast 3864e9870b Commit screenshots 2024-05-14 04:14:05 +00:00
Owncast e24831f4cf Bundle embedded web app 2024-05-13 12:10:39 +00:00
renovate[bot]andGitHub 9e0bb3ff27 chore(deps): update dependency knip to v5.14.0 2024-05-13 11:12:54 +00:00
Owncast 4ceb43b05b Commit screenshots 2024-05-13 04:11:49 +00:00
Owncast dad0690a1f Commit screenshots 2024-05-12 04:11:10 +00:00
Owncast 72ac80fe34 Bundle embedded web app 2024-05-11 21:32:42 +00:00
renovate[bot]andGitHub d9ac224ac2 chore(deps): update dependency chromatic to v11.3.2 2024-05-11 18:29:14 +00:00
Owncast ee391277b2 Bundle embedded web app 2024-05-11 18:27:48 +00:00
renovate[bot]andGitHub a0a8a470ae chore(deps): update dependency @types/node to v20.12.11 2024-05-11 15:45:25 +00:00
Owncast cacc76603d Commit screenshots 2024-05-11 04:10:55 +00:00
Owncast f576230ee6 Bundle embedded web app 2024-05-11 01:45:00 +00:00
renovate[bot]andGitHub 7792102fba chore(deps): update dependency cypress to v13.9.0 2024-05-10 23:12:33 +00:00
Owncast 967b8c5776 Bundle embedded web app 2024-05-10 17:17:35 +00:00
renovate[bot]andGitHub dac9a320a5 chore(deps): update dependency knip to v5.13.0 2024-05-10 14:22:35 +00:00
Owncast 04f0a6b153 Bundle embedded web app 2024-05-10 14:21:11 +00:00
renovate[bot]andGitHub 766a0fb368 chore(deps): update dependency chromatic to v11.3.1 2024-05-10 10:09:19 +00:00
Owncast a3bf2b1466 Bundle embedded web app 2024-05-10 10:07:58 +00:00
renovate[bot]andGitHub da340df397 chore(deps): update dependency sass to v1.77.0 2024-05-10 07:24:34 +00:00
Owncast f142185191 Commit screenshots 2024-05-10 04:10:59 +00:00
Owncast 613f00bd48 Bundle embedded web app 2024-05-09 22:44:24 +00:00
renovate[bot]andGitHub 78e4fc90e4 chore(deps): update dependency @types/node to v20.12.10 2024-05-09 20:14:40 +00:00
Owncast 000c4401c1 Bundle embedded web app 2024-05-09 16:31:39 +00:00
renovate[bot]andGitHub 36537a6ea6 chore(deps): update dependency @types/node to v20.12.9 2024-05-09 14:31:00 +00:00
Owncast 61cf6b1ca0 Bundle embedded web app 2024-05-09 10:51:32 +00:00
renovate[bot]andGitHub 4ce68d553a chore(deps): update dependency knip to v5.12.3 2024-05-09 08:10:00 +00:00
Owncast 43c64c7874 Commit screenshots 2024-05-09 04:11:52 +00:00
renovate[bot]andGitHub a5955b8be6 chore(deps): update peter-evans/create-or-update-comment digest to 34c499d 2024-05-08 10:01:45 +00:00
Owncast 8d4656588d Commit screenshots 2024-05-08 04:10:08 +00:00
Owncast c29b4d5e34 Bundle embedded web app 2024-05-07 14:51:23 +00:00
renovate[bot]andGitHub a44f9c1b6c chore(deps): update dependency knip to v5.12.2 2024-05-07 10:12:56 +00:00
Owncast 1d509d91e2 Commit screenshots 2024-05-07 04:10:39 +00:00
Owncast 9f8108f6ce Bundle embedded web app 2024-05-06 21:42:03 +00:00
renovate[bot]andGitHub 8748611d21 chore(deps): update dependency knip to v5.12.1 2024-05-06 18:25:04 +00:00
Owncast eca4e7b8db Commit screenshots 2024-05-06 04:11:13 +00:00
Owncast cf5b607200 Bundle embedded web app 2024-05-06 00:59:18 +00:00
renovate[bot]andGitHub fe96c22be9 chore(deps): update dependency knip to v5.12.0 2024-05-05 21:22:46 +00:00
Owncast e232358180 Bundle embedded web app 2024-05-05 10:40:29 +00:00
renovate[bot]andGitHub 5eaac5db31 chore(deps): update dependency @types/markdown-it to v14.1.1 2024-05-05 07:24:20 +00:00
Owncast 0525e9aed6 Bundle embedded web app 2024-05-05 07:23:03 +00:00
renovate[bot] ebc0104de1 chore(deps): update storybook monorepo to v7.6.19 2024-05-05 07:17:37 +00:00
Owncast 8ef2edeb05 Commit screenshots 2024-05-05 04:12:29 +00:00
Gabe KangasandGitHub aee4699c8b Update spec path in workflow 2024-05-04 18:39:34 -07:00
Owncast 9007ea2d9a Bundle embedded web app 2024-05-04 22:09:54 +00:00
renovate[bot]andGitHub 19f7fff024 chore(deps): update dependency @types/node to v20.12.8 2024-05-04 19:50:03 +00:00
renovate[bot]andGitHub 0e6ff12ffe chore(deps): update dependency sass to v1.76.0 2024-05-04 05:12:23 +00:00
Owncast aba49b532c Commit screenshots 2024-05-04 04:11:36 +00:00
Owncast 971c77f5c0 Bundle embedded web app 2024-05-04 01:28:04 +00:00
renovate[bot]andGitHub df19bce74d chore(deps): update dependency npm to v10.7.0 2024-05-03 22:42:15 +00:00
Gabe Kangas 1ca7a877e4 Organize new openapi spec with tags and generate API docs with the new spec 2024-05-03 15:39:59 -07:00
Owncast ae76abbb34 Bundle embedded web app 2024-05-03 05:09:18 +00:00
mahmed2000andGitHub d9ee7578d1 Rework stream duration string formatting for the status bar (#3729)
* Use date-fns formatDuration to generate stream duration string

* Remove unneeded pluralize function
2024-05-02 22:03:26 -07:00
Owncast da46a2ff7f Commit screenshots 2024-05-03 04:12:11 +00:00
Owncast 48bb70c1ce Bundle embedded web app 2024-05-03 01:04:23 +00:00
renovate[bot]andGitHub 422a5112ec chore(deps): update typescript-eslint monorepo to v7.8.0 2024-05-02 21:30:17 +00:00
Owncast 48f76fe284 Bundle embedded web app 2024-05-02 21:27:46 +00:00
renovate[bot]andGitHub 8eb7aedba1 chore(deps): update dependency @babel/core to v7.24.5 2024-05-02 19:31:46 +00:00
Owncast 1f22094d82 Bundle embedded web app 2024-05-02 09:14:31 +00:00
renovate[bot]andGitHub 4d8e569b0b fix(deps): update dependency @uiw/react-codemirror to v4.22.0 2024-05-02 07:56:30 +00:00
Owncast 14e02df3d6 Bundle embedded web app 2024-05-02 07:54:32 +00:00
renovate[bot]andGitHub 96cb802b88 fix(deps): update dependency @uiw/codemirror-theme-bbedit to v4.22.0 2024-05-02 05:38:28 +00:00
Owncast 56c1a78889 Commit screenshots 2024-05-02 04:11:03 +00:00
OwncastandGabe Kangas 2ac8e3a475 Commit updated API documentation 2024-05-01 19:22:30 -07:00
+2
Brenden EhlersGabe Kangasdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Owncastmahmed2000Muaz AhmadJeet ChawdaVivian Lim ⭐vivlimdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2513b15b40 Spec-based API (#3653)
* chore(deps): update dependency sass to v1.74.1

* Bundle embedded web app

* chore(deps): update dependency typescript to v5.4.4

* Bundle embedded web app

* Commit screenshots

* fix(deps): update module github.com/yuin/goldmark to v1.7.1

* chore(deps): lock file maintenance (#3662)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* Commit screenshots

* chore(deps): update dependency @types/node to v20.12.5

* Bundle embedded web app

* fix(deps): update module github.com/shirou/gopsutil/v3 to v3.24.3

* fix(deps): update module github.com/aws/aws-sdk-go to v1.51.17

* fix(deps): update module golang.org/x/net to v0.24.0

* fix(deps): update module golang.org/x/mod to v0.17.0

* add get admin chat clients

* comment out old code

* add get admin logs

* add get admin warnings

* add get admin chat

* add post admin chat messageVisibility

* add post admin chat users enabled

* add post admin chat users ipban create

* add post admin chat users ipban remove

* add get admin chat users ipbans

* chore(deps): update dependency sass-loader to v14 (#3674)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update typescript-eslint monorepo to v7 (#3680)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency less-loader to v12 (#3673)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency chromatic to v11 (#3669)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency knip to v5 (#3672)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update nick-fields/retry action to v3 (#3678)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @types/markdown-it to v14 (#3668)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update chromaui/action action to v11 (#3666)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* Bump version number

* Bundle embedded web app

* Bundle embedded web app

* Bundle embedded web app

* fix(deps): update dependency react-virtuoso to v4.7.8

* Commit screenshots

* Bundle embedded web app

* Optionally disable chat rate limiter and add optional chat slur/language filter (#3681)

* feat(chat): basic profanity filter. For #3139

* feat(chat): add setting for disabling chat spam protection. Closes #3523

* feat(chat): wire up the new chat slur filter to admin and chat. Closes #3139

* Bundle embedded web app

* chore(deps): update peter-evans/create-or-update-comment digest to 48bb05b

* fix(deps): update dependency @codemirror/language-data to v6.5.0

* Bundle embedded web app

* chore(deps): update dependency css-loader to v7 (#3670)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* add get admin chat users disabled

* add get admin chat users setModerator

* add get admin chat users moderators

* add get admin followers

* chore(deps): update tj-actions/changed-files action to v44 (#3679)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Fix for copying stream key without revealing it only copies asterisks (#3663)

* asterisk copy fix for #3460

* rename text to keys for consistency + linting fix

* make onCopy spawn an antd message directly, remove redundant copyText function

---------

Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>

* Commit updated Storybook stories

* Bundle embedded web app

* Commit screenshots

* chore(deps): update dependency css-loader to v7.1.0

* Bundle embedded web app

* chore(deps): update typescript-eslint monorepo to v7.6.0

* Bundle embedded web app

* chore(deps): update dependency @types/react to v18.2.75 (#3686)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* stream offline status embed updated (#3684)

* Commit screenshots

* add get admin followers pending

* add get admin followers blocked

* add post admin followers approve

* add 400 error to admin requests

* add post admin emoji upload

* add post admin emoji delete

* add post admin config adminpass

* add post admin config streamkeys

* add post admin config pagecontent

* add post admin config streamtitle

* add post admin config streamtitle

* add post admin config serversummary

* add post admin config offlinemessage

* add post admin config welcomemessage

* add post admin config chat disable

* add post admin config chat joinmessageenabled

* add post admin config chat establishedusermode

* add post admin config chat forbiddenusernames

* add post admin config chat suggestedusernames

* add post admin config video codec

* add post admin config appearance

* Allow react/no-danger

* Commit screenshots

* Change tag to self-closing since the linter complains otherwise (#3690)

Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>

* Bundle embedded web app

* chore(deps): update dependency css-loader to v7.1.1 (#3691)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* chore(deps): update dependency @types/node to v20.12.7 (#3688)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* Commit screenshots

* chore(deps): update dependency npm to v10.5.2

* Bundle embedded web app

* chore(deps): update dependency knip to v5.9.4

* Bundle embedded web app

* chore(deps): update dependency typescript to v5.4.5

* Bundle embedded web app

* prevent css-loader v7 renaming default to _default, needed for storybook (#3692)

Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>

* Bundle embedded web app

* chore(deps): update dependency cypress to v13.7.3

* Bundle embedded web app

* Fix custom runtimeCaching rules, default runtimeCaching rule order (#3685)

Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>

* Bundle embedded web app

* Fixes for aria-live bugs (#3694)

* make the aria-live text adhere to the last message's username

* Wrap lastMessage in an Interweave to handle pre-encoded characters properly

---------

Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>

* Bundle embedded web app

* chore(deps): update dependency @types/react to v18.2.77

* Bundle embedded web app

* chore(deps): update dependency @storybook/addon-styling-webpack to v1 (#3667)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* chore(deps): update dependency storybook-addon-fetch-mock to v2 (#3675)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* chore(deps): update dependency sass to v1.75.0

* Bundle embedded web app

* Commit screenshots

* chore(deps): update dependency sass-loader to v14.2.0

* Bundle embedded web app

* fix(deps): update codemirror

* add get admin webhooks

* add create and delete webhooks

* add get, delete, create access token endpoints

* add update endpoints

* Bundle embedded web app

* fix(deps): update nextjs monorepo to v14.2.0

* Bundle embedded web app

* fix(deps): update nextjs monorepo to v14.2.1

* Bundle embedded web app

* Commit screenshots

* chore(deps): update dependency @types/markdown-it to v14.0.1

* Bundle embedded web app

* chore(deps): update dependency @types/react to v18.2.78

* Bundle embedded web app

* add send message to connected client

* add deprecated send user message

* add send message to 3rd party

* add send user action

* add update external message visibility

* add update external stream title

* add external get chat messages and fixed error with chat

* add external get clients

* remove options request

* add update logo

* add update tags

* add update ffmpeg path

* add update server port

* add update server ip

* add update rtmp port

* add update websocket host override

* add update custom video serving endpoint

* add update nsfw

* add update directory enabled

* Commit screenshots

* add update social handles

* add update stream latency level

* add update stream output variants

* add update s3 config

* add update server url

* add reset yp registration

* add update external action links

* add update custom styles

* add update custom javascript

* add get video metrics

* add update hide viewer count

* add update search indexing

* Change fediAuth message to be more succinct (#3696)

* Change fediAuth message to be more succint

* Update controllers/auth/fediverse/fediverse.go

for -> from

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>

* Mute the 'stream ended' clip's audio (#3630)

* Mute the 'stream ended' clip's audio

The 'stream ended' clip plays at the at the end of every stream broadcast using owncast.
It currently contains audio that peaks at -7.1db. (according to ffmpeg's volumedetect audio filter)
This can result in a bad experience for viewers if the stream that they were just watching had a much lower average volume, and they had turned up their speakers or headphones to compensate. In extreme cases this could theoretically cause harm to viewers and/or their equipment.
As an admin running owncast, there is no way to remove this audio *except* for patching the file. Even if you do patch the file, you need to notify your viewers to clear their browser caches if they have ever seen the clip, because offline.ts has a cache-control max-age header specifying 365 days. The caching of the previous version of this clip is out of scope of this PR.

This issue is discussed in more detail in #1965.

Unlike my previous attempt in #3332, which removed the audio track, this PR *mutes* the audio.

Specifically, I used this ffmpeg command:
```
ffmpeg -i offline.ts -filter:a "volume=0.0" output.ts
```

There are no other modifications to the clip.

* Commit updated API documentation

* feat(video): make compatible muted offline clip. Rename clip as a v2 so it is not cached

* Fix conflict

* force add new offline file

---------

Co-authored-by: vivlim <vivlim@vivl.im>
Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>

* Commit screenshots

* chore(tests): retry cypress tests on failure

* fix(deps): update module github.com/aws/aws-sdk-go to v1.51.23

* chore(deps): update dependency @types/video.js to v7.3.58 (#3705)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* fix(deps): update dependency @codemirror/language-data to v6.5.1 (#3706)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* add update message visibility

* add update user status

* add get moderation user details

* create file for /admin/config endpoints

* chore(deps): update dependency style-loader to v4 (#3703)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Bundle embedded web app

* chore(deps): update typescript-eslint monorepo to v7.7.0

* Bundle embedded web app

* add non-api endpoints to new router

* add set federation enabled

* add set federation private

* chore(tests): attempt to clean up test flakiness (#3710)

* chore(deps): update dependency @types/react to v18.2.79

* add show federation engagement

* add set federation username

* add set federation go live message

* add set federation blocked domains

* add set discord notification configuration

* add set browser notification configuration

* add prometheus endpoint

* add send federation message

* add get federation activities

* add indie auth documentation

* add changes to indie auth server

* Bundle embedded web app

* add fediverse auth

* add spam protection and slur filter

* Commit screenshots

* fix bug with hls endpoint

* Add effect to set media session metadata (#3713)

* Bundle embedded web app

* chore(deps): update dependency sass-loader to v14.2.1

* Bundle embedded web app

* Commit screenshots

* fix(deps): update nextjs monorepo to v14.2.2

* Bundle embedded web app

* Commit screenshots

* fix(deps): update dependency react-virtuoso to v4.7.9

* Bundle embedded web app

* chore(deps): update dependency cypress to v13.8.0

* Bundle embedded web app

* Commit screenshots

* chore(deps): lock file maintenance

* Bundle embedded web app

* add options requests and reformat operationIds

* chore(go): update to go 1.22 (#3708)

* chore(go): update to go 1.22

* fix: install go before running codeql to fix autobild

* chore(test): explicitly set base crosscompile image

* fix(test): do not point at image with incorrect version of Go

* chore: troubleshoot js autoformating in workflow

* Commit screenshots

* add wildcard to index path

* give /admin/ routes precedence

* fix(deps): update dependency @fontsource/poppins to v5.0.14

* Bundle embedded web app

* Bump formidable and artillery in /test/load (#3718)

Removes [formidable](https://github.com/node-formidable/formidable). It's no longer used after updating ancestor dependency [artillery](https://github.com/artilleryio/artillery). These dependencies need to be updated together.

Removes `formidable`

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

---
updated-dependencies:
- dependency-name: formidable
  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>

* Commit screenshots

* chore(deps): update peter-evans/create-or-update-comment digest to da12db3

* fix(deps): update dependency react-virtuoso to v4.7.10

* add verbose logging and update handler precedence

* Bundle embedded web app

* Commit screenshots

* chore(deps): update golangci/golangci-lint-action action to v5 (#3722)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update typescript-eslint monorepo to v7.7.1

* Bundle embedded web app

* Commit screenshots

* chore(deps): update dependency knip to v5.10.0

* Bundle embedded web app

* add federation routers

* updated comments to pass linter

* fix bug with mount

* fix linting errors

* add router logs to log files

* update emoji dir

* fixed linting error

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
Co-authored-by: mahmed2000 <49453542+mahmed2000@users.noreply.github.com>
Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>
Co-authored-by: Jeet Chawda <68128367+Jeetch8@users.noreply.github.com>
Co-authored-by: Vivian Lim ⭐ <1565930+vivlim@users.noreply.github.com>
Co-authored-by: vivlim <vivlim@vivl.im>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 19:22:27 -07:00
Gabe Kangas 1d49801a27 feat(api): cache thumb and preview in memory for fast retrieval. Help address #3462 2024-05-01 18:56:38 -07:00
Owncast aa0b74e236 Bundle embedded web app 2024-05-01 12:39:54 +00:00
renovate[bot]andGitHub 14286f1470 fix(deps): update dependency yaml to v2.4.2 2024-05-01 11:08:56 +00:00
Owncast a0a40864cc Commit screenshots 2024-05-01 04:11:02 +00:00
Owncast b583fa67e1 Bundle embedded web app 2024-04-30 09:23:13 +00:00
renovate[bot]andGitHub 4f43833756 fix(deps): update dependency video.js to v8.12.0 2024-04-30 08:47:44 +00:00
Owncast c5b1a315ae Bundle embedded web app 2024-04-30 05:16:42 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
99dc8fc1c0 chore(deps): update dependency knip to v5.11.0 (#3726)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-29 22:10:04 -07:00
Owncast fc741ba4b6 Commit screenshots 2024-04-30 04:12:25 +00:00
Owncast 2249739f4e Bundle embedded web app 2024-04-30 00:42:40 +00:00
renovate[bot]andGitHub 35f8e989a6 chore(deps): update react monorepo 2024-04-29 23:28:11 +00:00
Owncast 8bf98289f4 Commit screenshots 2024-04-29 04:10:55 +00:00
Owncast 8ef5c8b940 Bundle embedded web app 2024-04-29 00:12:23 +00:00
renovate[bot]andGitHub f4b6ab1a5b chore(deps): update react monorepo 2024-04-28 21:33:23 +00:00
Owncast a04e953a51 Bundle embedded web app 2024-04-28 21:31:14 +00:00
renovate[bot]andGitHub 2600e62626 chore(deps): update dependency npm to v10.6.0 2024-04-28 19:42:34 +00:00
Owncast 759c4460da Commit screenshots 2024-04-28 04:16:45 +00:00
Owncast 5b60588cd5 Bundle embedded web app 2024-04-27 22:23:59 +00:00
renovate[bot]andGitHub fe25271c53 fix(deps): update nextjs monorepo to v14.2.3 2024-04-27 19:53:28 +00:00
Owncast 94ebf11779 Bundle embedded web app 2024-04-27 08:38:41 +00:00
renovate[bot]andGitHub 94fb37c445 fix(deps): update workbox monorepo to v7.1.0 2024-04-27 04:59:04 +00:00
Owncast f642ba4f15 Commit screenshots 2024-04-27 04:10:09 +00:00
Owncast 7b108a5489 Bundle embedded web app 2024-04-27 01:44:33 +00:00
renovate[bot]andGitHub adb39f382c chore(deps): update dependency cypress to v13.8.1 2024-04-26 23:26:33 +00:00
Owncast e140f82f00 Bundle embedded web app 2024-04-26 13:21:24 +00:00
renovate[bot]andGitHub d39e02811a chore(deps): update dependency knip to v5.10.0 2024-04-26 11:16:39 +00:00
Owncast e6c974601e Commit screenshots 2024-04-26 04:10:41 +00:00
Owncast d390b0722d Bundle embedded web app 2024-04-25 21:50:09 +00:00
renovate[bot]andGitHub 14a4808f9a chore(deps): update typescript-eslint monorepo to v7.7.1 2024-04-25 19:32:21 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
c9f50b51a9 chore(deps): update golangci/golangci-lint-action action to v5 (#3722)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-25 08:31:57 -07:00
Owncast 95bb90b6ef Commit screenshots 2024-04-25 04:11:42 +00:00
Owncast 23435c554b Bundle embedded web app 2024-04-24 17:42:37 +00:00
renovate[bot]andGitHub a6ce15727e fix(deps): update dependency react-virtuoso to v4.7.10 2024-04-24 13:58:33 +00:00
renovate[bot]andGitHub 5dbf55bfc7 chore(deps): update peter-evans/create-or-update-comment digest to da12db3 2024-04-24 07:42:04 +00:00
Owncast 708b5b2afc Commit screenshots 2024-04-24 04:10:30 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a33b2d13b6 Bump formidable and artillery in /test/load (#3718)
Removes [formidable](https://github.com/node-formidable/formidable). It's no longer used after updating ancestor dependency [artillery](https://github.com/artilleryio/artillery). These dependencies need to be updated together.


Removes `formidable`

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

---
updated-dependencies:
- dependency-name: formidable
  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-04-23 12:48:16 -07:00
Owncast 983848d706 Bundle embedded web app 2024-04-23 19:00:51 +00:00
renovate[bot]andGitHub 1d25bd703d fix(deps): update dependency @fontsource/poppins to v5.0.14 2024-04-23 17:22:03 +00:00
Owncast b2a7d81d12 Commit screenshots 2024-04-23 04:11:37 +00:00
Gabe Kangas 2acbda625b chore: troubleshoot js autoformating in workflow 2024-04-22 18:11:31 -07:00
Gabe KangasandGitHub 439da013d8 chore(go): update to go 1.22 (#3708)
* chore(go): update to go 1.22

* fix: install go before running codeql to fix autobild

* chore(test): explicitly set base crosscompile image

* fix(test): do not point at image with incorrect version of Go
2024-04-22 13:23:52 -07:00
Owncast ac0204104d Bundle embedded web app 2024-04-22 11:48:06 +00:00
renovate[bot] 4767f7f615 chore(deps): lock file maintenance 2024-04-22 11:42:27 +00:00
Owncast 7e30ed3a1f Commit screenshots 2024-04-22 04:10:42 +00:00
Owncast 1349e5cc44 Bundle embedded web app 2024-04-22 03:54:24 +00:00
renovate[bot]andGitHub a5442f8723 chore(deps): update dependency cypress to v13.8.0 2024-04-22 01:15:55 +00:00
Owncast 09029e1ccd Bundle embedded web app 2024-04-21 10:43:54 +00:00
renovate[bot]andGitHub 9616d69158 fix(deps): update dependency react-virtuoso to v4.7.9 2024-04-21 08:16:58 +00:00
Owncast 6eff5f2ceb Commit screenshots 2024-04-21 04:11:22 +00:00
Owncast 72b2598e89 Bundle embedded web app 2024-04-21 03:59:49 +00:00
renovate[bot]andGitHub a3c457363e fix(deps): update nextjs monorepo to v14.2.2 2024-04-21 00:25:12 +00:00
Owncast e40ba1d725 Commit screenshots 2024-04-20 04:10:20 +00:00
Owncast 7d42f01ecb Bundle embedded web app 2024-04-20 01:32:14 +00:00
renovate[bot]andGitHub 984f5bd562 chore(deps): update dependency sass-loader to v14.2.1 2024-04-19 21:08:33 +00:00
Owncast 65e1a4fc3f Bundle embedded web app 2024-04-19 21:07:12 +00:00
mahmed2000andGitHub 56eef2e601 Add effect to set media session metadata (#3713) 2024-04-19 14:01:50 -07:00
Owncast 8dd43ea6cc Commit screenshots 2024-04-19 04:21:00 +00:00
Owncast 82271a9b08 Bundle embedded web app 2024-04-19 00:09:04 +00:00
renovate[bot]andGitHub 288219d40c chore(deps): update dependency @types/react to v18.2.79 2024-04-18 22:12:23 +00:00
Gabe KangasandGitHub 9b569f3293 chore(tests): attempt to clean up test flakiness (#3710) 2024-04-18 15:11:06 -07:00
Owncast fec0e9d11f Bundle embedded web app 2024-04-18 19:59:12 +00:00
renovate[bot]andGitHub cd8cf2fc7a chore(deps): update typescript-eslint monorepo to v7.7.0 2024-04-18 19:35:52 +00:00
Owncast 304652047d Bundle embedded web app 2024-04-18 19:34:18 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
52243fda36 chore(deps): update dependency style-loader to v4 (#3703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-18 12:27:31 -07:00
Owncast 47ea42d7f2 Bundle embedded web app 2024-04-18 15:28:43 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
30a008d85b fix(deps): update dependency @codemirror/language-data to v6.5.1 (#3706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-18 08:23:18 -07:00
Owncast c6570edfa4 Bundle embedded web app 2024-04-18 15:10:34 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
9fa761b130 chore(deps): update dependency @types/video.js to v7.3.58 (#3705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-18 08:04:51 -07:00
renovate[bot]andGitHub 4ac08252c1 fix(deps): update module github.com/aws/aws-sdk-go to v1.51.23 2024-04-18 10:18:31 +00:00
Gabe Kangas 799e880718 chore(tests): retry cypress tests on failure 2024-04-17 22:10:39 -07:00
Owncast 201689cb66 Commit screenshots 2024-04-18 04:10:59 +00:00
f81e8dcda7 Mute the 'stream ended' clip's audio (#3630)
* Mute the 'stream ended' clip's audio

The 'stream ended' clip plays at the at the end of every stream broadcast using owncast.
It currently contains audio that peaks at -7.1db. (according to ffmpeg's volumedetect audio filter)
This can result in a bad experience for viewers if the stream that they were just watching had a much lower average volume, and they had turned up their speakers or headphones to compensate. In extreme cases this could theoretically cause harm to viewers and/or their equipment.
As an admin running owncast, there is no way to remove this audio *except* for patching the file. Even if you do patch the file, you need to notify your viewers to clear their browser caches if they have ever seen the clip, because offline.ts has a cache-control max-age header specifying 365 days. The caching of the previous version of this clip is out of scope of this PR.

This issue is discussed in more detail in #1965.

Unlike my previous attempt in #3332, which removed the audio track, this PR *mutes* the audio.

Specifically, I used this ffmpeg command:
```
ffmpeg -i offline.ts -filter:a "volume=0.0" output.ts
```

There are no other modifications to the clip.

* Commit updated API documentation

* feat(video): make compatible muted offline clip. Rename clip as a v2 so it is not cached

* Fix conflict

* force add new offline file

---------

Co-authored-by: vivlim <vivlim@vivl.im>
Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-04-17 11:44:19 -07:00
d3b0cb261a Change fediAuth message to be more succinct (#3696)
* Change fediAuth message to be more succint

* Update controllers/auth/fediverse/fediverse.go

for -> from

---------

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2024-04-17 11:01:32 -07:00
Owncast 3fd18a73ae Commit screenshots 2024-04-17 04:12:19 +00:00
Owncast 6c02683122 Bundle embedded web app 2024-04-16 12:53:44 +00:00
renovate[bot]andGitHub 26f602a6a5 chore(deps): update dependency @types/react to v18.2.78 2024-04-16 09:44:05 +00:00
Owncast 4380b6c8d9 Bundle embedded web app 2024-04-16 09:42:45 +00:00
renovate[bot]andGitHub 5a9075d017 chore(deps): update dependency @types/markdown-it to v14.0.1 2024-04-16 08:21:47 +00:00
Owncast e059c6bcdf Commit screenshots 2024-04-16 04:11:19 +00:00
Owncast 0b93575209 Bundle embedded web app 2024-04-16 03:09:06 +00:00
renovate[bot]andGitHub 07b5f3b96f fix(deps): update nextjs monorepo to v14.2.1 2024-04-15 22:38:08 +00:00
Owncast 75bab43954 Bundle embedded web app 2024-04-15 18:45:22 +00:00
renovate[bot]andGitHub 717dd67ffc fix(deps): update nextjs monorepo to v14.2.0 2024-04-15 17:02:11 +00:00
Owncast a7922c1721 Bundle embedded web app 2024-04-15 17:00:28 +00:00
renovate[bot]andGitHub dc742e3d10 fix(deps): update codemirror 2024-04-15 11:59:19 +00:00
Owncast 83ed34d422 Bundle embedded web app 2024-04-15 11:57:36 +00:00
renovate[bot]andGitHub 1967da067e chore(deps): update dependency sass-loader to v14.2.0 2024-04-15 06:40:43 +00:00
Owncast b7cd4b6323 Commit screenshots 2024-04-15 04:24:19 +00:00
Owncast 3a11e5a9ab Bundle embedded web app 2024-04-15 03:59:22 +00:00
renovate[bot]andGitHub fc997fd49b chore(deps): update dependency sass to v1.75.0 2024-04-15 02:25:22 +00:00
Owncast 3535823e4d Bundle embedded web app 2024-04-15 02:23:55 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
f626dbafe0 chore(deps): update dependency storybook-addon-fetch-mock to v2 (#3675)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-14 19:18:36 -07:00
Owncast cb3d7ea46e Bundle embedded web app 2024-04-15 02:00:43 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
0262b449c2 chore(deps): update dependency @storybook/addon-styling-webpack to v1 (#3667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-14 18:55:16 -07:00
Owncast ecd68e4d4b Bundle embedded web app 2024-04-15 01:34:25 +00:00
renovate[bot] 6afa3ab832 chore(deps): update dependency @types/react to v18.2.77 2024-04-15 01:29:00 +00:00
Owncast 5762e91a7c Bundle embedded web app 2024-04-15 01:12:22 +00:00
5a145eb407 Fixes for aria-live bugs (#3694)
* make the aria-live text adhere to the last message's username

* Wrap lastMessage in an Interweave to handle pre-encoded characters properly

---------

Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>
2024-04-14 18:06:29 -07:00
Owncast 1d3e52d2bb Bundle embedded web app 2024-04-14 22:48:24 +00:00
1a5e7cb113 Fix custom runtimeCaching rules, default runtimeCaching rule order (#3685)
Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>
2024-04-14 15:41:39 -07:00
Owncast aa4a3024d9 Bundle embedded web app 2024-04-14 20:17:11 +00:00
renovate[bot]andGitHub 06a40a346e chore(deps): update dependency cypress to v13.7.3 2024-04-14 19:49:39 +00:00
Owncast 59dc83a02a Bundle embedded web app 2024-04-14 19:47:56 +00:00
57135960cb prevent css-loader v7 renaming default to _default, needed for storybook (#3692)
Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>
2024-04-14 12:40:54 -07:00
Owncast 12a6ebed54 Bundle embedded web app 2024-04-14 14:17:48 +00:00
renovate[bot]andGitHub 16eb1754c7 chore(deps): update dependency typescript to v5.4.5 2024-04-14 10:18:21 +00:00
Owncast cd06207866 Bundle embedded web app 2024-04-14 10:16:56 +00:00
renovate[bot] f38d7bb6c8 chore(deps): update dependency knip to v5.9.4 2024-04-14 10:11:24 +00:00
Owncast ec85a0f264 Bundle embedded web app 2024-04-14 07:33:27 +00:00
renovate[bot]andGitHub dc98ac0fe4 chore(deps): update dependency npm to v10.5.2 2024-04-14 04:42:58 +00:00
Owncast 371a42c49d Commit screenshots 2024-04-14 04:25:23 +00:00
Owncast d9f22f99b1 Bundle embedded web app 2024-04-14 00:22:43 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
10147cdf9f chore(deps): update dependency @types/node to v20.12.7 (#3688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-13 17:16:21 -07:00
Owncast 95819ca1a5 Bundle embedded web app 2024-04-14 00:02:41 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
f7906cd7ce chore(deps): update dependency css-loader to v7.1.1 (#3691)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-13 16:55:23 -07:00
Owncast 859ff3f082 Bundle embedded web app 2024-04-13 20:59:19 +00:00
9c3a4acb16 Change tag to self-closing since the linter complains otherwise (#3690)
Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>
2024-04-13 13:51:43 -07:00
Owncast 921f4f9e4c Commit screenshots 2024-04-13 04:10:16 +00:00
Gabe KangasandGitHub 30d51d81ac Allow react/no-danger 2024-04-12 10:20:06 -07:00
Owncast 3e1cd98e5f Commit screenshots 2024-04-12 04:10:40 +00:00
Jeet ChawdaandGitHub 39078babbe stream offline status embed updated (#3684) 2024-04-11 19:57:25 -07:00
Owncast 3cafe0533c Bundle embedded web app 2024-04-12 01:53:39 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2731b2a26e chore(deps): update dependency @types/react to v18.2.75 (#3686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-11 18:48:20 -07:00
Owncast ac69fc38b0 Bundle embedded web app 2024-04-12 01:31:52 +00:00
renovate[bot]andGitHub e604d52f64 chore(deps): update typescript-eslint monorepo to v7.6.0 2024-04-11 22:51:26 +00:00
Owncast 7c11c91324 Bundle embedded web app 2024-04-11 22:49:34 +00:00
renovate[bot]andGitHub 2c6bba025c chore(deps): update dependency css-loader to v7.1.0 2024-04-11 19:47:06 +00:00
Owncast 87dbb58711 Commit screenshots 2024-04-11 04:11:18 +00:00
Owncast aa9d799128 Bundle embedded web app 2024-04-10 20:21:21 +00:00
Owncast f8a3d0559b Commit updated Storybook stories 2024-04-10 20:19:25 +00:00
de8bc8d41b Fix for copying stream key without revealing it only copies asterisks (#3663)
* asterisk copy fix for #3460

* rename text to keys for consistency + linting fix

* make onCopy spawn an antd message directly, remove redundant copyText function

---------

Co-authored-by: Muaz Ahmad <mahmad2000@protonmail.com>
2024-04-10 13:15:53 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
9bbb09c911 chore(deps): update tj-actions/changed-files action to v44 (#3679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-10 12:35:43 -07:00
Owncast e50e4473dd Bundle embedded web app 2024-04-10 15:18:38 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
4b52800061 chore(deps): update dependency css-loader to v7 (#3670)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-10 08:13:06 -07:00
Owncast c618fb0a4e Bundle embedded web app 2024-04-10 14:37:39 +00:00
renovate[bot]andGitHub ecdfd977dd fix(deps): update dependency @codemirror/language-data to v6.5.0 2024-04-10 09:53:30 +00:00
renovate[bot]andGitHub 92aa873365 chore(deps): update peter-evans/create-or-update-comment digest to 48bb05b 2024-04-10 05:33:12 +00:00
Owncast e6e73d1750 Bundle embedded web app 2024-04-10 05:32:15 +00:00
Gabe KangasandGitHub a450e62397 Optionally disable chat rate limiter and add optional chat slur/language filter (#3681)
* feat(chat): basic profanity filter. For #3139

* feat(chat): add setting for disabling chat spam protection. Closes #3523

* feat(chat): wire up the new chat slur filter to admin and chat. Closes #3139
2024-04-09 22:25:41 -07:00
Owncast 04eaf8c20e Bundle embedded web app 2024-04-10 04:15:02 +00:00
Owncast a7134edafb Commit screenshots 2024-04-10 04:10:32 +00:00
renovate[bot] 7837c7656c fix(deps): update dependency react-virtuoso to v4.7.8 2024-04-10 04:09:42 +00:00
Owncast 5792343eef Bundle embedded web app 2024-04-10 02:23:43 +00:00
Owncast c066cd9035 Bundle embedded web app 2024-04-10 01:58:24 +00:00
Owncast 9e533498a3 Bundle embedded web app 2024-04-10 01:57:05 +00:00
Gabe KangasandGitHub 68645ab8e7 Bump version number 2024-04-09 18:56:31 -07:00
Owncast cd3015466e Bundle embedded web app 2024-04-10 01:51:30 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
91788c9087 chore(deps): update chromaui/action action to v11 (#3666)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:25:55 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
a02e9a5601 chore(deps): update dependency @types/markdown-it to v14 (#3668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:25:22 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
a1a879a768 chore(deps): update nick-fields/retry action to v3 (#3678)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:23:40 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
e3c93d9c59 chore(deps): update dependency knip to v5 (#3672)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:22:43 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
58e11d4880 chore(deps): update dependency chromatic to v11 (#3669)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:21:53 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
568b9cace4 chore(deps): update dependency less-loader to v12 (#3673)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:21:21 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
dcba3818c6 chore(deps): update typescript-eslint monorepo to v7 (#3680)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:20:47 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
50ee6426b6 chore(deps): update dependency sass-loader to v14 (#3674)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-09 18:20:13 -07:00
renovate[bot]andGitHub a06db37cd9 fix(deps): update module golang.org/x/mod to v0.17.0 2024-04-09 16:18:22 +00:00
renovate[bot]andGitHub 2d03ea5691 fix(deps): update module golang.org/x/net to v0.24.0 2024-04-09 13:52:36 +00:00
renovate[bot]andGitHub 9c973f3d5b fix(deps): update module github.com/aws/aws-sdk-go to v1.51.17 2024-04-09 10:14:48 +00:00
renovate[bot]andGitHub f4cae0f1dd fix(deps): update module github.com/shirou/gopsutil/v3 to v3.24.3 2024-04-09 06:39:54 +00:00
Owncast db0ef04127 Bundle embedded web app 2024-04-09 06:38:39 +00:00
renovate[bot]andGitHub 43e18267b6 chore(deps): update dependency @types/node to v20.12.5 2024-04-09 04:17:47 +00:00
Owncast 50d6443a13 Commit screenshots 2024-04-09 04:12:54 +00:00
Owncast d7bee391d6 Bundle embedded web app 2024-04-09 01:33:07 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
75b4feddeb chore(deps): lock file maintenance (#3662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-08 18:20:01 -07:00
renovate[bot]andGitHub fd271c23f0 fix(deps): update module github.com/yuin/goldmark to v1.7.1 2024-04-08 22:54:43 +00:00
Owncast e0fcce3efb Commit screenshots 2024-04-08 04:10:43 +00:00
Owncast 10cb2e4b5b Bundle embedded web app 2024-04-07 22:07:22 +00:00
renovate[bot]andGitHub ec8bfb3e0f chore(deps): update dependency typescript to v5.4.4 2024-04-07 19:28:08 +00:00
562 changed files with 35794 additions and 26248 deletions
+3 -3
View File
@@ -7,7 +7,7 @@ A collection of design contribution guidelines and resources for the Owncast int
## 👋 Welcome ## 👋 Welcome
Owncast is a is a live streaming and chat server targeted to anybody who has live streaming needs. This means anybody from corporate events, government meetings, game streamers, musicians, churches, TV stations, and more. Owncast is a live streaming and chat server targeted to anybody who has live streaming needs. This means anybody from corporate events, government meetings, game streamers, musicians, churches, TV stations, and more.
Read the detailed [product definition](https://github.com/owncast/owncast/blob/develop/docs/product-definition.md) to learn more. Read the detailed [product definition](https://github.com/owncast/owncast/blob/develop/docs/product-definition.md) to learn more.
@@ -25,7 +25,7 @@ Read the detailed [product definition](https://github.com/owncast/owncast/blob/d
## 🎭 Target audience ## 🎭 Target audience
Owncast is a is a live streaming and chat server targeted to anybody who has live streaming needs. This means anything from corporate events, government meetings, game streams, concerts, TV stations, and more. Owncast is a live streaming and chat server targeted to anybody who has live streaming needs. This means anything from corporate events, government meetings, game streams, concerts, TV stations, and more.
## 🧑‍🎨 Product design opportunities ## 🧑‍🎨 Product design opportunities
@@ -57,7 +57,7 @@ https://owncast.online/components/?path=%2Fdocs%2Fowncast-styles-colors-componen
### Design Files, Screenshots, etc ### Design Files, Screenshots, etc
We do not currently have any design files that fully represent the state of We do not currently have any design files that fully represent the state of
the Owncast interface. However going forward it would be nice to resolve this the Owncast interface. However, going forward it would be nice to resolve this
and collaborate on designs. and collaborate on designs.
We do have a [PenPot organization](https://design.penpot.app/#/dashboard/team/8373f780-f255-11ec-b774-f940e3befd53/projects). Please ask for access. We do have a [PenPot organization](https://design.penpot.app/#/dashboard/team/8373f780-f255-11ec-b774-f940e3befd53/projects). Please ask for access.
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
issues: write issues: write
steps: steps:
- name: Add comment - name: Add comment
uses: peter-evans/create-or-update-comment@8d281ecfdf52405614030db6e4265486aa119f52 uses: peter-evans/create-or-update-comment@71ac479718f8aed504782bc920d802da994c05fa
with: with:
issue-number: ${{ github.event.issue.number }} issue-number: ${{ github.event.issue.number }}
body: | body: |
@@ -35,7 +35,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Run API tests - name: Run API tests
uses: nick-fields/retry@v2 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 10 timeout_minutes: 10
max_attempts: 3 max_attempts: 3
+2 -2
View File
@@ -40,14 +40,14 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.21' go-version: '1.22'
cache: true cache: true
- name: Install Google Chrome - name: Install Google Chrome
run: sudo apt-get update && sudo apt-get install google-chrome-stable run: sudo apt-get update && sudo apt-get install google-chrome-stable
- name: Run Browser tests - name: Run Browser tests
uses: nick-fields/retry@v2 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 20 timeout_minutes: 20
max_attempts: 3 max_attempts: 3
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
- name: Publish to Chromatic - name: Publish to Chromatic
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }} if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }}
uses: chromaui/action@v1 uses: chromaui/action@v11
# Chromatic GitHub Action options # Chromatic GitHub Action options
with: with:
workingDir: web workingDir: web
+6 -1
View File
@@ -9,7 +9,7 @@
# the `language` matrix defined below to confirm you have the correct set of # the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages. # supported CodeQL languages.
# #
name: "CodeQL" name: 'CodeQL'
on: on:
push: push:
@@ -39,6 +39,11 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v3
+1 -1
View File
@@ -49,7 +49,7 @@ jobs:
EARTHLY_BUILD_TAG: 'nightly' EARTHLY_BUILD_TAG: 'nightly'
EARTHLY_BUILD_BRANCH: 'develop' EARTHLY_BUILD_BRANCH: 'develop'
EARTHLY_PUSH: true EARTHLY_PUSH: true
uses: nick-fields/retry@v2 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 20 timeout_minutes: 20
max_attempts: 3 max_attempts: 3
@@ -14,14 +14,14 @@ jobs:
- name: Run redoc on openapi.yaml - name: Run redoc on openapi.yaml
run: | run: |
npx redoc-cli bundle openapi.yaml -o docs/api/index.html --options '{"hideHostname": true, "pathInMiddlePanel": true}' npx @redocly/cli --config docs/api/redocly.yaml build-docs openapi.yaml -o docs/api/index.html
- name: Commit changes - name: Commit changes
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v9
with: with:
author_name: Owncast author_name: Owncast
author_email: owncast@owncast.online author_email: owncast@owncast.online
message: "Commit updated API documentation" message: 'Commit updated API documentation'
add: "docs/api/index.html" add: 'docs/api/index.html'
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+2 -2
View File
@@ -28,11 +28,11 @@ jobs:
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.21' go-version: '1.22'
cache: true cache: true
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v4 uses: golangci/golangci-lint-action@v6
with: with:
only-new-issues: true only-new-issues: true
args: --timeout=3m args: --timeout=3m
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.20.x, 1.21.x] go-version: [1.21.x, 1.22.x]
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
+3 -3
View File
@@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.21' go-version: '1.22'
cache: true cache: true
- name: Cache node modules - name: Cache node modules
@@ -43,14 +43,14 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: Local stroage - name: Local stroage
uses: nick-fields/retry@v2 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 10 timeout_minutes: 10
max_attempts: 3 max_attempts: 3
command: cd test/automated/hls && ./run.sh command: cd test/automated/hls && ./run.sh
- name: S3 storage - name: S3 storage
uses: nick-fields/retry@v2 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 10 timeout_minutes: 10
max_attempts: 3 max_attempts: 3
@@ -39,7 +39,7 @@ jobs:
- name: Get changed files - name: Get changed files
id: changed-files-yaml id: changed-files-yaml
uses: tj-actions/changed-files@v41 uses: tj-actions/changed-files@v44
with: with:
path: 'web' path: 'web'
files_ignore: | files_ignore: |
@@ -72,6 +72,9 @@ jobs:
if: steps.changed-files-yaml.outputs.src_any_changed == 'true' if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: npx prettier --write ${{ steps.changed-files-yaml.outputs.src_all_changed_files }} run: npx prettier --write ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}
- name: Debug changed files output
run: 'pwd && echo "Changed files: ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}"'
- name: Commit changes - name: Commit changes
if: steps.changed-files-yaml.outputs.src_any_changed == 'true' if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v9
@@ -80,6 +83,7 @@ jobs:
author_email: owncast@owncast.online author_email: owncast@owncast.online
message: 'Javascript formatting autofixes' message: 'Javascript formatting autofixes'
add: ${{ steps.changed-files-yaml.outputs.src_all_changed_files }} add: ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}
cwd: './web' # Ensure this is the correct relative directory
pull: '--rebase --autostash' pull: '--rebase --autostash'
unused-code: unused-code:
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: '1.21' go-version: '1.22'
cache: true cache: true
- name: Cache node modules - name: Cache node modules
@@ -33,7 +33,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: Automate screenshots - name: Automate screenshots
uses: nick-fields/retry@v2 uses: nick-fields/retry@v3
with: with:
timeout_minutes: 10 timeout_minutes: 10
max_attempts: 4 max_attempts: 4
+2 -2
View File
@@ -5,7 +5,7 @@ run:
# Define the Go version limit. # Define the Go version limit.
# Mainly related to generics support in go1.18. # Mainly related to generics support in go1.18.
# Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.18 # Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.18
go: '1.21' go: '1.22'
issues: issues:
# The linter has a default list of ignorable errors. Turning this on will enable that list. # The linter has a default list of ignorable errors. Turning this on will enable that list.
@@ -69,7 +69,7 @@ linters-settings:
gosimple: gosimple:
# Select the Go version to target. The default is '1.13'. # Select the Go version to target. The default is '1.13'.
go: '1.21' go: '1.22'
# https://staticcheck.io/docs/options#checks # https://staticcheck.io/docs/options#checks
checks: ['all'] checks: ['all']
+1 -1
View File
@@ -22,7 +22,7 @@ ENV NAME=${NAME}
RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags "-extldflags \"-static\" -s -w -X github.com/owncast/owncast/config.GitCommit=$GIT_COMMIT -X github.com/owncast/owncast/config.VersionNumber=$VERSION -X github.com/owncast/owncast/config.BuildPlatform=$NAME" -o owncast . RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags "-extldflags \"-static\" -s -w -X github.com/owncast/owncast/config.GitCommit=$GIT_COMMIT -X github.com/owncast/owncast/config.VersionNumber=$VERSION -X github.com/owncast/owncast/config.BuildPlatform=$NAME" -o owncast .
# Create the image by copying the result of the build into a new alpine image # Create the image by copying the result of the build into a new alpine image
FROM alpine:3.19.1 FROM alpine:3.20.1
RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates && update-ca-certificates RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates && update-ca-certificates
RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast
+60 -17
View File
@@ -1,37 +1,48 @@
<br />
<p align="center"> <p align="center">
<a href="https://github.com/owncast/owncast" alt="Owncast"> <a href="https://github.com/owncast/owncast" alt="Owncast">
<img src="https://owncast.online/images/logo.png" alt="Logo" width="200"> <img src="https://owncast.online/images/logo.png" alt="Owncast Logo" width="200">
</a>
</p>
<p align="center">
<strong>Take control over your content and stream it yourself.</strong>
</p>
<br/>
<p align="center">
<a href="https://github.com/owncast/owncast/blob/develop/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License" />
</a> </a>
</p> </p>
<br/> <br/>
<p align="center"> <p align="center">
<strong>Take control over your content and stream it yourself.</strong>
<br />
<a href="https://owncast.online"><strong>Explore the docs »</strong></a> <a href="https://owncast.online"><strong>Explore the docs »</strong></a>
<br /> <br />
<a href="https://watch.owncast.online/">View Demo</a> <a href="https://watch.owncast.online/">View Demo</a>
· ·
<a href="https://broadcast.owncast.online/">Use Our Server for Testing</a>
·
<a href="https://owncast.online/faq/">FAQ</a> <a href="https://owncast.online/faq/">FAQ</a>
· ·
<a href="https://github.com/owncast/owncast/issues">Report Bug</a> <a href="https://github.com/owncast/owncast/issues">Report Bug</a>
</p> </p>
</p>
<!-- TABLE OF CONTENTS --> <!-- TABLE OF CONTENTS -->
## Table of Contents ## Table of Contents
- [About the Project](#about-the-project) - 📒 [About the Project](#about-the-project)
- [Getting Started](#getting-started) - 🚀 [Getting Started](#getting-started)
- [Use with your broadcasting software](#use-with-your-existing-broadcasting-software) - 👨‍💻 [Use with your broadcasting software](#use-with-your-existing-broadcasting-software)
- [Building from source](#building-from-source) - 🛠 [Building from source](#building-from-source)
- [Contributing](#contributing) - 🚨 [Important note about source code and the develop branch](#important-note-about-source-code-and-the-develop-branch)
- [License](#license) - 🗄️ [Backend](#backend)
- ⚛️ [Frontend](#frontend)
- 👏 [Contributing](#contributing)
- 💵 [Donors](#donors)
- 📝 [License](#license)
- [Contact](#contact) - [Contact](#contact)
<!-- ABOUT THE PROJECT --> <!-- ABOUT THE PROJECT -->
@@ -95,7 +106,7 @@ The Owncast backend is a service written in Go.
1. Ensure you have prerequisites installed. 1. Ensure you have prerequisites installed.
- C compiler, such as [GCC compiler](https://gcc.gnu.org/install/download.html) or a [Musl-compatible compiler](https://musl.libc.org/) - C compiler, such as [GCC compiler](https://gcc.gnu.org/install/download.html) or a [Musl-compatible compiler](https://musl.libc.org/)
- [ffmpeg](https://ffmpeg.org/download.html) - [ffmpeg](https://ffmpeg.org/download.html)
1. Install the [Go toolchain](https://golang.org/dl/) (1.21 or above). 1. Install the [Go toolchain](https://golang.org/dl/) (1.22 or above).
1. Clone the repo. `git clone https://github.com/owncast/owncast` 1. Clone the repo. `git clone https://github.com/owncast/owncast`
1. `go run main.go` will run from the source. 1. `go run main.go` will run from the source.
1. Visit `http://yourserver:8080` to access the web interface or `http://yourserver:8080/admin` to access the admin. 1. Visit `http://yourserver:8080` to access the web interface or `http://yourserver:8080/admin` to access the admin.
@@ -117,18 +128,50 @@ And while we have a small team of kind, talented and thoughtful volunteers, we h
We abide by our [Code of Conduct](https://owncast.online/contribute/) and feel strongly about open, appreciative, and empathetic people joining us. We abide by our [Code of Conduct](https://owncast.online/contribute/) and feel strongly about open, appreciative, and empathetic people joining us.
We’ve been very lucky to have this so far, so maybe you can help us with your skills and passion, too! We’ve been very lucky to have this so far, so maybe you can help us with your skills and passion, too!
If you're new to the project, maybe you'd be interested in looking at [![Good First Issue](https://img.shields.io/github/issues/owncast/owncast/good%20first%20issue.svg)](https://github.com/owncast/owncast/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
There is a larger, more detailed, and more up-to-date [guide for helping contribute to Owncast on our website](https://owncast.online/help/). There is a larger, more detailed, and more up-to-date [guide for helping contribute to Owncast on our website](https://owncast.online/help/).
### Donors
The Owncast project is possible thanks to the people who make a donation to support us and our work.
Thank you to all our donors who help keep Owncast running by donating on OpenCollective. You can support this project by [becoming a backer/sponsor](https://opencollective.com/owncast#suppor).
<div>
<a href="https://opencollective.com/owncast#support">
<img alt="GitHub issues by-label" src="https://opencollective.com/owncast/tiers/backers.svg?avatarHeight=36&width=600" alt="Backer button">
</a>
</div>
<!-- LICENSE --> <!-- LICENSE -->
## License ## License
Distributed under the MIT License. See `LICENSE` for more information. Distributed under the MIT License. See `LICENSE` for more information.
## Supported by ## Support
- This project is tested with [BrowserStack](https://browserstack.com).
<ul style="font-size:21px; color:black; ">
<li>Browser testing via <a
href="https://www.lambdatest.com/" target="_blank"><img
src="https://www.lambdatest.com/support/img/logo.svg"
style="vertical-align: middle;margin-left:5px" width="147" height="26"
/></a></li>
<li>Project chat provided by
<a href="https://rocket.chat" target="_blank">
<img src="https://owncast.online/images/sponsors/rocketchat.png" width="147" height="26" style="vertical-align: middle;margin-left:5px">
</a>
</li>
<li>CDN services by
<a href="https://fastly.com" target="_blank">
<img src="https://owncast.online/images/sponsors/fastly.png" height="26" style="vertical-align: middle;margin-left:5px">
</a>
</li>
<li>UI testing with Chromatic
<a href="https://chromatic.com" target="_blank">
<img src="https://owncast.online/images/sponsors/chromatic.png" height="26" style="vertical-align: middle;margin-left:5px">
</a>
</li>
</ul>
<!-- CONTACT --> <!-- CONTACT -->
## Contact ## Contact
-1
View File
@@ -17,7 +17,6 @@ func Start(datastore *data.Datastore) {
persistence.Setup(datastore) persistence.Setup(datastore)
workerpool.InitOutboundWorkerPool() workerpool.InitOutboundWorkerPool()
inbox.InitInboxWorkerPool() inbox.InitInboxWorkerPool()
StartRouter()
// Generate the keys for signing federated activity if needed. // Generate the keys for signing federated activity if needed.
if data.GetPrivateKey() == "" { if data.GetPrivateKey() == "" {
-35
View File
@@ -1,35 +0,0 @@
package activitypub
import (
"net/http"
"github.com/owncast/owncast/activitypub/controllers"
"github.com/owncast/owncast/router/middleware"
)
// StartRouter will start the federation specific http router.
func StartRouter() {
// WebFinger
http.HandleFunc("/.well-known/webfinger", controllers.WebfingerHandler)
// Host Metadata
http.HandleFunc("/.well-known/host-meta", controllers.HostMetaController)
// Nodeinfo v1
http.HandleFunc("/.well-known/nodeinfo", controllers.NodeInfoController)
// x-nodeinfo v2
http.HandleFunc("/.well-known/x-nodeinfo2", controllers.XNodeInfo2Controller)
// Nodeinfo v2
http.HandleFunc("/nodeinfo/2.0", controllers.NodeInfoV2Controller)
// Instance details
http.HandleFunc("/api/v1/instance", controllers.InstanceV1Controller)
// Single ActivityPub Actor
http.HandleFunc("/federation/user/", middleware.RequireActivityPubOrRedirect(controllers.ActorHandler))
// Single AP object
http.HandleFunc("/federation/", middleware.RequireActivityPubOrRedirect(controllers.ObjectHandler))
}
-44
View File
@@ -1,13 +1,7 @@
package auth package auth
import ( import (
"context"
"strings"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/db"
) )
var _datastore *data.Datastore var _datastore *data.Datastore
@@ -27,41 +21,3 @@ func Setup(db *data.Datastore) {
_datastore.MustExec(createTableSQL) _datastore.MustExec(createTableSQL)
_datastore.MustExec(`CREATE INDEX IF NOT EXISTS idx_auth_token ON auth (token);`) _datastore.MustExec(`CREATE INDEX IF NOT EXISTS idx_auth_token ON auth (token);`)
} }
// AddAuth will add an external authentication token and type for a user.
func AddAuth(userID, authToken string, authType Type) error {
return _datastore.GetQueries().AddAuthForUser(context.Background(), db.AddAuthForUserParams{
UserID: userID,
Token: authToken,
Type: string(authType),
})
}
// GetUserByAuth will return an existing user given auth details if a user
// has previously authenticated with that method.
func GetUserByAuth(authToken string, authType Type) *user.User {
u, err := _datastore.GetQueries().GetUserByAuth(context.Background(), db.GetUserByAuthParams{
Token: authToken,
Type: string(authType),
})
if err != nil {
return nil
}
var scopes []string
if u.Scopes.Valid {
scopes = strings.Split(u.Scopes.String, ",")
}
return &user.User{
ID: u.ID,
DisplayName: u.DisplayName,
DisplayColor: int(u.DisplayColor),
CreatedAt: u.CreatedAt.Time,
DisabledAt: &u.DisabledAt.Time,
PreviousNames: strings.Split(u.PreviousNames.String, ","),
NameChangedAt: &u.NamechangedAt.Time,
AuthenticatedAt: &u.AuthenticatedAt.Time,
Scopes: scopes,
}
}
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
# go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest
# setup
package="generated"
folderPath="webserver/handlers/generated"
specPath="openapi.yaml"
# validate scripts are installed
if ! command -v swagger-cli &>/dev/null; then
echo "Please install \`swagger-cli\` before running this script"
exit 1
fi
if ! command -v oapi-codegen &>/dev/null; then
echo "Please install \`oapi-codegen\` before running this script"
echo "Hint: run \`go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest\` to install"
exit 1
fi
# validate schema
swagger-cli validate $specPath
if [ $? -ne 0 ]; then
echo "Open API specification is not valid"
exit 1
fi
# cleanup
rm -r $folderPath
mkdir -p $folderPath
# codegen
oapi-codegen -generate types -o $folderPath/$package-types.gen.go -package $package $specPath
oapi-codegen -generate "chi-server" -o $folderPath/$package.gen.go -package $package $specPath
# go
go mod tidy
+1 -1
View File
@@ -4,7 +4,7 @@ import "path/filepath"
const ( const (
// StaticVersionNumber is the version of Owncast that is used when it's not overwritten via build-time settings. // StaticVersionNumber is the version of Owncast that is used when it's not overwritten via build-time settings.
StaticVersionNumber = "0.1.3" // Shown when you build from develop StaticVersionNumber = "0.2.0" // Shown when you build from develop
// FfmpegSuggestedVersion is the version of ffmpeg we suggest. // FfmpegSuggestedVersion is the version of ffmpeg we suggest.
FfmpegSuggestedVersion = "v4.1.5" // Requires the v FfmpegSuggestedVersion = "v4.1.5" // Requires the v
// DataDirectory is the directory we save data to. // DataDirectory is the directory we save data to.
-13
View File
@@ -1,13 +0,0 @@
package controllers
import (
"net/http"
"github.com/owncast/owncast/core/rtmp"
)
// DisconnectInboundConnection will force-disconnect an inbound stream.
func DisconnectInboundConnection(w http.ResponseWriter, r *http.Request) {
rtmp.Disconnect()
w.WriteHeader(http.StatusOK)
}
-35
View File
@@ -1,35 +0,0 @@
package admin
import (
"encoding/json"
"net/http"
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/data"
)
// SetCustomColorVariableValues sets the custom color variables.
func SetCustomColorVariableValues(w http.ResponseWriter, r *http.Request) {
if !requirePOST(w, r) {
return
}
type request struct {
Value map[string]string `json:"value"`
}
decoder := json.NewDecoder(r.Body)
var values request
if err := decoder.Decode(&values); err != nil {
controllers.WriteSimpleResponse(w, false, "unable to update appearance variable values")
return
}
if err := data.SetCustomColorVariableValues(values.Value); err != nil {
controllers.WriteSimpleResponse(w, false, err.Error())
return
}
controllers.WriteSimpleResponse(w, true, "custom appearance variables updated")
}
-102
View File
@@ -1,102 +0,0 @@
package admin
import (
"encoding/json"
"errors"
"net/http"
"time"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/utils"
)
type deleteExternalAPIUserRequest struct {
Token string `json:"token"`
}
type createExternalAPIUserRequest struct {
Name string `json:"name"`
Scopes []string `json:"scopes"`
}
// CreateExternalAPIUser will generate a 3rd party access token.
func CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
decoder := json.NewDecoder(r.Body)
var request createExternalAPIUserRequest
if err := decoder.Decode(&request); err != nil {
controllers.BadRequestHandler(w, err)
return
}
// Verify all the scopes provided are valid
if !user.HasValidScopes(request.Scopes) {
controllers.BadRequestHandler(w, errors.New("one or more invalid scopes provided"))
return
}
token, err := utils.GenerateAccessToken()
if err != nil {
controllers.InternalErrorHandler(w, err)
return
}
color := utils.GenerateRandomDisplayColor(config.MaxUserColor)
if err := user.InsertExternalAPIUser(token, request.Name, color, request.Scopes); err != nil {
controllers.InternalErrorHandler(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
controllers.WriteResponse(w, user.ExternalAPIUser{
AccessToken: token,
DisplayName: request.Name,
DisplayColor: color,
Scopes: request.Scopes,
CreatedAt: time.Now(),
LastUsedAt: nil,
})
}
// GetExternalAPIUsers will return all 3rd party access tokens.
func GetExternalAPIUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
tokens, err := user.GetExternalAPIUser()
if err != nil {
controllers.InternalErrorHandler(w, err)
return
}
controllers.WriteResponse(w, tokens)
}
// DeleteExternalAPIUser will return a single 3rd party access token.
func DeleteExternalAPIUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if r.Method != controllers.POST {
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
return
}
decoder := json.NewDecoder(r.Body)
var request deleteExternalAPIUserRequest
if err := decoder.Decode(&request); err != nil {
controllers.BadRequestHandler(w, err)
return
}
if request.Token == "" {
controllers.BadRequestHandler(w, errors.New("must provide a token"))
return
}
if err := user.DeleteExternalAPIUser(request.Token); err != nil {
controllers.InternalErrorHandler(w, err)
return
}
controllers.WriteSimpleResponse(w, true, "deleted token")
}
-99
View File
@@ -1,99 +0,0 @@
package controllers
import (
"encoding/json"
"net/http"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/router/middleware"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
// ExternalGetChatMessages gets all of the chat messages.
func ExternalGetChatMessages(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
getChatMessages(w, r)
}
// GetChatMessages gets all of the chat messages.
func GetChatMessages(u user.User, w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
getChatMessages(w, r)
}
func getChatMessages(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodGet:
messages := chat.GetChatHistory()
if err := json.NewEncoder(w).Encode(messages); err != nil {
log.Debugln(err)
}
default:
w.WriteHeader(http.StatusNotImplemented)
if err := json.NewEncoder(w).Encode(j{"error": "method not implemented (PRs are accepted)"}); err != nil {
InternalErrorHandler(w, err)
}
}
}
// RegisterAnonymousChatUser will register a new user.
func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
if r.Method == "OPTIONS" {
// All OPTIONS requests should have a wildcard CORS header.
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(http.StatusNoContent)
return
}
if r.Method != http.MethodPost {
// nolint:goconst
WriteSimpleResponse(w, false, r.Method+" not supported")
return
}
type registerAnonymousUserRequest struct {
DisplayName string `json:"displayName"`
}
type registerAnonymousUserResponse struct {
ID string `json:"id"`
AccessToken string `json:"accessToken"`
DisplayName string `json:"displayName"`
}
decoder := json.NewDecoder(r.Body)
var request registerAnonymousUserRequest
if err := decoder.Decode(&request); err != nil { //nolint
// this is fine. register a new user anyway.
}
if request.DisplayName == "" {
request.DisplayName = r.Header.Get("X-Forwarded-User")
}
proposedNewDisplayName := utils.MakeSafeStringOfLength(request.DisplayName, config.MaxChatDisplayNameLength)
newUser, accessToken, err := user.CreateAnonymousUser(proposedNewDisplayName)
if err != nil {
WriteSimpleResponse(w, false, err.Error())
return
}
response := registerAnonymousUserResponse{
ID: newUser.ID,
AccessToken: accessToken,
DisplayName: newUser.DisplayName,
}
w.Header().Set("Content-Type", "application/json")
middleware.DisableCache(w)
WriteResponse(w, response)
}
-153
View File
@@ -1,153 +0,0 @@
package controllers
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/owncast/owncast/activitypub"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/router/middleware"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
type webConfigResponse struct {
AppearanceVariables map[string]string `json:"appearanceVariables"`
Name string `json:"name"`
CustomStyles string `json:"customStyles"`
StreamTitle string `json:"streamTitle,omitempty"` // What's going on with the current stream
OfflineMessage string `json:"offlineMessage"`
Logo string `json:"logo"`
Version string `json:"version"`
SocketHostOverride string `json:"socketHostOverride,omitempty"`
ExtraPageContent string `json:"extraPageContent"`
Summary string `json:"summary"`
Tags []string `json:"tags"`
SocialHandles []models.SocialHandle `json:"socialHandles"`
ExternalActions []models.ExternalAction `json:"externalActions"`
Notifications notificationsConfigResponse `json:"notifications"`
Federation federationConfigResponse `json:"federation"`
MaxSocketPayloadSize int `json:"maxSocketPayloadSize"`
HideViewerCount bool `json:"hideViewerCount"`
ChatDisabled bool `json:"chatDisabled"`
NSFW bool `json:"nsfw"`
Authentication authenticationConfigResponse `json:"authentication"`
}
type federationConfigResponse struct {
Account string `json:"account,omitempty"`
FollowerCount int `json:"followerCount,omitempty"`
Enabled bool `json:"enabled"`
}
type browserNotificationsConfigResponse struct {
PublicKey string `json:"publicKey,omitempty"`
Enabled bool `json:"enabled"`
}
type notificationsConfigResponse struct {
Browser browserNotificationsConfigResponse `json:"browser"`
}
type authenticationConfigResponse struct {
IndieAuthEnabled bool `json:"indieAuthEnabled"`
}
// GetWebConfig gets the status of the server.
func GetWebConfig(w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
middleware.DisableCache(w)
w.Header().Set("Content-Type", "application/json")
configuration := getConfigResponse()
if err := json.NewEncoder(w).Encode(configuration); err != nil {
BadRequestHandler(w, err)
}
}
func getConfigResponse() webConfigResponse {
pageContent := utils.RenderPageContentMarkdown(data.GetExtraPageBodyContent())
offlineMessage := utils.RenderSimpleMarkdown(data.GetCustomOfflineMessage())
socialHandles := data.GetSocialHandles()
for i, handle := range socialHandles {
platform := models.GetSocialHandle(handle.Platform)
if platform != nil {
handle.Icon = platform.Icon
socialHandles[i] = handle
}
}
serverSummary := data.GetServerSummary()
var federationResponse federationConfigResponse
federationEnabled := data.GetFederationEnabled()
followerCount, _ := activitypub.GetFollowerCount()
if federationEnabled {
serverURLString := data.GetServerURL()
serverURL, _ := url.Parse(serverURLString)
account := fmt.Sprintf("%s@%s", data.GetDefaultFederationUsername(), serverURL.Host)
federationResponse = federationConfigResponse{
Enabled: federationEnabled,
FollowerCount: int(followerCount),
Account: account,
}
}
browserPushEnabled := data.GetBrowserPushConfig().Enabled
browserPushPublicKey, err := data.GetBrowserPushPublicKey()
if err != nil {
log.Errorln("unable to fetch browser push notifications public key", err)
browserPushEnabled = false
}
notificationsResponse := notificationsConfigResponse{
Browser: browserNotificationsConfigResponse{
Enabled: browserPushEnabled,
PublicKey: browserPushPublicKey,
},
}
authenticationResponse := authenticationConfigResponse{
IndieAuthEnabled: data.GetServerURL() != "",
}
return webConfigResponse{
Name: data.GetServerName(),
Summary: serverSummary,
OfflineMessage: offlineMessage,
Logo: "/logo",
Tags: data.GetServerMetadataTags(),
Version: config.GetReleaseString(),
NSFW: data.GetNSFW(),
SocketHostOverride: data.GetWebsocketOverrideHost(),
ExtraPageContent: pageContent,
StreamTitle: data.GetStreamTitle(),
SocialHandles: socialHandles,
ChatDisabled: data.GetChatDisabled(),
ExternalActions: data.GetExternalActions(),
CustomStyles: data.GetCustomStyles(),
MaxSocketPayloadSize: config.MaxSocketPayloadSize,
Federation: federationResponse,
Notifications: notificationsResponse,
Authentication: authenticationResponse,
AppearanceVariables: data.GetCustomColorVariableValues(),
HideViewerCount: data.GetHideViewerCount(),
}
}
// GetAllSocialPlatforms will return a list of all social platform types.
func GetAllSocialPlatforms(w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
w.Header().Set("Content-Type", "application/json")
platforms := models.GetAllSocialHandles()
if err := json.NewEncoder(w).Encode(platforms); err != nil {
InternalErrorHandler(w, err)
}
}
-7
View File
@@ -1,7 +0,0 @@
package controllers
// POST is the HTTP POST method.
const POST = "POST"
// GET is the HTTP GET method.
const GET = "GET"
-62
View File
@@ -1,62 +0,0 @@
package controllers
import (
"net/http"
"path/filepath"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/utils"
)
const (
contentTypeJPEG = "image/jpeg"
contentTypeGIF = "image/gif"
)
// GetThumbnail will return the thumbnail image as a response.
func GetThumbnail(w http.ResponseWriter, r *http.Request) {
imageFilename := "thumbnail.jpg"
imagePath := filepath.Join(config.TempDir, imageFilename)
var imageBytes []byte
var err error
if utils.DoesFileExists(imagePath) {
imageBytes, err = getImage(imagePath)
} else {
GetLogo(w, r)
return
}
if err != nil {
GetLogo(w, r)
return
}
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
writeBytesAsImage(imageBytes, contentTypeJPEG, w, cacheTime)
}
// GetPreview will return the preview gif as a response.
func GetPreview(w http.ResponseWriter, r *http.Request) {
imageFilename := "preview.gif"
imagePath := filepath.Join(config.TempDir, imageFilename)
var imageBytes []byte
var err error
if utils.DoesFileExists(imagePath) {
imageBytes, err = getImage(imagePath)
} else {
GetLogo(w, r)
return
}
if err != nil {
GetLogo(w, r)
return
}
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
writeBytesAsImage(imageBytes, contentTypeGIF, w, cacheTime)
}
-52
View File
@@ -1,52 +0,0 @@
package controllers
import (
"encoding/json"
"net/http"
"github.com/owncast/owncast/metrics"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
// ReportPlaybackMetrics will accept playback metrics from a client and save
// them for future video health reporting.
func ReportPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
if r.Method != POST {
WriteSimpleResponse(w, false, r.Method+" not supported")
return
}
type reportPlaybackMetricsRequest struct {
Bandwidth float64 `json:"bandwidth"`
Latency float64 `json:"latency"`
Errors float64 `json:"errors"`
DownloadDuration float64 `json:"downloadDuration"`
QualityVariantChanges float64 `json:"qualityVariantChanges"`
}
decoder := json.NewDecoder(r.Body)
var request reportPlaybackMetricsRequest
if err := decoder.Decode(&request); err != nil {
log.Errorln("error decoding playback metrics payload:", err)
WriteSimpleResponse(w, false, err.Error())
return
}
clientID := utils.GenerateClientIDFromRequest(r)
metrics.RegisterPlaybackErrorCount(clientID, request.Errors)
if request.Bandwidth != 0.0 {
metrics.RegisterPlayerBandwidth(clientID, request.Bandwidth)
}
if request.Latency != 0.0 {
metrics.RegisterPlayerLatency(clientID, request.Latency)
}
if request.DownloadDuration != 0.0 {
metrics.RegisterPlayerSegmentDownloadDuration(clientID, request.DownloadDuration)
}
metrics.RegisterQualityVariantChangesCount(clientID, request.QualityVariantChanges)
}
+19 -4
View File
@@ -13,8 +13,9 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/user" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/geoip" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/services/geoip"
) )
// Client represents a single chat client. // Client represents a single chat client.
@@ -22,8 +23,9 @@ type Client struct {
ConnectedAt time.Time `json:"connectedAt"` ConnectedAt time.Time `json:"connectedAt"`
timeoutTimer *time.Timer timeoutTimer *time.Timer
rateLimiter *rate.Limiter rateLimiter *rate.Limiter
messageFilter *ChatMessageFilter
conn *websocket.Conn conn *websocket.Conn
User *user.User `json:"user"` User *models.User `json:"user"`
server *Server server *Server
Geo *geoip.GeoDetails `json:"geo"` Geo *geoip.GeoDetails `json:"geo"`
// Buffered channel of outbound messages. // Buffered channel of outbound messages.
@@ -90,6 +92,7 @@ func (c *Client) readPump() {
// Allow 3 messages every two seconds. // Allow 3 messages every two seconds.
limit := rate.Every(2 * time.Second / 3) limit := rate.Every(2 * time.Second / 3)
c.rateLimiter = rate.NewLimiter(limit, 1) c.rateLimiter = rate.NewLimiter(limit, 1)
c.messageFilter = NewMessageFilter()
defer func() { defer func() {
c.close() c.close()
@@ -129,6 +132,12 @@ func (c *Client) readPump() {
continue continue
} }
// Check if this message passes the optional language filter
if data.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) {
c.sendAction("Sorry, that message contained language that is not allowed in this chat.")
continue
}
message = bytes.TrimSpace(bytes.ReplaceAll(message, newline, space)) message = bytes.TrimSpace(bytes.ReplaceAll(message, newline, space))
c.handleEvent(message) c.handleEvent(message)
} }
@@ -200,7 +209,13 @@ func (c *Client) close() {
} }
func (c *Client) passesRateLimit() bool { func (c *Client) passesRateLimit() bool {
return c.User.IsModerator() || (c.rateLimiter.Allow() && !c.inTimeout) // If spam rate limiting is disabled, or the user is a moderator, always
// allow the message.
if !data.GetChatSpamProtectionEnabled() || c.User.IsModerator() {
return true
}
return (c.rateLimiter.Allow() && !c.inTimeout)
} }
func (c *Client) startChatRejectionTimeout() { func (c *Client) startChatRejectionTimeout() {
+13 -7
View File
@@ -9,8 +9,8 @@ import (
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -46,12 +46,14 @@ func (s *Server) userNameChanged(eventData chatClientEvent) {
} }
} }
userRepository := userrepository.Get()
// Check if the name is not already assigned to a registered user. // Check if the name is not already assigned to a registered user.
if available, err := user.IsDisplayNameAvailable(proposedUsername); err != nil { if available, err := userRepository.IsDisplayNameAvailable(proposedUsername); err != nil {
log.Errorln("error checking if name is available", err) log.Errorln("error checking if name is available", err)
return return
} else if !available { } else if !available {
message := fmt.Sprintf("The name **%s** has been already registered. If this is your name, please authenticate.", proposedUsername) message := fmt.Sprintf("The name **%s** has already been registered. If this is your name, please authenticate.", proposedUsername)
s.sendActionToClient(eventData.client, message) s.sendActionToClient(eventData.client, message)
// Resend the client's user so their username is in sync. // Resend the client's user so their username is in sync.
@@ -60,7 +62,7 @@ func (s *Server) userNameChanged(eventData chatClientEvent) {
return return
} }
savedUser := user.GetUserByToken(eventData.client.accessToken) savedUser := userRepository.GetUserByToken(eventData.client.accessToken)
oldName := savedUser.DisplayName oldName := savedUser.DisplayName
// Check that the new name is different from old. // Check that the new name is different from old.
@@ -70,7 +72,7 @@ func (s *Server) userNameChanged(eventData chatClientEvent) {
} }
// Save the new name // Save the new name
if err := user.ChangeUsername(eventData.client.User.ID, proposedUsername); err != nil { if err := userRepository.ChangeUsername(eventData.client.User.ID, proposedUsername); err != nil {
log.Errorln("error changing username", err) log.Errorln("error changing username", err)
} }
@@ -103,6 +105,8 @@ func (s *Server) userNameChanged(eventData chatClientEvent) {
} }
func (s *Server) userColorChanged(eventData chatClientEvent) { func (s *Server) userColorChanged(eventData chatClientEvent) {
userRepository := userrepository.Get()
var receivedEvent events.ColorChangeEvent var receivedEvent events.ColorChangeEvent
if err := json.Unmarshal(eventData.data, &receivedEvent); err != nil { if err := json.Unmarshal(eventData.data, &receivedEvent); err != nil {
log.Errorln("error unmarshalling to ColorChangeEvent", err) log.Errorln("error unmarshalling to ColorChangeEvent", err)
@@ -116,7 +120,7 @@ func (s *Server) userColorChanged(eventData chatClientEvent) {
} }
// Save the new color // Save the new color
if err := user.ChangeUserColor(eventData.client.User.ID, receivedEvent.NewColor); err != nil { if err := userRepository.ChangeUserColor(eventData.client.User.ID, receivedEvent.NewColor); err != nil {
log.Errorln("error changing user display color", err) log.Errorln("error changing user display color", err)
} }
@@ -126,6 +130,8 @@ func (s *Server) userColorChanged(eventData chatClientEvent) {
} }
func (s *Server) userMessageSent(eventData chatClientEvent) { func (s *Server) userMessageSent(eventData chatClientEvent) {
userRepository := userrepository.Get()
var event events.UserMessageEvent var event events.UserMessageEvent
if err := json.Unmarshal(eventData.data, &event); err != nil { if err := json.Unmarshal(eventData.data, &event); err != nil {
log.Errorln("error unmarshalling to UserMessageEvent", err) log.Errorln("error unmarshalling to UserMessageEvent", err)
@@ -148,7 +154,7 @@ func (s *Server) userMessageSent(eventData chatClientEvent) {
} }
} }
event.User = user.GetUserByToken(eventData.client.accessToken) event.User = userRepository.GetUserByToken(eventData.client.accessToken)
// Guard against nil users // Guard against nil users
if event.User == nil { if event.User == nil {
+2 -2
View File
@@ -1,9 +1,9 @@
package events package events
import "github.com/owncast/owncast/core/user" import "github.com/owncast/owncast/models"
// ConnectedClientInfo represents the information about a connected client. // ConnectedClientInfo represents the information about a connected client.
type ConnectedClientInfo struct { type ConnectedClientInfo struct {
User *user.User `json:"user"` User *models.User `json:"user"`
Event Event
} }
+4 -4
View File
@@ -20,7 +20,7 @@ import (
"mvdan.cc/xurls" "mvdan.cc/xurls"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user" "github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -30,19 +30,19 @@ type EventPayload map[string]interface{}
// OutboundEvent represents an event that is sent out to all listeners of the chat server. // OutboundEvent represents an event that is sent out to all listeners of the chat server.
type OutboundEvent interface { type OutboundEvent interface {
GetBroadcastPayload() EventPayload GetBroadcastPayload() EventPayload
GetMessageType() EventType GetMessageType() models.EventType
} }
// Event is any kind of event. A type is required to be specified. // Event is any kind of event. A type is required to be specified.
type Event struct { type Event struct {
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
Type EventType `json:"type,omitempty"` Type models.EventType `json:"type,omitempty"`
ID string `json:"id"` ID string `json:"id"`
} }
// UserEvent is an event with an associated user. // UserEvent is an event with an associated user.
type UserEvent struct { type UserEvent struct {
User *user.User `json:"user"` User *models.User `json:"user"`
HiddenAt *time.Time `json:"hiddenAt,omitempty"` HiddenAt *time.Time `json:"hiddenAt,omitempty"`
ClientID uint `json:"clientId,omitempty"` ClientID uint `json:"clientId,omitempty"`
} }
+18
View File
@@ -0,0 +1,18 @@
package chat
import (
goaway "github.com/TwiN/go-away"
)
// ChatMessageFilter is a allow/deny chat message filter.
type ChatMessageFilter struct{}
// NewMessageFilter will return an instance of the chat message filter.
func NewMessageFilter() *ChatMessageFilter {
return &ChatMessageFilter{}
}
// Allow will test if this message should be allowed to be sent.
func (*ChatMessageFilter) Allow(message string) bool {
return !goaway.IsProfane(message)
}
+39
View File
@@ -0,0 +1,39 @@
package chat
import (
"testing"
)
func TestFiltering(t *testing.T) {
filter := NewMessageFilter()
filteredTestMessages := []string{
"Hello, fucking world!",
"Suck my dick",
"Eat my ass",
"fuck this shit",
"@$$h073",
"F u C k th1$ $h!t",
"u r fag",
"fucking sucks",
}
unfilteredTestMessages := []string{
"bass fish",
"assumptions",
}
for _, m := range filteredTestMessages {
result := filter.Allow(m)
if result {
t.Errorf("%s should be seen as a filtered profane message", m)
}
}
for _, m := range unfilteredTestMessages {
result := filter.Allow(m)
if !result {
t.Errorf("%s should not be filtered", m)
}
}
}
+4 -3
View File
@@ -8,8 +8,9 @@ import (
"github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/tables"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -22,7 +23,7 @@ const (
func setupPersistence() { func setupPersistence() {
_datastore = data.GetDatastore() _datastore = data.GetDatastore()
data.CreateMessagesTable(_datastore.DB) tables.CreateMessagesTable(_datastore.DB)
data.CreateBanIPTable(_datastore.DB) data.CreateBanIPTable(_datastore.DB)
chatDataPruner := time.NewTicker(5 * time.Minute) chatDataPruner := time.NewTicker(5 * time.Minute)
@@ -104,7 +105,7 @@ func makeUserMessageEventFromRowData(row rowData) events.UserMessageEvent {
isBot := (row.userType != nil && *row.userType == "API") isBot := (row.userType != nil && *row.userType == "API")
scopeSlice := strings.Split(scopes, ",") scopeSlice := strings.Split(scopes, ",")
u := user.User{ u := models.User{
ID: *row.userID, ID: *row.userID,
DisplayName: displayName, DisplayName: displayName,
DisplayColor: displayColor, DisplayColor: displayColor,
+11 -5
View File
@@ -14,9 +14,10 @@ import (
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/geoip" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/services/geoip"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
) )
@@ -82,7 +83,7 @@ func (s *Server) Run() {
} }
// Addclient registers new connection as a User. // Addclient registers new connection as a User.
func (s *Server) Addclient(conn *websocket.Conn, user *user.User, accessToken string, userAgent string, ipAddress string) *Client { func (s *Server) Addclient(conn *websocket.Conn, user *models.User, accessToken string, userAgent string, ipAddress string) *Client {
client := &Client{ client := &Client{
server: s, server: s,
conn: conn, conn: conn,
@@ -239,8 +240,11 @@ func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request)
return return
} }
userRepository := userrepository.Get()
// A user is required to use the websocket // A user is required to use the websocket
user := user.GetUserByToken(accessToken) user := userRepository.GetUserByToken(accessToken)
if user == nil { if user == nil {
// Send error that registration is required // Send error that registration is required
_ = conn.WriteJSON(events.EventPayload{ _ = conn.WriteJSON(events.EventPayload{
@@ -335,8 +339,10 @@ func SendConnectedClientInfoToUser(userID string) error {
return err return err
} }
userRepository := userrepository.Get()
// Get an updated reference to the user. // Get an updated reference to the user.
user := user.GetUserByID(userID) user := userRepository.GetUserByID(userID)
if user == nil { if user == nil {
return fmt.Errorf("user not found") return fmt.Errorf("user not found")
} }
+3 -3
View File
@@ -13,10 +13,10 @@ import (
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/rtmp" "github.com/owncast/owncast/core/rtmp"
"github.com/owncast/owncast/core/transcoder" "github.com/owncast/owncast/core/transcoder"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications" "github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
"github.com/owncast/owncast/yp" "github.com/owncast/owncast/yp"
) )
@@ -56,7 +56,7 @@ func Start() error {
log.Errorln("storage error", err) log.Errorln("storage error", err)
} }
user.SetupUsers() tables.SetupUsers(data.GetDatastore().DB)
auth.Setup(data.GetDatastore()) auth.Setup(data.GetDatastore())
fileWriter.SetupFileWriterReceiverService(&handler) fileWriter.SetupFileWriterReceiverService(&handler)
@@ -104,7 +104,7 @@ func transitionToOfflineVideoStreamContent() {
_transcoder.SetLatencyLevel(models.GetLatencyLevel(4)) _transcoder.SetLatencyLevel(models.GetLatencyLevel(4))
_transcoder.SetIsEvent(true) _transcoder.SetIsEvent(true)
offlineFilePath, err := saveOfflineClipToDisk("offline.ts") offlineFilePath, err := saveOfflineClipToDisk("offline-v2.ts")
if err != nil { if err != nil {
log.Fatalln("unable to save offline clip:", err) log.Fatalln("unable to save offline clip:", err)
} }
+37 -1
View File
@@ -59,6 +59,8 @@ const (
suggestedUsernamesKey = "suggested_usernames" suggestedUsernamesKey = "suggested_usernames"
chatJoinMessagesEnabledKey = "chat_join_messages_enabled" chatJoinMessagesEnabledKey = "chat_join_messages_enabled"
chatEstablishedUsersOnlyModeKey = "chat_established_users_only_mode" chatEstablishedUsersOnlyModeKey = "chat_established_users_only_mode"
chatSpamProtectionEnabledKey = "chat_spam_protection_enabled"
chatSlurFilterEnabledKey = "chat_slur_filter_enabled"
notificationsEnabledKey = "notifications_enabled" notificationsEnabledKey = "notifications_enabled"
discordConfigurationKey = "discord_configuration" discordConfigurationKey = "discord_configuration"
browserPushConfigurationKey = "browser_push_configuration" browserPushConfigurationKey = "browser_push_configuration"
@@ -113,7 +115,11 @@ func GetAdminPassword() string {
// SetAdminPassword will set the admin password. // SetAdminPassword will set the admin password.
func SetAdminPassword(key string) error { func SetAdminPassword(key string) error {
return _datastore.SetString(adminPasswordKey, key) hashed_pass, err := utils.HashPassword(key)
if err != nil {
return err
}
return _datastore.SetString(adminPasswordKey, hashed_pass)
} }
// GetLogoPath will return the path for the logo, relative to webroot. // GetLogoPath will return the path for the logo, relative to webroot.
@@ -528,6 +534,36 @@ func GetChatEstbalishedUsersOnlyMode() bool {
return false return false
} }
// SetChatSpamProtectionEnabled will enable chat spam protection if set to true.
func SetChatSpamProtectionEnabled(enabled bool) error {
return _datastore.SetBool(chatSpamProtectionEnabledKey, enabled)
}
// GetChatSpamProtectionEnabled will return if chat spam protection is enabled.
func GetChatSpamProtectionEnabled() bool {
enabled, err := _datastore.GetBool(chatSpamProtectionEnabledKey)
if err == nil {
return enabled
}
return true
}
// SetChatSlurFilterEnabled will enable the chat slur filter.
func SetChatSlurFilterEnabled(enabled bool) error {
return _datastore.SetBool(chatSlurFilterEnabledKey, enabled)
}
// GetChatSlurFilterEnabled will return if the chat slur filter is enabled.
func GetChatSlurFilterEnabled() bool {
enabled, err := _datastore.GetBool(chatSlurFilterEnabledKey)
if err == nil {
return enabled
}
return false
}
// GetExternalActions will return the registered external actions. // GetExternalActions will return the registered external actions.
func GetExternalActions() []models.ExternalAction { func GetExternalActions() []models.ExternalAction {
configEntry, err := _datastore.Get(externalActionsKey) configEntry, err := _datastore.Get(externalActionsKey)
+5 -3
View File
@@ -11,6 +11,8 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -74,8 +76,8 @@ func SetupPersistence(file string) error {
_, _ = db.Exec("pragma wal_checkpoint(full)") _, _ = db.Exec("pragma wal_checkpoint(full)")
createWebhooksTable() createWebhooksTable()
createUsersTable(db) tables.CreateUsersTable(db)
createAccessTokenTable(db) tables.CreateAccessTokenTable(db)
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS config ( if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS config (
"key" string NOT NULL PRIMARY KEY, "key" string NOT NULL PRIMARY KEY,
@@ -108,7 +110,7 @@ func SetupPersistence(file string) error {
// is database schema outdated? // is database schema outdated?
if version < schemaVersion { if version < schemaVersion {
if err := migrateDatabaseSchema(db, version, schemaVersion); err != nil { if err := tables.MigrateDatabaseSchema(db, version, schemaVersion); err != nil {
return err return err
} }
} }
+13 -2
View File
@@ -8,7 +8,7 @@ import (
) )
const ( const (
datastoreValuesVersion = 3 datastoreValuesVersion = 4
datastoreValueVersionKey = "DATA_STORE_VERSION" datastoreValueVersionKey = "DATA_STORE_VERSION"
) )
@@ -27,6 +27,8 @@ func migrateDatastoreValues(datastore *Datastore) {
migrateToDatastoreValues2(datastore) migrateToDatastoreValues2(datastore)
case 2: case 2:
migrateToDatastoreValues3ServingEndpoint3(datastore) migrateToDatastoreValues3ServingEndpoint3(datastore)
case 3:
migrateToDatastoreValues4(datastore)
default: default:
log.Fatalln("missing datastore values migration step") log.Fatalln("missing datastore values migration step")
} }
@@ -58,7 +60,8 @@ func migrateToDatastoreValues1(datastore *Datastore) {
func migrateToDatastoreValues2(datastore *Datastore) { func migrateToDatastoreValues2(datastore *Datastore) {
oldAdminPassword, _ := datastore.GetString("stream_key") oldAdminPassword, _ := datastore.GetString("stream_key")
_ = SetAdminPassword(oldAdminPassword) // Avoids double hashing the password
_ = datastore.SetString("admin_password_key", oldAdminPassword)
_ = SetStreamKeys([]models.StreamKey{ _ = SetStreamKeys([]models.StreamKey{
{Key: oldAdminPassword, Comment: "Default stream key"}, {Key: oldAdminPassword, Comment: "Default stream key"},
}) })
@@ -73,3 +76,11 @@ func migrateToDatastoreValues3ServingEndpoint3(_ *Datastore) {
_ = SetVideoServingEndpoint(s3Config.ServingEndpoint) _ = SetVideoServingEndpoint(s3Config.ServingEndpoint)
} }
func migrateToDatastoreValues4(datastore *Datastore) {
unhashed_pass, _ := datastore.GetString("admin_password_key")
err := SetAdminPassword(unhashed_pass)
if err != nil {
log.Fatalln("error migrating admin password:", err)
}
}
-26
View File
@@ -9,32 +9,6 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// CreateMessagesTable will create the chat messages table if needed.
func CreateMessagesTable(db *sql.DB) {
createTableSQL := `CREATE TABLE IF NOT EXISTS messages (
"id" string NOT NULL,
"user_id" TEXT,
"body" TEXT,
"eventType" TEXT,
"hidden_at" DATETIME,
"timestamp" DATETIME,
"title" TEXT,
"subtitle" TEXT,
"image" TEXT,
"link" TEXT,
PRIMARY KEY (id)
);`
MustExec(createTableSQL, db)
// Create indexes
MustExec(`CREATE INDEX IF NOT EXISTS user_id_hidden_at_timestamp ON messages (id, user_id, hidden_at, timestamp);`, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_id ON messages (id);`, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_user_id ON messages (user_id);`, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_hidden_at ON messages (hidden_at);`, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_timestamp ON messages (timestamp);`, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_messages_hidden_at_timestamp on messages(hidden_at, timestamp);`, db)
}
// GetMessagesCount will return the number of messages in the database. // GetMessagesCount will return the number of messages in the database.
func GetMessagesCount() int64 { func GetMessagesCount() int64 {
query := `SELECT COUNT(*) FROM messages` query := `SELECT COUNT(*) FROM messages`
+1 -1
View File
@@ -36,7 +36,7 @@ func appendOfflineToVariantPlaylist(index int, playlistFilePath string) {
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-DISCONTINUITY\n") _, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-DISCONTINUITY\n")
// If "offline" content gets changed then change the duration below // If "offline" content gets changed then change the duration below
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXTINF:8.000000,\n") _, _ = atomicWriteTmpPlaylistFile.WriteString("#EXTINF:8.000000,\n")
_, _ = atomicWriteTmpPlaylistFile.WriteString("offline.ts\n") _, _ = atomicWriteTmpPlaylistFile.WriteString("offline-v2.ts\n")
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-ENDLIST\n") _, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-ENDLIST\n")
if err := atomicWriteTmpPlaylistFile.Close(); err != nil { if err := atomicWriteTmpPlaylistFile.Close(); err != nil {
+1 -1
View File
@@ -8,8 +8,8 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/geoip"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/services/geoip"
) )
var ( var (
+1 -1
View File
@@ -93,7 +93,7 @@ func SetStreamAsDisconnected() {
_stats.LastConnectTime = nil _stats.LastConnectTime = nil
_broadcaster = nil _broadcaster = nil
offlineFilename := "offline.ts" offlineFilename := "offline-v2.ts"
offlineFilePath, err := saveOfflineClipToDisk(offlineFilename) offlineFilePath, err := saveOfflineClipToDisk(offlineFilename)
if err != nil { if err != nil {
-311
View File
@@ -1,311 +0,0 @@
package user
import (
"context"
"database/sql"
"strings"
"time"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/teris-io/shortid"
)
// ExternalAPIUser represents a single 3rd party integration that uses an access token.
// This struct mostly matches the User struct so they can be used interchangeably.
type ExternalAPIUser struct {
CreatedAt time.Time `json:"createdAt"`
LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
ID string `json:"id"`
AccessToken string `json:"accessToken"`
DisplayName string `json:"displayName"`
Type string `json:"type,omitempty"` // Should be API
Scopes []string `json:"scopes"`
DisplayColor int `json:"displayColor"`
IsBot bool `json:"isBot"`
}
const (
// ScopeCanSendChatMessages will allow sending chat messages as itself.
ScopeCanSendChatMessages = "CAN_SEND_MESSAGES"
// ScopeCanSendSystemMessages will allow sending chat messages as the system.
ScopeCanSendSystemMessages = "CAN_SEND_SYSTEM_MESSAGES"
// ScopeHasAdminAccess will allow performing administrative actions on the server.
ScopeHasAdminAccess = "HAS_ADMIN_ACCESS"
)
// For a scope to be seen as "valid" it must live in this slice.
var validAccessTokenScopes = []string{
ScopeCanSendChatMessages,
ScopeCanSendSystemMessages,
ScopeHasAdminAccess,
}
// InsertExternalAPIUser will add a new API user to the database.
func InsertExternalAPIUser(token string, name string, color int, scopes []string) error {
log.Traceln("Adding new API user")
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
scopesString := strings.Join(scopes, ",")
id := shortid.MustGenerate()
tx, err := _datastore.DB.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("INSERT INTO users(id, display_name, display_color, scopes, type, previous_names) values(?, ?, ?, ?, ?, ?)")
if err != nil {
return err
}
defer stmt.Close()
if _, err = stmt.Exec(id, name, color, scopesString, "API", name); err != nil {
return err
}
if err = tx.Commit(); err != nil {
return err
}
if err := addAccessTokenForUser(token, id); err != nil {
return errors.Wrap(err, "unable to save access token for new external api user")
}
return nil
}
// DeleteExternalAPIUser will delete a token from the database.
func DeleteExternalAPIUser(token string) error {
log.Traceln("Deleting access token")
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
tx, err := _datastore.DB.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("UPDATE users SET disabled_at = CURRENT_TIMESTAMP WHERE id = (SELECT user_id FROM user_access_tokens WHERE token = ?)")
if err != nil {
return err
}
defer stmt.Close()
result, err := stmt.Exec(token)
if err != nil {
return err
}
if rowsDeleted, _ := result.RowsAffected(); rowsDeleted == 0 {
tx.Rollback() //nolint
return errors.New(token + " not found")
}
if err = tx.Commit(); err != nil {
return err
}
return nil
}
// GetExternalAPIUserForAccessTokenAndScope will determine if a specific token has access to perform a scoped action.
func GetExternalAPIUserForAccessTokenAndScope(token string, scope string) (*ExternalAPIUser, error) {
// This will split the scopes from comma separated to individual rows
// so we can efficiently find if a token supports a single scope.
// This is SQLite specific, so if we ever support other database
// backends we need to support other methods.
query := `SELECT
id,
scopes,
display_name,
display_color,
created_at,
last_used
FROM
user_access_tokens
INNER JOIN (
WITH RECURSIVE split(
id,
scopes,
display_name,
display_color,
created_at,
last_used,
disabled_at,
scope,
rest
) AS (
SELECT
id,
scopes,
display_name,
display_color,
created_at,
last_used,
disabled_at,
'',
scopes || ','
FROM
users AS u
UNION ALL
SELECT
id,
scopes,
display_name,
display_color,
created_at,
last_used,
disabled_at,
substr(rest, 0, instr(rest, ',')),
substr(rest, instr(rest, ',') + 1)
FROM
split
WHERE
rest <> ''
)
SELECT
id,
display_name,
display_color,
created_at,
last_used,
disabled_at,
scopes,
scope
FROM
split
WHERE
scope <> ''
) ON user_access_tokens.user_id = id
WHERE
disabled_at IS NULL
AND token = ?
AND scope = ?;`
row := _datastore.DB.QueryRow(query, token, scope)
integration, err := makeExternalAPIUserFromRow(row)
return integration, err
}
// GetIntegrationNameForAccessToken will return the integration name associated with a specific access token.
func GetIntegrationNameForAccessToken(token string) *string {
name, err := _datastore.GetQueries().GetUserDisplayNameByToken(context.Background(), token)
if err != nil {
return nil
}
return &name
}
// GetExternalAPIUser will return all API users with access tokens.
func GetExternalAPIUser() ([]ExternalAPIUser, error) { //nolint
query := "SELECT id, token, display_name, display_color, scopes, created_at, last_used FROM users, user_access_tokens WHERE user_access_tokens.user_id = id AND type IS 'API' AND disabled_at IS NULL"
rows, err := _datastore.DB.Query(query)
if err != nil {
return []ExternalAPIUser{}, err
}
defer rows.Close()
integrations, err := makeExternalAPIUsersFromRows(rows)
return integrations, err
}
// SetExternalAPIUserAccessTokenAsUsed will update the last used timestamp for a token.
func SetExternalAPIUserAccessTokenAsUsed(token string) error {
tx, err := _datastore.DB.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("UPDATE users SET last_used = CURRENT_TIMESTAMP WHERE id = (SELECT user_id FROM user_access_tokens WHERE token = ?)")
if err != nil {
return err
}
defer stmt.Close()
if _, err := stmt.Exec(token); err != nil {
return err
}
if err = tx.Commit(); err != nil {
return err
}
return nil
}
func makeExternalAPIUserFromRow(row *sql.Row) (*ExternalAPIUser, error) {
var id string
var displayName string
var displayColor int
var scopes string
var createdAt time.Time
var lastUsedAt *time.Time
err := row.Scan(&id, &scopes, &displayName, &displayColor, &createdAt, &lastUsedAt)
if err != nil {
log.Debugln("unable to convert row to api user", err)
return nil, err
}
integration := ExternalAPIUser{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
Scopes: strings.Split(scopes, ","),
LastUsedAt: lastUsedAt,
}
return &integration, nil
}
func makeExternalAPIUsersFromRows(rows *sql.Rows) ([]ExternalAPIUser, error) {
integrations := make([]ExternalAPIUser, 0)
for rows.Next() {
var id string
var accessToken string
var displayName string
var displayColor int
var scopes string
var createdAt time.Time
var lastUsedAt *time.Time
err := rows.Scan(&id, &accessToken, &displayName, &displayColor, &scopes, &createdAt, &lastUsedAt)
if err != nil {
log.Errorln(err)
return nil, err
}
integration := ExternalAPIUser{
ID: id,
AccessToken: accessToken,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
Scopes: strings.Split(scopes, ","),
LastUsedAt: lastUsedAt,
IsBot: true,
}
integrations = append(integrations, integration)
}
return integrations, nil
}
// HasValidScopes will verify that all the scopes provided are valid.
func HasValidScopes(scopes []string) bool {
for _, scope := range scopes {
_, foundInSlice := utils.FindInSlice(validAccessTokenScopes, scope)
if !foundInSlice {
return false
}
}
return true
}
-93
View File
@@ -1,93 +0,0 @@
package user
import (
"testing"
"github.com/owncast/owncast/core/data"
)
const (
tokenName = "test token name"
token = "test-token-123"
)
var testScopes = []string{"test-scope"}
func TestMain(m *testing.M) {
if err := data.SetupPersistence(":memory:"); err != nil {
panic(err)
}
SetupUsers()
m.Run()
}
func TestCreateExternalAPIUser(t *testing.T) {
if err := InsertExternalAPIUser(token, tokenName, 0, testScopes); err != nil {
t.Fatal(err)
}
user := GetUserByToken(token)
if user == nil {
t.Fatal("api user not found after creating")
}
if user.DisplayName != tokenName {
t.Errorf("expected display name %q, got %q", tokenName, user.DisplayName)
}
if user.Scopes[0] != testScopes[0] {
t.Errorf("expected scopes %q, got %q", testScopes, user.Scopes)
}
}
func TestDeleteExternalAPIUser(t *testing.T) {
if err := DeleteExternalAPIUser(token); err != nil {
t.Fatal(err)
}
}
func TestVerifyTokenDisabled(t *testing.T) {
users, err := GetExternalAPIUser()
if err != nil {
t.Fatal(err)
}
if len(users) > 0 {
t.Fatal("disabled user returned in list of all API users")
}
}
func TestVerifyGetUserTokenDisabled(t *testing.T) {
user := GetUserByToken(token)
if user == nil {
t.Fatal("user not returned in GetUserByToken after disabling")
}
if user.DisabledAt == nil {
t.Fatal("user returned in GetUserByToken after disabling")
}
}
func TestVerifyGetExternalAPIUserForAccessTokenAndScopeTokenDisabled(t *testing.T) {
user, _ := GetExternalAPIUserForAccessTokenAndScope(token, testScopes[0])
if user != nil {
t.Fatal("user returned in GetExternalAPIUserForAccessTokenAndScope after disabling")
}
}
func TestCreateAdditionalAPIUser(t *testing.T) {
if err := InsertExternalAPIUser("ignore-me", "token-to-be-ignored", 0, testScopes); err != nil {
t.Fatal(err)
}
}
func TestAgainVerifyGetExternalAPIUserForAccessTokenAndScopeTokenDisabled(t *testing.T) {
user, _ := GetExternalAPIUserForAccessTokenAndScope(token, testScopes[0])
if user != nil {
t.Fatal("user returned in TestAgainVerifyGetExternalAPIUserForAccessTokenAndScopeTokenDisabled after disabling")
}
}
-473
View File
@@ -1,473 +0,0 @@
package user
import (
"context"
"database/sql"
"fmt"
"sort"
"strings"
"time"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
"github.com/teris-io/shortid"
log "github.com/sirupsen/logrus"
)
var _datastore *data.Datastore
const (
moderatorScopeKey = "MODERATOR"
minSuggestedUsernamePoolLength = 10
)
// User represents a single chat user.
type User struct {
CreatedAt time.Time `json:"createdAt"`
DisabledAt *time.Time `json:"disabledAt,omitempty"`
NameChangedAt *time.Time `json:"nameChangedAt,omitempty"`
AuthenticatedAt *time.Time `json:"-"`
ID string `json:"id"`
DisplayName string `json:"displayName"`
PreviousNames []string `json:"previousNames"`
Scopes []string `json:"scopes,omitempty"`
DisplayColor int `json:"displayColor"`
IsBot bool `json:"isBot"`
Authenticated bool `json:"authenticated"`
}
// IsEnabled will return if this single user is enabled.
func (u *User) IsEnabled() bool {
return u.DisabledAt == nil
}
// IsModerator will return if the user has moderation privileges.
func (u *User) IsModerator() bool {
_, hasModerationScope := utils.FindInSlice(u.Scopes, moderatorScopeKey)
return hasModerationScope
}
// SetupUsers will perform the initial initialization of the user package.
func SetupUsers() {
_datastore = data.GetDatastore()
}
func generateDisplayName() string {
suggestedUsernamesList := data.GetSuggestedUsernamesList()
if len(suggestedUsernamesList) >= minSuggestedUsernamePoolLength {
index := utils.RandomIndex(len(suggestedUsernamesList))
return suggestedUsernamesList[index]
} else {
return utils.GeneratePhrase()
}
}
// CreateAnonymousUser will create a new anonymous user with the provided display name.
func CreateAnonymousUser(displayName string) (*User, string, error) {
// Try to assign a name that was requested.
if displayName != "" {
// If name isn't available then generate a random one.
if available, _ := IsDisplayNameAvailable(displayName); !available {
displayName = generateDisplayName()
}
} else {
displayName = generateDisplayName()
}
displayColor := utils.GenerateRandomDisplayColor(config.MaxUserColor)
id := shortid.MustGenerate()
user := &User{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: time.Now(),
}
// Create new user.
if err := create(user); err != nil {
return nil, "", err
}
// Assign it an access token.
accessToken, err := utils.GenerateAccessToken()
if err != nil {
log.Errorln("Unable to create access token for new user")
return nil, "", err
}
if err := addAccessTokenForUser(accessToken, id); err != nil {
return nil, "", errors.Wrap(err, "unable to save access token for new user")
}
return user, accessToken, nil
}
// IsDisplayNameAvailable will check if the proposed name is available for use.
func IsDisplayNameAvailable(displayName string) (bool, error) {
if available, err := _datastore.GetQueries().IsDisplayNameAvailable(context.Background(), displayName); err != nil {
return false, errors.Wrap(err, "unable to check if display name is available")
} else if available != 0 {
return false, nil
}
return true, nil
}
// ChangeUsername will change the user associated to userID from one display name to another.
func ChangeUsername(userID string, username string) error {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
if err := _datastore.GetQueries().ChangeDisplayName(context.Background(), db.ChangeDisplayNameParams{
DisplayName: username,
ID: userID,
PreviousNames: sql.NullString{String: fmt.Sprintf(",%s", username), Valid: true},
NamechangedAt: sql.NullTime{Time: time.Now(), Valid: true},
}); err != nil {
return errors.Wrap(err, "unable to change display name")
}
return nil
}
// ChangeUserColor will change the user associated to userID from one display name to another.
func ChangeUserColor(userID string, color int) error {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
if err := _datastore.GetQueries().ChangeDisplayColor(context.Background(), db.ChangeDisplayColorParams{
DisplayColor: int32(color),
ID: userID,
}); err != nil {
return errors.Wrap(err, "unable to change display color")
}
return nil
}
func addAccessTokenForUser(accessToken, userID string) error {
return _datastore.GetQueries().AddAccessTokenForUser(context.Background(), db.AddAccessTokenForUserParams{
Token: accessToken,
UserID: userID,
})
}
func create(user *User) error {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
tx, err := _datastore.DB.Begin()
if err != nil {
log.Debugln(err)
}
defer func() {
_ = tx.Rollback()
}()
stmt, err := tx.Prepare("INSERT INTO users(id, display_name, display_color, previous_names, created_at) values(?, ?, ?, ?, ?)")
if err != nil {
log.Debugln(err)
}
defer stmt.Close()
_, err = stmt.Exec(user.ID, user.DisplayName, user.DisplayColor, user.DisplayName, user.CreatedAt)
if err != nil {
log.Errorln("error creating new user", err)
return err
}
return tx.Commit()
}
// SetEnabled will set the enabled status of a single user by ID.
func SetEnabled(userID string, enabled bool) error {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
tx, err := _datastore.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback() //nolint
var stmt *sql.Stmt
if !enabled {
stmt, err = tx.Prepare("UPDATE users SET disabled_at=DATETIME('now', 'localtime') WHERE id IS ?")
} else {
stmt, err = tx.Prepare("UPDATE users SET disabled_at=null WHERE id IS ?")
}
if err != nil {
return err
}
defer stmt.Close()
if _, err := stmt.Exec(userID); err != nil {
return err
}
return tx.Commit()
}
// GetUserByToken will return a user by an access token.
func GetUserByToken(token string) *User {
u, err := _datastore.GetQueries().GetUserByAccessToken(context.Background(), token)
if err != nil {
return nil
}
var scopes []string
if u.Scopes.Valid {
scopes = strings.Split(u.Scopes.String, ",")
}
var disabledAt *time.Time
if u.DisabledAt.Valid {
disabledAt = &u.DisabledAt.Time
}
var authenticatedAt *time.Time
if u.AuthenticatedAt.Valid {
authenticatedAt = &u.AuthenticatedAt.Time
}
return &User{
ID: u.ID,
DisplayName: u.DisplayName,
DisplayColor: int(u.DisplayColor),
CreatedAt: u.CreatedAt.Time,
DisabledAt: disabledAt,
PreviousNames: strings.Split(u.PreviousNames.String, ","),
NameChangedAt: &u.NamechangedAt.Time,
AuthenticatedAt: authenticatedAt,
Authenticated: authenticatedAt != nil,
Scopes: scopes,
}
}
// SetAccessTokenToOwner will reassign an access token to be owned by a
// different user. Used for logging in with external auth.
func SetAccessTokenToOwner(token, userID string) error {
return _datastore.GetQueries().SetAccessTokenToOwner(context.Background(), db.SetAccessTokenToOwnerParams{
UserID: userID,
Token: token,
})
}
// SetUserAsAuthenticated will mark that a user has been authenticated
// in some way.
func SetUserAsAuthenticated(userID string) error {
return errors.Wrap(_datastore.GetQueries().SetUserAsAuthenticated(context.Background(), userID), "unable to set user as authenticated")
}
// SetModerator will add or remove moderator status for a single user by ID.
func SetModerator(userID string, isModerator bool) error {
if isModerator {
return addScopeToUser(userID, moderatorScopeKey)
}
return removeScopeFromUser(userID, moderatorScopeKey)
}
func addScopeToUser(userID string, scope string) error {
u := GetUserByID(userID)
if u == nil {
return errors.New("user not found when modifying scope")
}
scopesString := u.Scopes
scopes := utils.StringSliceToMap(scopesString)
scopes[scope] = true
scopesSlice := utils.StringMapKeys(scopes)
return setScopesOnUser(userID, scopesSlice)
}
func removeScopeFromUser(userID string, scope string) error {
u := GetUserByID(userID)
scopesString := u.Scopes
scopes := utils.StringSliceToMap(scopesString)
delete(scopes, scope)
scopesSlice := utils.StringMapKeys(scopes)
return setScopesOnUser(userID, scopesSlice)
}
func setScopesOnUser(userID string, scopes []string) error {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
tx, err := _datastore.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback() //nolint
scopesSliceString := strings.TrimSpace(strings.Join(scopes, ","))
stmt, err := tx.Prepare("UPDATE users SET scopes=? WHERE id IS ?")
if err != nil {
return err
}
defer stmt.Close()
var val *string
if scopesSliceString == "" {
val = nil
} else {
val = &scopesSliceString
}
if _, err := stmt.Exec(val, userID); err != nil {
return err
}
return tx.Commit()
}
// GetUserByID will return a user by a user ID.
func GetUserByID(id string) *User {
_datastore.DbLock.Lock()
defer _datastore.DbLock.Unlock()
query := "SELECT id, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, scopes FROM users WHERE id = ?"
row := _datastore.DB.QueryRow(query, id)
if row == nil {
log.Errorln(row)
return nil
}
return getUserFromRow(row)
}
// GetDisabledUsers will return back all the currently disabled users that are not API users.
func GetDisabledUsers() []*User {
query := "SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at FROM users WHERE disabled_at IS NOT NULL AND type IS NOT 'API'"
rows, err := _datastore.DB.Query(query)
if err != nil {
log.Errorln(err)
return nil
}
defer rows.Close()
users := getUsersFromRows(rows)
sort.Slice(users, func(i, j int) bool {
return users[i].DisabledAt.Before(*users[j].DisabledAt)
})
return users
}
// GetModeratorUsers will return a list of users with moderator access.
func GetModeratorUsers() []*User {
query := `SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at FROM (
WITH RECURSIVE split(id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at, scope, rest) AS (
SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at, '', scopes || ',' FROM users
UNION ALL
SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at,
substr(rest, 0, instr(rest, ',')),
substr(rest, instr(rest, ',')+1)
FROM split
WHERE rest <> '')
SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at, scope
FROM split
WHERE scope <> ''
ORDER BY created_at
) AS token WHERE token.scope = ?`
rows, err := _datastore.DB.Query(query, moderatorScopeKey)
if err != nil {
log.Errorln(err)
return nil
}
defer rows.Close()
users := getUsersFromRows(rows)
return users
}
func getUsersFromRows(rows *sql.Rows) []*User {
users := make([]*User, 0)
for rows.Next() {
var id string
var displayName string
var displayColor int
var createdAt time.Time
var disabledAt *time.Time
var previousUsernames string
var userNameChangedAt *time.Time
var scopesString *string
if err := rows.Scan(&id, &displayName, &scopesString, &displayColor, &createdAt, &disabledAt, &previousUsernames, &userNameChangedAt); err != nil {
log.Errorln("error creating collection of users from results", err)
return nil
}
var scopes []string
if scopesString != nil {
scopes = strings.Split(*scopesString, ",")
}
user := &User{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
DisabledAt: disabledAt,
PreviousNames: strings.Split(previousUsernames, ","),
NameChangedAt: userNameChangedAt,
Scopes: scopes,
}
users = append(users, user)
}
sort.Slice(users, func(i, j int) bool {
return users[i].CreatedAt.Before(users[j].CreatedAt)
})
return users
}
func getUserFromRow(row *sql.Row) *User {
var id string
var displayName string
var displayColor int
var createdAt time.Time
var disabledAt *time.Time
var previousUsernames string
var userNameChangedAt *time.Time
var scopesString *string
if err := row.Scan(&id, &displayName, &displayColor, &createdAt, &disabledAt, &previousUsernames, &userNameChangedAt, &scopesString); err != nil {
return nil
}
var scopes []string
if scopesString != nil {
scopes = strings.Split(*scopesString, ",")
}
return &User{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
DisabledAt: disabledAt,
PreviousNames: strings.Split(previousUsernames, ","),
NameChangedAt: userNameChangedAt,
Scopes: scopes,
}
}
+3 -4
View File
@@ -5,13 +5,12 @@ import (
"time" "time"
"github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
) )
func TestSendChatEvent(t *testing.T) { func TestSendChatEvent(t *testing.T) {
timestamp := time.Unix(72, 6).UTC() timestamp := time.Unix(72, 6).UTC()
user := user.User{ user := models.User{
ID: "user id", ID: "user id",
DisplayName: "display name", DisplayName: "display name",
DisplayColor: 4, DisplayColor: 4,
@@ -64,7 +63,7 @@ func TestSendChatEvent(t *testing.T) {
func TestSendChatEventUsernameChanged(t *testing.T) { func TestSendChatEventUsernameChanged(t *testing.T) {
timestamp := time.Unix(72, 6).UTC() timestamp := time.Unix(72, 6).UTC()
user := user.User{ user := models.User{
ID: "user id", ID: "user id",
DisplayName: "display name", DisplayName: "display name",
DisplayColor: 4, DisplayColor: 4,
@@ -112,7 +111,7 @@ func TestSendChatEventUsernameChanged(t *testing.T) {
func TestSendChatEventUserJoined(t *testing.T) { func TestSendChatEventUserJoined(t *testing.T) {
timestamp := time.Unix(72, 6).UTC() timestamp := time.Unix(72, 6).UTC()
user := user.User{ user := models.User{
ID: "user id", ID: "user id",
DisplayName: "display name", DisplayName: "display name",
DisplayColor: 4, DisplayColor: 4,
+1 -2
View File
@@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
) )
@@ -17,7 +16,7 @@ type WebhookEvent struct {
// WebhookChatMessage represents a single chat message sent as a webhook payload. // WebhookChatMessage represents a single chat message sent as a webhook payload.
type WebhookChatMessage struct { type WebhookChatMessage struct {
User *user.User `json:"user,omitempty"` User *models.User `json:"user,omitempty"`
Timestamp *time.Time `json:"timestamp,omitempty"` Timestamp *time.Time `json:"timestamp,omitempty"`
Body string `json:"body,omitempty"` Body string `json:"body,omitempty"`
RawBody string `json:"rawBody,omitempty"` RawBody string `json:"rawBody,omitempty"`
+4563 -2406
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
decorators:
remove-x-internal: on
+24 -17
View File
@@ -1,25 +1,25 @@
module github.com/owncast/owncast module github.com/owncast/owncast
go 1.21 go 1.22
require ( require (
github.com/aws/aws-sdk-go v1.50.33 github.com/aws/aws-sdk-go v1.54.13
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f
github.com/go-fed/httpsig v1.1.0 github.com/go-fed/httpsig v1.1.0
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.3
github.com/grafov/m3u8 v0.12.0 github.com/grafov/m3u8 v0.12.0
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/mattn/go-sqlite3 v1.14.22 github.com/mattn/go-sqlite3 v1.14.22
github.com/microcosm-cc/bluemonday v1.0.26 github.com/microcosm-cc/bluemonday v1.0.27
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590 github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590
github.com/oschwald/geoip2-golang v1.9.0 github.com/oschwald/geoip2-golang v1.11.0
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/schollz/sqlite3dump v1.3.1 github.com/schollz/sqlite3dump v1.3.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
github.com/yuin/goldmark v1.7.0 github.com/yuin/goldmark v1.7.4
golang.org/x/mod v0.16.0 golang.org/x/mod v0.18.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
) )
@@ -31,12 +31,12 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/crypto v0.21.0 // indirect golang.org/x/crypto v0.24.0
golang.org/x/net v0.22.0 golang.org/x/net v0.26.0
golang.org/x/sys v0.18.0 // indirect golang.org/x/sys v0.21.0 // indirect
) )
require github.com/prometheus/client_golang v1.19.0 require github.com/prometheus/client_golang v1.19.1
require ( require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
@@ -47,41 +47,48 @@ require (
github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.16.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
) )
require ( require (
github.com/nakabonne/tstorage v0.3.6 github.com/nakabonne/tstorage v0.3.6
github.com/shirou/gopsutil/v3 v3.24.2 github.com/shirou/gopsutil/v4 v4.24.6
) )
require github.com/SherClockHolmes/webpush-go v1.3.0 require github.com/SherClockHolmes/webpush-go v1.3.0
require ( require (
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-test/deep v1.0.4 // indirect github.com/go-test/deep v1.0.4 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/gorilla/css v1.0.0 // indirect github.com/google/uuid v1.5.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/oschwald/maxminddb-golang v1.11.0 // indirect github.com/oschwald/maxminddb-golang v1.13.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sync v0.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
require ( require (
github.com/CAFxX/httpcompression v0.0.9 github.com/CAFxX/httpcompression v0.0.9
github.com/TwiN/go-away v1.6.13
github.com/andybalholm/cascadia v1.3.2 github.com/andybalholm/cascadia v1.3.2
github.com/go-chi/chi/v5 v5.1.0
github.com/jellydator/ttlcache/v3 v3.2.0 github.com/jellydator/ttlcache/v3 v3.2.0
github.com/mssola/user_agent v0.6.0 github.com/mssola/user_agent v0.6.0
github.com/oapi-codegen/runtime v1.1.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/yuin/goldmark-emoji v1.0.2 github.com/yuin/goldmark-emoji v1.0.3
gopkg.in/evanphx/json-patch.v5 v5.9.0 gopkg.in/evanphx/json-patch.v5 v5.9.0
mvdan.cc/xurls v1.1.0 mvdan.cc/xurls v1.1.0
mvdan.cc/xurls/v2 v2.5.0
) )
replace github.com/go-fed/activity => github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026 replace github.com/go-fed/activity => github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026
+70 -21
View File
@@ -1,23 +1,35 @@
github.com/CAFxX/httpcompression v0.0.9 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWGKSqgrg= github.com/CAFxX/httpcompression v0.0.9 h1:0ue2X8dOLEpxTm8tt+OdHcgA+gbDge0OqFQWGKSqgrg=
github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM= github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOgYygkFhizVPilM=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k= github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k=
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw= 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/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= 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/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/aws/aws-sdk-go v1.50.33 h1:/SKPJ7ZVPCFOYZyTKo5YdjeUEeOn2J2M0qfDTXWAoEU= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/aws/aws-sdk-go v1.50.33/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/aws/aws-sdk-go v1.53.5 h1:1OcVWMjGlwt7EU5OWmmEEXqaYfmX581EK317QJZXItM=
github.com/aws/aws-sdk-go v1.53.5/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go v1.54.13 h1:zpCuiG+/mFdDY/klKJvmSioAZWk45F4rLGq0JWVAAzk=
github.com/aws/aws-sdk-go v1.54.13/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
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= github.com/go-fed/httpsig v0.1.1-0.20190914113940-c2de3672e5b5/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE=
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
@@ -35,10 +47,16 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafov/m3u8 v0.12.0 h1:T6iTwTsSEtMcwkayef+FJO8kj+Sglr4Lh81Zj8Ked/4= github.com/grafov/m3u8 v0.12.0 h1:T6iTwTsSEtMcwkayef+FJO8kj+Sglr4Lh81Zj8Ked/4=
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080= github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE= github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
@@ -49,6 +67,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@@ -69,6 +88,8 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4= github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
github.com/mvdan/xurls v1.1.0 h1:OpuDelGQ1R1ueQ6sSryzi6P+1RtBpfQHM8fJwlE45ww= github.com/mvdan/xurls v1.1.0 h1:OpuDelGQ1R1ueQ6sSryzi6P+1RtBpfQHM8fJwlE45ww=
@@ -77,10 +98,16 @@ github.com/nakabonne/tstorage v0.3.6 h1:usp7pTohax8mynnFiUSUQ2QVBCKLCkYx3gmb3+rJ
github.com/nakabonne/tstorage v0.3.6/go.mod h1:1xUrK3s1MXSlU6dn96xHerHx/MdO4BGmsAHEUbsaOxU= github.com/nakabonne/tstorage v0.3.6/go.mod h1:1xUrK3s1MXSlU6dn96xHerHx/MdO4BGmsAHEUbsaOxU=
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590 h1:PnxRU8L8Y2q82vFC2QdNw23Dm2u6WrjecIdpXjiYbXM= github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590 h1:PnxRU8L8Y2q82vFC2QdNw23Dm2u6WrjecIdpXjiYbXM=
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590/go.mod h1:XmAOs6UJXpNXRwKk+KY/nv5kL6xXYXyellk+A1pTlko= github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590/go.mod h1:XmAOs6UJXpNXRwKk+KY/nv5kL6xXYXyellk+A1pTlko=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0= github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg= github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026 h1:E1nxiX44BcMQTSSs8MHLm2rXnqXNedYZkFI31gXMsJc= github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026 h1:E1nxiX44BcMQTSSs8MHLm2rXnqXNedYZkFI31gXMsJc=
github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026/go.mod h1:v4QoPaAzjWZ8zN2VFVGL5ep9C02mst0hQYHUpQwso4Q= github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026/go.mod h1:v4QoPaAzjWZ8zN2VFVGL5ep9C02mst0hQYHUpQwso4Q=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
@@ -92,8 +119,8 @@ 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/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 h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
@@ -106,8 +133,11 @@ 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/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 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI= github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI=
github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -116,9 +146,11 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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= github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.4-0.20181223182923-24fa6976df40/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.4-0.20181223182923-24fa6976df40/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -138,10 +170,14 @@ 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/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 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-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
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/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
@@ -151,12 +187,16 @@ 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-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.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.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -165,13 +205,17 @@ 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.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.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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.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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -186,23 +230,27 @@ 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.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.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-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.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.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.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.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.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.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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -226,3 +274,4 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc= mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc=
mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI= mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI=
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
+2 -2
View File
@@ -12,8 +12,8 @@ import (
"github.com/owncast/owncast/core" "github.com/owncast/owncast/core"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/metrics" "github.com/owncast/owncast/metrics"
"github.com/owncast/owncast/router"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
"github.com/owncast/owncast/webserver/router"
) )
var ( var (
@@ -105,7 +105,7 @@ func main() {
go metrics.Start(core.GetStatus) go metrics.Start(core.GetStatus)
if err := router.Start(); err != nil { if err := router.Start(*enableVerboseLogging); err != nil {
log.Fatalln("failed to start/run the router", err) log.Fatalln("failed to start/run the router", err)
} }
} }
+3 -1
View File
@@ -7,6 +7,7 @@ import (
"github.com/owncast/owncast/core" "github.com/owncast/owncast/core"
"github.com/owncast/owncast/core/chat" "github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/userrepository"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -59,7 +60,8 @@ func collectChatClientCount() {
currentChatMessageCount.Set(float64(cmc)) currentChatMessageCount.Set(float64(cmc))
// Total user count // Total user count
uc := data.GetUsersCount() userRepository := userrepository.Get()
uc := userRepository.GetUsersCount()
// Insert user count into Prometheus collector. // Insert user count into Prometheus collector.
chatUserCount.Set(float64(uc)) chatUserCount.Set(float64(uc))
+11
View File
@@ -0,0 +1,11 @@
package models
// Type represents a form of authentication.
type AuthType string
// The different auth types we support.
const (
// IndieAuth https://indieauth.spec.indieweb.org/.
IndieAuth AuthType = "indieauth"
Fediverse AuthType = "fediverse"
)
+12
View File
@@ -0,0 +1,12 @@
package models
const (
// ScopeCanSendChatMessages will allow sending chat messages as itself.
ScopeCanSendChatMessages = "CAN_SEND_MESSAGES"
// ScopeCanSendSystemMessages will allow sending chat messages as the system.
ScopeCanSendSystemMessages = "CAN_SEND_SYSTEM_MESSAGES"
// ScopeHasAdminAccess will allow performing administrative actions on the server.
ScopeHasAdminAccess = "HAS_ADMIN_ACCESS"
ModeratorScopeKey = "MODERATOR"
)
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/owncast/owncast/geoip" "github.com/owncast/owncast/services/geoip"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
) )
+19
View File
@@ -0,0 +1,19 @@
package models
import (
"time"
)
// ExternalAPIUser represents a single 3rd party integration that uses an access token.
// This struct mostly matches the User struct so they can be used interchangeably.
type ExternalAPIUser struct {
CreatedAt time.Time `json:"createdAt"`
LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
ID string `json:"id"`
AccessToken string `json:"accessToken"`
DisplayName string `json:"displayName"`
Type string `json:"type,omitempty"` // Should be API
Scopes []string `json:"scopes"`
DisplayColor int `json:"displayColor"`
IsBot bool `json:"isBot"`
}
+36
View File
@@ -0,0 +1,36 @@
package models
import (
"time"
"github.com/owncast/owncast/utils"
)
const (
moderatorScopeKey = "MODERATOR"
)
type User struct {
CreatedAt time.Time `json:"createdAt"`
DisabledAt *time.Time `json:"disabledAt,omitempty"`
NameChangedAt *time.Time `json:"nameChangedAt,omitempty"`
AuthenticatedAt *time.Time `json:"-"`
ID string `json:"id"`
DisplayName string `json:"displayName"`
PreviousNames []string `json:"previousNames"`
Scopes []string `json:"scopes,omitempty"`
DisplayColor int `json:"displayColor"`
IsBot bool `json:"isBot"`
Authenticated bool `json:"authenticated"`
}
// IsEnabled will return if this single user is enabled.
func (u *User) IsEnabled() bool {
return u.DisabledAt == nil
}
// IsModerator will return if the user has moderation privileges.
func (u *User) IsModerator() bool {
_, hasModerationScope := utils.FindInSlice(u.Scopes, moderatorScopeKey)
return hasModerationScope
}
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/owncast/owncast/geoip" "github.com/owncast/owncast/services/geoip"
"github.com/owncast/owncast/utils" "github.com/owncast/owncast/utils"
) )
+33 -1
View File
@@ -1,11 +1,15 @@
package notifications package notifications
import ( import (
"context"
"fmt" "fmt"
"github.com/owncast/owncast/config" "github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models" "github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/notifications/browser" "github.com/owncast/owncast/notifications/browser"
"github.com/owncast/owncast/notifications/discord" "github.com/owncast/owncast/notifications/discord"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -21,7 +25,7 @@ type Notifier struct {
// Setup will perform any pre-use setup for the notifier. // Setup will perform any pre-use setup for the notifier.
func Setup(datastore *data.Datastore) { func Setup(datastore *data.Datastore) {
createNotificationsTable(datastore.DB) tables.CreateNotificationsTable(datastore.DB)
initializeBrowserPushIfNeeded() initializeBrowserPushIfNeeded()
} }
@@ -150,3 +154,31 @@ func (n *Notifier) Notify() {
n.notifyDiscord() n.notifyDiscord()
} }
} }
// 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,
})
}
// GetNotificationDestinationsForChannel will return a collection of
// destinations to notify for a given channel.
func GetNotificationDestinationsForChannel(channel string) ([]string, error) {
result, err := data.GetDatastore().GetQueries().GetNotificationDestinationsForChannel(context.Background(), channel)
if err != nil {
return nil, errors.Wrap(err, "unable to query notification destinations for channel "+channel)
}
return result, nil
}
// AddNotification saves a new user notification destination.
func AddNotification(channel, destination string) error {
return data.GetDatastore().GetQueries().AddNotification(context.Background(), db.AddNotificationParams{
Channel: channel,
Destination: destination,
})
}
-52
View File
@@ -1,52 +0,0 @@
package notifications
import (
"context"
"database/sql"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/db"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func createNotificationsTable(db *sql.DB) {
log.Traceln("Creating federation followers table...")
createTableSQL := `CREATE TABLE IF NOT EXISTS notifications (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"channel" TEXT NOT NULL,
"destination" TEXT NOT NULL,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`
data.MustExec(createTableSQL, db)
data.MustExec(`CREATE INDEX IF NOT EXISTS idx_channel ON notifications (channel);`, db)
}
// AddNotification saves a new user notification destination.
func AddNotification(channel, destination string) error {
return data.GetDatastore().GetQueries().AddNotification(context.Background(), db.AddNotificationParams{
Channel: channel,
Destination: destination,
})
}
// 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,
})
}
// GetNotificationDestinationsForChannel will return a collection of
// destinations to notify for a given channel.
func GetNotificationDestinationsForChannel(channel string) ([]string, error) {
result, err := data.GetDatastore().GetQueries().GetNotificationDestinationsForChannel(context.Background(), channel)
if err != nil {
return nil, errors.Wrap(err, "unable to query notification destinations for channel "+channel)
}
return result, nil
}
+4332 -2168
View File
File diff suppressed because one or more lines are too long
+33
View File
@@ -0,0 +1,33 @@
package tables
import (
"database/sql"
"github.com/owncast/owncast/utils"
)
// CreateMessagesTable will create the chat messages table if needed.
func CreateMessagesTable(db *sql.DB) {
createTableSQL := `CREATE TABLE IF NOT EXISTS messages (
"id" string NOT NULL,
"user_id" TEXT,
"body" TEXT,
"eventType" TEXT,
"hidden_at" DATETIME,
"timestamp" DATETIME,
"title" TEXT,
"subtitle" TEXT,
"image" TEXT,
"link" TEXT,
PRIMARY KEY (id)
);`
utils.MustExec(createTableSQL, db)
// Create indexes
utils.MustExec(`CREATE INDEX IF NOT EXISTS user_id_hidden_at_timestamp ON messages (id, user_id, hidden_at, timestamp);`, db)
utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_id ON messages (id);`, db)
utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_user_id ON messages (user_id);`, db)
utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_hidden_at ON messages (hidden_at);`, db)
utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_timestamp ON messages (timestamp);`, db)
utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_messages_hidden_at_timestamp on messages(hidden_at, timestamp);`, db)
}
@@ -1,4 +1,4 @@
package data package tables
import ( import (
"database/sql" "database/sql"
@@ -12,7 +12,7 @@ import (
"github.com/teris-io/shortid" "github.com/teris-io/shortid"
) )
func migrateDatabaseSchema(db *sql.DB, from, to int) error { func MigrateDatabaseSchema(db *sql.DB, from, to int) error {
log.Printf("Migrating database from version %d to %d", from, to) log.Printf("Migrating database from version %d to %d", from, to)
dbBackupFile := filepath.Join(config.BackupDirectory, fmt.Sprintf("owncast-v%d.bak", from)) dbBackupFile := filepath.Join(config.BackupDirectory, fmt.Sprintf("owncast-v%d.bak", from))
utils.Backup(db, dbBackupFile) utils.Backup(db, dbBackupFile)
@@ -94,7 +94,7 @@ func migrateToSchema6(db *sql.DB) {
// Fix chat messages table schema. Since chat is ephemeral we can drop // Fix chat messages table schema. Since chat is ephemeral we can drop
// the table and recreate it. // the table and recreate it.
// Drop the old messages table // Drop the old messages table
MustExec(`DROP TABLE messages`, db) utils.MustExec(`DROP TABLE messages`, db)
// Recreate it // Recreate it
CreateMessagesTable(db) CreateMessagesTable(db)
@@ -103,7 +103,7 @@ func migrateToSchema6(db *sql.DB) {
// nolint:cyclop // nolint:cyclop
func migrateToSchema5(db *sql.DB) { func migrateToSchema5(db *sql.DB) {
// Create the access tokens table. // Create the access tokens table.
createAccessTokenTable(db) CreateAccessTokenTable(db)
// 1. Authenticated bool added to the users table. // 1. Authenticated bool added to the users table.
// 2. Access tokens are now stored in their own table. // 2. Access tokens are now stored in their own table.
+21
View File
@@ -0,0 +1,21 @@
package tables
import (
"database/sql"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
func CreateNotificationsTable(db *sql.DB) {
log.Traceln("Creating federation followers table...")
createTableSQL := `CREATE TABLE IF NOT EXISTS notifications (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"channel" TEXT NOT NULL,
"destination" TEXT NOT NULL,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`
utils.MustExec(createTableSQL, db)
utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_channel ON notifications (channel);`, db)
}
@@ -1,12 +1,18 @@
package data package tables
import ( import (
"database/sql" "database/sql"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
func createAccessTokenTable(db *sql.DB) { func SetupUsers(db *sql.DB) {
CreateUsersTable(db)
CreateAccessTokenTable(db)
}
func CreateAccessTokenTable(db *sql.DB) {
createTableSQL := `CREATE TABLE IF NOT EXISTS user_access_tokens ( createTableSQL := `CREATE TABLE IF NOT EXISTS user_access_tokens (
"token" TEXT NOT NULL PRIMARY KEY, "token" TEXT NOT NULL PRIMARY KEY,
"user_id" TEXT NOT NULL, "user_id" TEXT NOT NULL,
@@ -25,7 +31,7 @@ func createAccessTokenTable(db *sql.DB) {
} }
} }
func createUsersTable(db *sql.DB) { func CreateUsersTable(db *sql.DB) {
log.Traceln("Creating users table...") log.Traceln("Creating users table...")
createTableSQL := `CREATE TABLE IF NOT EXISTS users ( createTableSQL := `CREATE TABLE IF NOT EXISTS users (
@@ -43,25 +49,8 @@ func createUsersTable(db *sql.DB) {
PRIMARY KEY (id) PRIMARY KEY (id)
);` );`
MustExec(createTableSQL, db) utils.MustExec(createTableSQL, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_user_id ON users (id);`, db) utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_user_id ON users (id);`, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_user_id_disabled ON users (id, disabled_at);`, db) utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_user_id_disabled ON users (id, disabled_at);`, db)
MustExec(`CREATE INDEX IF NOT EXISTS idx_user_disabled_at ON users (disabled_at);`, db) utils.MustExec(`CREATE INDEX IF NOT EXISTS idx_user_disabled_at ON users (disabled_at);`, db)
}
// GetUsersCount will return the number of users in the database.
func GetUsersCount() int64 {
query := `SELECT COUNT(*) FROM users`
rows, err := _db.Query(query)
if err != nil || rows.Err() != nil {
return 0
}
defer rows.Close()
var count int64
for rows.Next() {
if err := rows.Scan(&count); err != nil {
return 0
}
}
return count
} }
@@ -0,0 +1,806 @@
package userrepository
import (
"context"
"database/sql"
"fmt"
"sort"
"strings"
"time"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
"github.com/teris-io/shortid"
log "github.com/sirupsen/logrus"
)
type UserRepository interface {
ChangeUserColor(userID string, color int) error
ChangeUsername(userID string, username string) error
CreateAnonymousUser(displayName string) (*models.User, string, error)
DeleteExternalAPIUser(token string) error
GetDisabledUsers() []*models.User
GetExternalAPIUser() ([]models.ExternalAPIUser, error)
GetExternalAPIUserForAccessTokenAndScope(token string, scope string) (*models.ExternalAPIUser, error)
GetModeratorUsers() []*models.User
GetUserByID(id string) *models.User
GetUserByToken(token string) *models.User
InsertExternalAPIUser(token string, name string, color int, scopes []string) error
IsDisplayNameAvailable(displayName string) (bool, error)
SetAccessTokenToOwner(token, userID string) error
SetEnabled(userID string, enabled bool) error
SetModerator(userID string, isModerator bool) error
SetUserAsAuthenticated(userID string) error
HasValidScopes(scopes []string) bool
GetUserByAuth(authToken string, authType models.AuthType) *models.User
AddAuth(userID, authToken string, authType models.AuthType) error
SetExternalAPIUserAccessTokenAsUsed(token string) error
GetUsersCount() int
}
type SqlUserRepository struct {
datastore *data.Datastore
}
// NOTE: This is temporary during the transition period.
var temporaryGlobalInstance UserRepository
// Get will return the user repository.
func Get() UserRepository {
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) UserRepository {
r := SqlUserRepository{
datastore: datastore,
}
return &r
}
// CreateAnonymousUser will create a new anonymous user with the provided display name.
func (r *SqlUserRepository) CreateAnonymousUser(displayName string) (*models.User, string, error) {
if displayName == "" {
return nil, "", errors.New("display name cannot be empty")
}
// Try to assign a name that was requested.
// If name isn't available then generate a random one.
if available, _ := r.IsDisplayNameAvailable(displayName); !available {
rand, _ := utils.GenerateRandomString(3)
displayName += rand
}
displayColor := utils.GenerateRandomDisplayColor(config.MaxUserColor)
id := shortid.MustGenerate()
user := &models.User{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: time.Now(),
}
// Create new user.
if err := r.create(user); err != nil {
return nil, "", err
}
// Assign it an access token.
accessToken, err := utils.GenerateAccessToken()
if err != nil {
log.Errorln("Unable to create access token for new user")
return nil, "", err
}
if err := r.addAccessTokenForUser(accessToken, id); err != nil {
return nil, "", errors.Wrap(err, "unable to save access token for new user")
}
return user, accessToken, nil
}
// IsDisplayNameAvailable will check if the proposed name is available for use.
func (r *SqlUserRepository) IsDisplayNameAvailable(displayName string) (bool, error) {
if available, err := r.datastore.GetQueries().IsDisplayNameAvailable(context.Background(), displayName); err != nil {
return false, errors.Wrap(err, "unable to check if display name is available")
} else if available != 0 {
return false, nil
}
return true, nil
}
// ChangeUsername will change the user associated to userID from one display name to another.
func (r *SqlUserRepository) ChangeUsername(userID string, username string) error {
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
if err := r.datastore.GetQueries().ChangeDisplayName(context.Background(), db.ChangeDisplayNameParams{
DisplayName: username,
ID: userID,
PreviousNames: sql.NullString{String: fmt.Sprintf(",%s", username), Valid: true},
NamechangedAt: sql.NullTime{Time: time.Now(), Valid: true},
}); err != nil {
return errors.Wrap(err, "unable to change display name")
}
return nil
}
// ChangeUserColor will change the user associated to userID from one display name to another.
func (r *SqlUserRepository) ChangeUserColor(userID string, color int) error {
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
if err := r.datastore.GetQueries().ChangeDisplayColor(context.Background(), db.ChangeDisplayColorParams{
DisplayColor: int32(color),
ID: userID,
}); err != nil {
return errors.Wrap(err, "unable to change display color")
}
return nil
}
func (r *SqlUserRepository) addAccessTokenForUser(accessToken, userID string) error {
return r.datastore.GetQueries().AddAccessTokenForUser(context.Background(), db.AddAccessTokenForUserParams{
Token: accessToken,
UserID: userID,
})
}
func (r *SqlUserRepository) create(user *models.User) error {
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
tx, err := r.datastore.DB.Begin()
if err != nil {
log.Debugln(err)
}
defer func() {
_ = tx.Rollback()
}()
stmt, err := tx.Prepare("INSERT INTO users(id, display_name, display_color, previous_names, created_at) values(?, ?, ?, ?, ?)")
if err != nil {
log.Debugln(err)
}
defer stmt.Close()
_, err = stmt.Exec(user.ID, user.DisplayName, user.DisplayColor, user.DisplayName, user.CreatedAt)
if err != nil {
log.Errorln("error creating new user", err)
return err
}
return tx.Commit()
}
// SetEnabled will set the enabled status of a single user by ID.
func (r *SqlUserRepository) SetEnabled(userID string, enabled bool) error {
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
tx, err := r.datastore.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback() //nolint
var stmt *sql.Stmt
if !enabled {
stmt, err = tx.Prepare("UPDATE users SET disabled_at=DATETIME('now', 'localtime') WHERE id IS ?")
} else {
stmt, err = tx.Prepare("UPDATE users SET disabled_at=null WHERE id IS ?")
}
if err != nil {
return err
}
defer stmt.Close()
if _, err := stmt.Exec(userID); err != nil {
return err
}
return tx.Commit()
}
// GetUserByToken will return a user by an access token.
func (r *SqlUserRepository) GetUserByToken(token string) *models.User {
u, err := r.datastore.GetQueries().GetUserByAccessToken(context.Background(), token)
if err != nil {
return nil
}
var scopes []string
if u.Scopes.Valid {
scopes = strings.Split(u.Scopes.String, ",")
}
var disabledAt *time.Time
if u.DisabledAt.Valid {
disabledAt = &u.DisabledAt.Time
}
var authenticatedAt *time.Time
if u.AuthenticatedAt.Valid {
authenticatedAt = &u.AuthenticatedAt.Time
}
return &models.User{
ID: u.ID,
DisplayName: u.DisplayName,
DisplayColor: int(u.DisplayColor),
CreatedAt: u.CreatedAt.Time,
DisabledAt: disabledAt,
PreviousNames: strings.Split(u.PreviousNames.String, ","),
NameChangedAt: &u.NamechangedAt.Time,
AuthenticatedAt: authenticatedAt,
Authenticated: authenticatedAt != nil,
Scopes: scopes,
}
}
// SetAccessTokenToOwner will reassign an access token to be owned by a
// different user. Used for logging in with external auth.
func (r *SqlUserRepository) SetAccessTokenToOwner(token, userID string) error {
return r.datastore.GetQueries().SetAccessTokenToOwner(context.Background(), db.SetAccessTokenToOwnerParams{
UserID: userID,
Token: token,
})
}
// SetUserAsAuthenticated will mark that a user has been authenticated
// in some way.
func (r *SqlUserRepository) SetUserAsAuthenticated(userID string) error {
return errors.Wrap(r.datastore.GetQueries().SetUserAsAuthenticated(context.Background(), userID), "unable to set user as authenticated")
}
// AddAuth will add an external authentication token and type for a user.
func (r *SqlUserRepository) AddAuth(userID, authToken string, authType models.AuthType) error {
return r.datastore.GetQueries().AddAuthForUser(context.Background(), db.AddAuthForUserParams{
UserID: userID,
Token: authToken,
Type: string(authType),
})
}
// GetUserByAuth will return an existing user given auth details if a user
// has previously authenticated with that method.
func (r *SqlUserRepository) GetUserByAuth(authToken string, authType models.AuthType) *models.User {
u, err := r.datastore.GetQueries().GetUserByAuth(context.Background(), db.GetUserByAuthParams{
Token: authToken,
Type: string(authType),
})
if err != nil {
return nil
}
var scopes []string
if u.Scopes.Valid {
scopes = strings.Split(u.Scopes.String, ",")
}
return &models.User{
ID: u.ID,
DisplayName: u.DisplayName,
DisplayColor: int(u.DisplayColor),
CreatedAt: u.CreatedAt.Time,
DisabledAt: &u.DisabledAt.Time,
PreviousNames: strings.Split(u.PreviousNames.String, ","),
NameChangedAt: &u.NamechangedAt.Time,
AuthenticatedAt: &u.AuthenticatedAt.Time,
Scopes: scopes,
}
}
// SetModerator will add or remove moderator status for a single user by ID.
func (r *SqlUserRepository) SetModerator(userID string, isModerator bool) error {
if isModerator {
return r.addScopeToUser(userID, models.ModeratorScopeKey)
}
return r.removeScopeFromUser(userID, models.ModeratorScopeKey)
}
func (r *SqlUserRepository) addScopeToUser(userID string, scope string) error {
u := r.GetUserByID(userID)
if u == nil {
return errors.New("user not found when modifying scope")
}
scopesString := u.Scopes
scopes := utils.StringSliceToMap(scopesString)
scopes[scope] = true
scopesSlice := utils.StringMapKeys(scopes)
return r.setScopesOnUser(userID, scopesSlice)
}
func (r *SqlUserRepository) removeScopeFromUser(userID string, scope string) error {
u := r.GetUserByID(userID)
scopesString := u.Scopes
scopes := utils.StringSliceToMap(scopesString)
delete(scopes, scope)
scopesSlice := utils.StringMapKeys(scopes)
return r.setScopesOnUser(userID, scopesSlice)
}
func (r *SqlUserRepository) setScopesOnUser(userID string, scopes []string) error {
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
tx, err := r.datastore.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback() //nolint
scopesSliceString := strings.TrimSpace(strings.Join(scopes, ","))
stmt, err := tx.Prepare("UPDATE users SET scopes=? WHERE id IS ?")
if err != nil {
return err
}
defer stmt.Close()
var val *string
if scopesSliceString == "" {
val = nil
} else {
val = &scopesSliceString
}
if _, err := stmt.Exec(val, userID); err != nil {
return err
}
return tx.Commit()
}
// GetUserByID will return a user by a user ID.
func (r *SqlUserRepository) GetUserByID(id string) *models.User {
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
query := "SELECT id, display_name, display_color, created_at, disabled_at, previous_names, namechanged_at, scopes FROM users WHERE id = ?"
row := r.datastore.DB.QueryRow(query, id)
if row == nil {
log.Errorln(row)
return nil
}
return r.getUserFromRow(row)
}
// GetDisabledUsers will return back all the currently disabled users that are not API users.
func (r *SqlUserRepository) GetDisabledUsers() []*models.User {
query := "SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at FROM users WHERE disabled_at IS NOT NULL AND type IS NOT 'API'"
rows, err := r.datastore.DB.Query(query)
if err != nil {
log.Errorln(err)
return nil
}
defer rows.Close()
users := r.getUsersFromRows(rows)
sort.Slice(users, func(i, j int) bool {
return users[i].DisabledAt.Before(*users[j].DisabledAt)
})
return users
}
// GetModeratorUsers will return a list of users with moderator access.
func (r *SqlUserRepository) GetModeratorUsers() []*models.User {
query := `SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at FROM (
WITH RECURSIVE split(id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at, scope, rest) AS (
SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at, '', scopes || ',' FROM users
UNION ALL
SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at,
substr(rest, 0, instr(rest, ',')),
substr(rest, instr(rest, ',')+1)
FROM split
WHERE rest <> '')
SELECT id, display_name, scopes, display_color, created_at, disabled_at, previous_names, namechanged_at, scope
FROM split
WHERE scope <> ''
ORDER BY created_at
) AS token WHERE token.scope = ?`
rows, err := r.datastore.DB.Query(query, models.ModeratorScopeKey)
if err != nil {
log.Errorln(err)
return nil
}
defer rows.Close()
users := r.getUsersFromRows(rows)
return users
}
func (r *SqlUserRepository) getUsersFromRows(rows *sql.Rows) []*models.User {
users := make([]*models.User, 0)
for rows.Next() {
var id string
var displayName string
var displayColor int
var createdAt time.Time
var disabledAt *time.Time
var previousUsernames string
var userNameChangedAt *time.Time
var scopesString *string
if err := rows.Scan(&id, &displayName, &scopesString, &displayColor, &createdAt, &disabledAt, &previousUsernames, &userNameChangedAt); err != nil {
log.Errorln("error creating collection of users from results", err)
return nil
}
var scopes []string
if scopesString != nil {
scopes = strings.Split(*scopesString, ",")
}
user := &models.User{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
DisabledAt: disabledAt,
PreviousNames: strings.Split(previousUsernames, ","),
NameChangedAt: userNameChangedAt,
Scopes: scopes,
}
users = append(users, user)
}
sort.Slice(users, func(i, j int) bool {
return users[i].CreatedAt.Before(users[j].CreatedAt)
})
return users
}
func (r *SqlUserRepository) getUserFromRow(row *sql.Row) *models.User {
var id string
var displayName string
var displayColor int
var createdAt time.Time
var disabledAt *time.Time
var previousUsernames string
var userNameChangedAt *time.Time
var scopesString *string
if err := row.Scan(&id, &displayName, &displayColor, &createdAt, &disabledAt, &previousUsernames, &userNameChangedAt, &scopesString); err != nil {
return nil
}
var scopes []string
if scopesString != nil {
scopes = strings.Split(*scopesString, ",")
}
return &models.User{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
DisabledAt: disabledAt,
PreviousNames: strings.Split(previousUsernames, ","),
NameChangedAt: userNameChangedAt,
Scopes: scopes,
}
}
// InsertExternalAPIUser will add a new API user to the database.
func (r *SqlUserRepository) InsertExternalAPIUser(token string, name string, color int, scopes []string) error {
log.Traceln("Adding new API user")
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
scopesString := strings.Join(scopes, ",")
id := shortid.MustGenerate()
tx, err := r.datastore.DB.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("INSERT INTO users(id, display_name, display_color, scopes, type, previous_names) values(?, ?, ?, ?, ?, ?)")
if err != nil {
return err
}
defer stmt.Close()
if _, err = stmt.Exec(id, name, color, scopesString, "API", name); err != nil {
return err
}
if err = tx.Commit(); err != nil {
return err
}
if err := r.addAccessTokenForUser(token, id); err != nil {
return errors.Wrap(err, "unable to save access token for new external api user")
}
return nil
}
// DeleteExternalAPIUser will delete a token from the database.
func (r *SqlUserRepository) DeleteExternalAPIUser(token string) error {
log.Traceln("Deleting access token")
r.datastore.DbLock.Lock()
defer r.datastore.DbLock.Unlock()
tx, err := r.datastore.DB.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("UPDATE users SET disabled_at = CURRENT_TIMESTAMP WHERE id = (SELECT user_id FROM user_access_tokens WHERE token = ?)")
if err != nil {
return err
}
defer stmt.Close()
result, err := stmt.Exec(token)
if err != nil {
return err
}
if rowsDeleted, _ := result.RowsAffected(); rowsDeleted == 0 {
tx.Rollback() //nolint
return errors.New(token + " not found")
}
if err = tx.Commit(); err != nil {
return err
}
return nil
}
// GetExternalAPIUserForAccessTokenAndScope will determine if a specific token has access to perform a scoped action.
func (r *SqlUserRepository) GetExternalAPIUserForAccessTokenAndScope(token string, scope string) (*models.ExternalAPIUser, error) {
// This will split the scopes from comma separated to individual rows
// so we can efficiently find if a token supports a single scope.
// This is SQLite specific, so if we ever support other database
// backends we need to support other methods.
query := `SELECT
id,
scopes,
display_name,
display_color,
created_at,
last_used
FROM
user_access_tokens
INNER JOIN (
WITH RECURSIVE split(
id,
scopes,
display_name,
display_color,
created_at,
last_used,
disabled_at,
scope,
rest
) AS (
SELECT
id,
scopes,
display_name,
display_color,
created_at,
last_used,
disabled_at,
'',
scopes || ','
FROM
users AS u
UNION ALL
SELECT
id,
scopes,
display_name,
display_color,
created_at,
last_used,
disabled_at,
substr(rest, 0, instr(rest, ',')),
substr(rest, instr(rest, ',') + 1)
FROM
split
WHERE
rest <> ''
)
SELECT
id,
display_name,
display_color,
created_at,
last_used,
disabled_at,
scopes,
scope
FROM
split
WHERE
scope <> ''
) ON user_access_tokens.user_id = id
WHERE
disabled_at IS NULL
AND token = ?
AND scope = ?;`
row := r.datastore.DB.QueryRow(query, token, scope)
integration, err := r.makeExternalAPIUserFromRow(row)
return integration, err
}
// GetIntegrationNameForAccessToken will return the integration name associated with a specific access token.
func (r *SqlUserRepository) GetIntegrationNameForAccessToken(token string) *string {
name, err := r.datastore.GetQueries().GetUserDisplayNameByToken(context.Background(), token)
if err != nil {
return nil
}
return &name
}
// GetExternalAPIUser will return all API users with access tokens.
func (r *SqlUserRepository) GetExternalAPIUser() ([]models.ExternalAPIUser, error) { //nolint
query := "SELECT id, token, display_name, display_color, scopes, created_at, last_used FROM users, user_access_tokens WHERE user_access_tokens.user_id = id AND type IS 'API' AND disabled_at IS NULL"
rows, err := r.datastore.DB.Query(query)
if err != nil {
return []models.ExternalAPIUser{}, err
}
defer rows.Close()
integrations, err := r.makeExternalAPIUsersFromRows(rows)
return integrations, err
}
// SetExternalAPIUserAccessTokenAsUsed will update the last used timestamp for a token.
func (r *SqlUserRepository) SetExternalAPIUserAccessTokenAsUsed(token string) error {
tx, err := r.datastore.DB.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("UPDATE users SET last_used = CURRENT_TIMESTAMP WHERE id = (SELECT user_id FROM user_access_tokens WHERE token = ?)")
if err != nil {
return err
}
defer stmt.Close()
if _, err := stmt.Exec(token); err != nil {
return err
}
if err = tx.Commit(); err != nil {
return err
}
return nil
}
func (r *SqlUserRepository) makeExternalAPIUserFromRow(row *sql.Row) (*models.ExternalAPIUser, error) {
var id string
var displayName string
var displayColor int
var scopes string
var createdAt time.Time
var lastUsedAt *time.Time
err := row.Scan(&id, &scopes, &displayName, &displayColor, &createdAt, &lastUsedAt)
if err != nil {
log.Debugln("unable to convert row to api user", err)
return nil, err
}
integration := models.ExternalAPIUser{
ID: id,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
Scopes: strings.Split(scopes, ","),
LastUsedAt: lastUsedAt,
}
return &integration, nil
}
func (r *SqlUserRepository) makeExternalAPIUsersFromRows(rows *sql.Rows) ([]models.ExternalAPIUser, error) {
integrations := make([]models.ExternalAPIUser, 0)
for rows.Next() {
var id string
var accessToken string
var displayName string
var displayColor int
var scopes string
var createdAt time.Time
var lastUsedAt *time.Time
err := rows.Scan(&id, &accessToken, &displayName, &displayColor, &scopes, &createdAt, &lastUsedAt)
if err != nil {
log.Errorln(err)
return nil, err
}
integration := models.ExternalAPIUser{
ID: id,
AccessToken: accessToken,
DisplayName: displayName,
DisplayColor: displayColor,
CreatedAt: createdAt,
Scopes: strings.Split(scopes, ","),
LastUsedAt: lastUsedAt,
IsBot: true,
}
integrations = append(integrations, integration)
}
return integrations, nil
}
// HasValidScopes will verify that all the scopes provided are valid.
func (r *SqlUserRepository) HasValidScopes(scopes []string) bool {
// For a scope to be seen as "valid" it must live in this slice.
validAccessTokenScopes := []string{
models.ScopeCanSendChatMessages,
models.ScopeCanSendSystemMessages,
models.ScopeHasAdminAccess,
}
for _, scope := range scopes {
_, foundInSlice := utils.FindInSlice(validAccessTokenScopes, scope)
if !foundInSlice {
return false
}
}
return true
}
// GetUsersCount will return the number of users in the database.
func (r *SqlUserRepository) GetUsersCount() int {
query := `SELECT COUNT(*) FROM users`
rows, err := r.datastore.DB.Query(query)
if err != nil || rows.Err() != nil {
return 0
}
defer rows.Close()
var count int
for rows.Next() {
if err := rows.Scan(&count); err != nil {
return 0
}
}
return count
}
-444
View File
@@ -1,444 +0,0 @@
package router
import (
"fmt"
"net/http"
"time"
"github.com/CAFxX/httpcompression"
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"github.com/owncast/owncast/activitypub"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/controllers/admin"
fediverseauth "github.com/owncast/owncast/controllers/auth/fediverse"
"github.com/owncast/owncast/controllers/auth/indieauth"
"github.com/owncast/owncast/controllers/moderation"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/router/middleware"
"github.com/owncast/owncast/utils"
"github.com/owncast/owncast/yp"
)
// Start starts the router for the http, ws, and rtmp.
func Start() error {
// The primary web app.
http.HandleFunc("/", controllers.IndexHandler)
// The admin web app.
http.HandleFunc("/admin/", middleware.RequireAdminAuth(controllers.IndexHandler))
// Images
http.HandleFunc("/thumbnail.jpg", controllers.GetThumbnail)
http.HandleFunc("/preview.gif", controllers.GetPreview)
http.HandleFunc("/logo", controllers.GetLogo)
// Custom Javascript
http.HandleFunc("/customjavascript", controllers.ServeCustomJavascript)
// Return a single emoji image.
http.HandleFunc(config.EmojiDir, controllers.GetCustomEmojiImage)
// return the logo
// return a logo that's compatible with external social networks
http.HandleFunc("/logo/external", controllers.GetCompatibleLogo)
// robots.txt
http.HandleFunc("/robots.txt", controllers.GetRobotsDotTxt)
// status of the system
http.HandleFunc("/api/status", controllers.GetStatus)
// custom emoji supported in the chat
http.HandleFunc("/api/emoji", controllers.GetCustomEmojiList)
// chat rest api
http.HandleFunc("/api/chat", middleware.RequireUserAccessToken(controllers.GetChatMessages))
// web config api
http.HandleFunc("/api/config", controllers.GetWebConfig)
// return the YP protocol data
http.HandleFunc("/api/yp", yp.GetYPResponse)
// list of all social platforms
http.HandleFunc("/api/socialplatforms", controllers.GetAllSocialPlatforms)
// return the list of video variants available
http.HandleFunc("/api/video/variants", controllers.GetVideoStreamOutputVariants)
// tell the backend you're an active viewer
http.HandleFunc("/api/ping", controllers.Ping)
// register a new chat user
http.HandleFunc("/api/chat/register", controllers.RegisterAnonymousChatUser)
// return remote follow details
http.HandleFunc("/api/remotefollow", controllers.RemoteFollow)
// return followers
http.HandleFunc("/api/followers", middleware.HandlePagination(controllers.GetFollowers))
// save client video playback metrics
http.HandleFunc("/api/metrics/playback", controllers.ReportPlaybackMetrics)
// Register for notifications
http.HandleFunc("/api/notifications/register", middleware.RequireUserAccessToken(controllers.RegisterForLiveNotifications))
// Authenticated admin requests
// Current inbound broadcaster
http.HandleFunc("/api/admin/status", middleware.RequireAdminAuth(admin.Status))
// Return HLS video
http.HandleFunc("/hls/", controllers.HandleHLSRequest)
// Disconnect inbound stream
http.HandleFunc("/api/admin/disconnect", middleware.RequireAdminAuth(admin.DisconnectInboundConnection))
// Server config
http.HandleFunc("/api/admin/serverconfig", middleware.RequireAdminAuth(admin.GetServerConfig))
// Get viewer count over time
http.HandleFunc("/api/admin/viewersOverTime", middleware.RequireAdminAuth(admin.GetViewersOverTime))
// Get active viewers
http.HandleFunc("/api/admin/viewers", middleware.RequireAdminAuth(admin.GetActiveViewers))
// Get hardware stats
http.HandleFunc("/api/admin/hardwarestats", middleware.RequireAdminAuth(admin.GetHardwareStats))
// Get a a detailed list of currently connected chat clients
http.HandleFunc("/api/admin/chat/clients", middleware.RequireAdminAuth(admin.GetConnectedChatClients))
// Get all logs
http.HandleFunc("/api/admin/logs", middleware.RequireAdminAuth(admin.GetLogs))
// Get warning/error logs
http.HandleFunc("/api/admin/logs/warnings", middleware.RequireAdminAuth(admin.GetWarnings))
// Get all chat messages for the admin, unfiltered.
http.HandleFunc("/api/admin/chat/messages", middleware.RequireAdminAuth(admin.GetChatMessages))
// Update chat message visibility
http.HandleFunc("/api/admin/chat/messagevisibility", middleware.RequireAdminAuth(admin.UpdateMessageVisibility))
// Enable/disable a user
http.HandleFunc("/api/admin/chat/users/setenabled", middleware.RequireAdminAuth(admin.UpdateUserEnabled))
// Ban/unban an IP address
http.HandleFunc("/api/admin/chat/users/ipbans/create", middleware.RequireAdminAuth(admin.BanIPAddress))
// Remove an IP address ban
http.HandleFunc("/api/admin/chat/users/ipbans/remove", middleware.RequireAdminAuth(admin.UnBanIPAddress))
// Return all the banned IP addresses
http.HandleFunc("/api/admin/chat/users/ipbans", middleware.RequireAdminAuth(admin.GetIPAddressBans))
// Get a list of disabled users
http.HandleFunc("/api/admin/chat/users/disabled", middleware.RequireAdminAuth(admin.GetDisabledUsers))
// Set moderator status for a user
http.HandleFunc("/api/admin/chat/users/setmoderator", middleware.RequireAdminAuth(admin.UpdateUserModerator))
// Get a list of moderator users
http.HandleFunc("/api/admin/chat/users/moderators", middleware.RequireAdminAuth(admin.GetModerators))
// return followers
http.HandleFunc("/api/admin/followers", middleware.RequireAdminAuth(middleware.HandlePagination(controllers.GetFollowers)))
// Get a list of pending follow requests
http.HandleFunc("/api/admin/followers/pending", middleware.RequireAdminAuth(admin.GetPendingFollowRequests))
// Get a list of rejected or blocked follows
http.HandleFunc("/api/admin/followers/blocked", middleware.RequireAdminAuth(admin.GetBlockedAndRejectedFollowers))
// Set the following state of a follower or follow request.
http.HandleFunc("/api/admin/followers/approve", middleware.RequireAdminAuth(admin.ApproveFollower))
// Upload custom emoji
http.HandleFunc("/api/admin/emoji/upload", middleware.RequireAdminAuth(admin.UploadCustomEmoji))
// Delete custom emoji
http.HandleFunc("/api/admin/emoji/delete", middleware.RequireAdminAuth(admin.DeleteCustomEmoji))
// Update config values
// Change the current streaming key in memory
http.HandleFunc("/api/admin/config/adminpass", middleware.RequireAdminAuth(admin.SetAdminPassword))
// Set an array of valid stream keys
http.HandleFunc("/api/admin/config/streamkeys", middleware.RequireAdminAuth(admin.SetStreamKeys))
// Change the extra page content in memory
http.HandleFunc("/api/admin/config/pagecontent", middleware.RequireAdminAuth(admin.SetExtraPageContent))
// Stream title
http.HandleFunc("/api/admin/config/streamtitle", middleware.RequireAdminAuth(admin.SetStreamTitle))
// Server name
http.HandleFunc("/api/admin/config/name", middleware.RequireAdminAuth(admin.SetServerName))
// Server summary
http.HandleFunc("/api/admin/config/serversummary", middleware.RequireAdminAuth(admin.SetServerSummary))
// Offline message
http.HandleFunc("/api/admin/config/offlinemessage", middleware.RequireAdminAuth(admin.SetCustomOfflineMessage))
// Server welcome message
http.HandleFunc("/api/admin/config/welcomemessage", middleware.RequireAdminAuth(admin.SetServerWelcomeMessage))
// Disable chat
http.HandleFunc("/api/admin/config/chat/disable", middleware.RequireAdminAuth(admin.SetChatDisabled))
// Disable chat user join messages
http.HandleFunc("/api/admin/config/chat/joinmessagesenabled", middleware.RequireAdminAuth(admin.SetChatJoinMessagesEnabled))
// Enable/disable chat established user mode
http.HandleFunc("/api/admin/config/chat/establishedusermode", middleware.RequireAdminAuth(admin.SetEnableEstablishedChatUserMode))
// Set chat usernames that are not allowed
http.HandleFunc("/api/admin/config/chat/forbiddenusernames", middleware.RequireAdminAuth(admin.SetForbiddenUsernameList))
// Set the suggested chat usernames that will be assigned automatically
http.HandleFunc("/api/admin/config/chat/suggestedusernames", middleware.RequireAdminAuth(admin.SetSuggestedUsernameList))
// Set video codec
http.HandleFunc("/api/admin/config/video/codec", middleware.RequireAdminAuth(admin.SetVideoCodec))
// Set style/color/css values
http.HandleFunc("/api/admin/config/appearance", middleware.RequireAdminAuth(admin.SetCustomColorVariableValues))
// Return all webhooks
http.HandleFunc("/api/admin/webhooks", middleware.RequireAdminAuth(admin.GetWebhooks))
// Delete a single webhook
http.HandleFunc("/api/admin/webhooks/delete", middleware.RequireAdminAuth(admin.DeleteWebhook))
// Create a single webhook
http.HandleFunc("/api/admin/webhooks/create", middleware.RequireAdminAuth(admin.CreateWebhook))
// Get all access tokens
http.HandleFunc("/api/admin/accesstokens", middleware.RequireAdminAuth(admin.GetExternalAPIUsers))
// Delete a single access token
http.HandleFunc("/api/admin/accesstokens/delete", middleware.RequireAdminAuth(admin.DeleteExternalAPIUser))
// Create a single access token
http.HandleFunc("/api/admin/accesstokens/create", middleware.RequireAdminAuth(admin.CreateExternalAPIUser))
// Return the auto-update features that are supported for this instance.
http.HandleFunc("/api/admin/update/options", middleware.RequireAdminAuth(admin.AutoUpdateOptions))
// Begin the auto update
http.HandleFunc("/api/admin/update/start", middleware.RequireAdminAuth(admin.AutoUpdateStart))
// Force quit the service to restart it
http.HandleFunc("/api/admin/update/forcequit", middleware.RequireAdminAuth(admin.AutoUpdateForceQuit))
// Send a system message to chat
http.HandleFunc("/api/integrations/chat/system", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, admin.SendSystemMessage))
// Send a system message to a single client
http.HandleFunc(utils.RestEndpoint("/api/integrations/chat/system/client/{clientId}", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, admin.SendSystemMessageToConnectedClient)))
// Send a user message to chat *NO LONGER SUPPORTED
http.HandleFunc("/api/integrations/chat/user", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendChatMessages, admin.SendUserMessage))
// Send a message to chat as a specific 3rd party bot/integration based on its access token
http.HandleFunc("/api/integrations/chat/send", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendChatMessages, admin.SendIntegrationChatMessage))
// Send a user action to chat
http.HandleFunc("/api/integrations/chat/action", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, admin.SendChatAction))
// Hide chat message
http.HandleFunc("/api/integrations/chat/messagevisibility", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, admin.ExternalUpdateMessageVisibility))
// Stream title
http.HandleFunc("/api/integrations/streamtitle", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, admin.ExternalSetStreamTitle))
// Get chat history
http.HandleFunc("/api/integrations/chat", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, controllers.ExternalGetChatMessages))
// Connected clients
http.HandleFunc("/api/integrations/clients", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, admin.ExternalGetConnectedChatClients))
// Logo path
http.HandleFunc("/api/admin/config/logo", middleware.RequireAdminAuth(admin.SetLogo))
// Server tags
http.HandleFunc("/api/admin/config/tags", middleware.RequireAdminAuth(admin.SetTags))
// ffmpeg
http.HandleFunc("/api/admin/config/ffmpegpath", middleware.RequireAdminAuth(admin.SetFfmpegPath))
// Server http port
http.HandleFunc("/api/admin/config/webserverport", middleware.RequireAdminAuth(admin.SetWebServerPort))
// Server http listen address
http.HandleFunc("/api/admin/config/webserverip", middleware.RequireAdminAuth(admin.SetWebServerIP))
// Server rtmp port
http.HandleFunc("/api/admin/config/rtmpserverport", middleware.RequireAdminAuth(admin.SetRTMPServerPort))
// Websocket host override
http.HandleFunc("/api/admin/config/sockethostoverride", middleware.RequireAdminAuth(admin.SetSocketHostOverride))
// Custom video serving endpoint
http.HandleFunc("/api/admin/config/videoservingendpoint", middleware.RequireAdminAuth(admin.SetVideoServingEndpoint))
// Is server marked as NSFW
http.HandleFunc("/api/admin/config/nsfw", middleware.RequireAdminAuth(admin.SetNSFW))
// directory enabled
http.HandleFunc("/api/admin/config/directoryenabled", middleware.RequireAdminAuth(admin.SetDirectoryEnabled))
// social handles
http.HandleFunc("/api/admin/config/socialhandles", middleware.RequireAdminAuth(admin.SetSocialHandles))
// set the number of video segments and duration per segment in a playlist
http.HandleFunc("/api/admin/config/video/streamlatencylevel", middleware.RequireAdminAuth(admin.SetStreamLatencyLevel))
// set an array of video output configurations
http.HandleFunc("/api/admin/config/video/streamoutputvariants", middleware.RequireAdminAuth(admin.SetStreamOutputVariants))
// set s3 configuration
http.HandleFunc("/api/admin/config/s3", middleware.RequireAdminAuth(admin.SetS3Configuration))
// set server url
http.HandleFunc("/api/admin/config/serverurl", middleware.RequireAdminAuth(admin.SetServerURL))
// reset the YP registration
http.HandleFunc("/api/admin/yp/reset", middleware.RequireAdminAuth(admin.ResetYPRegistration))
// set external action links
http.HandleFunc("/api/admin/config/externalactions", middleware.RequireAdminAuth(admin.SetExternalActions))
// set custom style css
http.HandleFunc("/api/admin/config/customstyles", middleware.RequireAdminAuth(admin.SetCustomStyles))
// set custom style javascript
http.HandleFunc("/api/admin/config/customjavascript", middleware.RequireAdminAuth(admin.SetCustomJavascript))
// Video playback metrics
http.HandleFunc("/api/admin/metrics/video", middleware.RequireAdminAuth(admin.GetVideoPlaybackMetrics))
// Is the viewer count hidden from viewers
http.HandleFunc("/api/admin/config/hideviewercount", middleware.RequireAdminAuth(admin.SetHideViewerCount))
// set disabling of search indexing
http.HandleFunc("/api/admin/config/disablesearchindexing", middleware.RequireAdminAuth(admin.SetDisableSearchIndexing))
// Inline chat moderation actions
// Update chat message visibility
http.HandleFunc("/api/chat/messagevisibility", middleware.RequireUserModerationScopeAccesstoken(admin.UpdateMessageVisibility))
// Enable/disable a user
http.HandleFunc("/api/chat/users/setenabled", middleware.RequireUserModerationScopeAccesstoken(admin.UpdateUserEnabled))
// Get a user's details
http.HandleFunc("/api/moderation/chat/user/", middleware.RequireUserModerationScopeAccesstoken(moderation.GetUserDetails))
// Configure Federation features
// enable/disable federation features
http.HandleFunc("/api/admin/config/federation/enable", middleware.RequireAdminAuth(admin.SetFederationEnabled))
// set if federation activities are private
http.HandleFunc("/api/admin/config/federation/private", middleware.RequireAdminAuth(admin.SetFederationActivityPrivate))
// set if fediverse engagement appears in chat
http.HandleFunc("/api/admin/config/federation/showengagement", middleware.RequireAdminAuth(admin.SetFederationShowEngagement))
// set local federated username
http.HandleFunc("/api/admin/config/federation/username", middleware.RequireAdminAuth(admin.SetFederationUsername))
// set federated go live message
http.HandleFunc("/api/admin/config/federation/livemessage", middleware.RequireAdminAuth(admin.SetFederationGoLiveMessage))
// Federation blocked domains
http.HandleFunc("/api/admin/config/federation/blockdomains", middleware.RequireAdminAuth(admin.SetFederationBlockDomains))
// send a public message to the Fediverse from the server's user
http.HandleFunc("/api/admin/federation/send", middleware.RequireAdminAuth(admin.SendFederatedMessage))
// Return federated activities
http.HandleFunc("/api/admin/federation/actions", middleware.RequireAdminAuth(middleware.HandlePagination(admin.GetFederatedActions)))
// Prometheus metrics
http.Handle("/api/admin/prometheus", middleware.RequireAdminAuth(func(rw http.ResponseWriter, r *http.Request) {
promhttp.Handler().ServeHTTP(rw, r)
}))
// Configure outbound notification channels.
http.HandleFunc("/api/admin/config/notifications/discord", middleware.RequireAdminAuth(admin.SetDiscordNotificationConfiguration))
http.HandleFunc("/api/admin/config/notifications/browser", middleware.RequireAdminAuth(admin.SetBrowserNotificationConfiguration))
// Auth
// Start auth flow
http.HandleFunc("/api/auth/indieauth", middleware.RequireUserAccessToken(indieauth.StartAuthFlow))
http.HandleFunc("/api/auth/indieauth/callback", indieauth.HandleRedirect)
http.HandleFunc("/api/auth/provider/indieauth", indieauth.HandleAuthEndpoint)
http.HandleFunc("/api/auth/fediverse", middleware.RequireUserAccessToken(fediverseauth.RegisterFediverseOTPRequest))
http.HandleFunc("/api/auth/fediverse/verify", fediverseauth.VerifyFediverseOTPRequest)
// ActivityPub has its own router
activitypub.Start(data.GetDatastore())
// websocket
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
chat.HandleClientConnection(w, r)
})
// Optional public static files
http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir(config.PublicFilesPath))))
port := config.WebServerPort
ip := config.WebServerIP
h2s := &http2.Server{}
// Create a custom mux handler to intercept the /debug/vars endpoint.
// This is a hack because Prometheus enables this endpoint by default
// due to its use of expvar and we do not want this exposed.
defaultMux := h2c.NewHandler(http.DefaultServeMux, h2s)
m := http.NewServeMux()
m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/debug/vars" {
w.WriteHeader(http.StatusNotFound)
return
} else if r.URL.Path == "/embed/chat/" || r.URL.Path == "/embed/chat" {
// Redirect /embed/chat
http.Redirect(w, r, "/embed/chat/readonly", http.StatusTemporaryRedirect)
} else {
defaultMux.ServeHTTP(w, r)
}
})
compress, _ := httpcompression.DefaultAdapter() // Use the default configuration
server := &http.Server{
Addr: fmt.Sprintf("%s:%d", ip, port),
ReadHeaderTimeout: 4 * time.Second,
Handler: compress(m),
}
if ip != "0.0.0.0" {
log.Infof("Web server is listening at %s:%d.", ip, port)
} else {
log.Infof("Web server is listening on port %d.", port)
}
log.Infoln("Configure this server by visiting /admin.")
return server.ListenAndServe()
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+2 -2
View File
@@ -51,12 +51,12 @@ func GetWebIndexTemplate() (*template.Template, error) {
return tmpl, err return tmpl, err
} }
//go:embed offline.tsclip //go:embed offline-v2.ts
var offlineVideoSegment []byte var offlineVideoSegment []byte
// GetOfflineSegment will return the offline video segment data. // GetOfflineSegment will return the offline video segment data.
func GetOfflineSegment() []byte { func GetOfflineSegment() []byte {
return getFileSystemStaticFileOrDefault("offline.tsclip", offlineVideoSegment) return getFileSystemStaticFileOrDefault("offline-v2.ts", offlineVideoSegment)
} }
//go:embed img/logo.png //go:embed img/logo.png
+3 -3
View File
File diff suppressed because one or more lines are too long
+3 -3
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1008],{34214:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"}},{tag:"path",attrs:{d:"M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"}}]},name:"eye-invisible",theme:"outlined"}},71008:function(e,t,a){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,n=(r=a(87866))&&r.__esModule?r:{default:r};t.default=n,e.exports=n},87866:function(e,t,a){var r=a(64836),n=a(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=r(a(42122)),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var a=i(t);if(a&&a.has(e))return a.get(e);var r={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&({}).hasOwnProperty.call(e,l)){var f=u?Object.getOwnPropertyDescriptor(e,l):null;f&&(f.get||f.set)?Object.defineProperty(r,l,f):r[l]=e[l]}return r.default=e,a&&a.set(e,r),r}(a(67294)),f=r(a(34214)),o=r(a(17298));function i(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,a=new WeakMap;return(i=function(e){return e?a:t})(e)}var c=l.forwardRef(function(e,t){return l.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:f.default}))});t.default=c}}]);
@@ -1 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6121],{96121:function(e,r,o){function t(e){var r={as:"keyword",do:"keyword",else:"keyword",end:"keyword",exception:"keyword",fun:"keyword",functor:"keyword",if:"keyword",in:"keyword",include:"keyword",let:"keyword",of:"keyword",open:"keyword",rec:"keyword",struct:"keyword",then:"keyword",type:"keyword",val:"keyword",while:"keyword",with:"keyword"},o=e.extraWords||{};for(var t in o)o.hasOwnProperty(t)&&(r[t]=e.extraWords[t]);var n=[];for(var i in r)n.push(i);function d(o,t){var n=o.next();if('"'===n)return t.tokenize=k,t.tokenize(o,t);if("{"===n&&o.eat("|"))return t.longString=!0,t.tokenize=y,t.tokenize(o,t);if("("===n&&o.match(/^\*(?!\))/))return t.commentLevel++,t.tokenize=w,t.tokenize(o,t);if("~"===n||"?"===n)return o.eatWhile(/\w/),"variableName.special";if("`"===n)return o.eatWhile(/\w/),"quote";if("/"===n&&e.slashComments&&o.eat("/"))return o.skipToEnd(),"comment";if(/\d/.test(n))return"0"===n&&o.eat(/[bB]/)&&o.eatWhile(/[01]/),"0"===n&&o.eat(/[xX]/)&&o.eatWhile(/[0-9a-fA-F]/),"0"===n&&o.eat(/[oO]/)?o.eatWhile(/[0-7]/):(o.eatWhile(/[\d_]/),o.eat(".")&&o.eatWhile(/[\d]/),o.eat(/[eE]/)&&o.eatWhile(/[\d\-+]/)),"number";if(/[+\-*&%=<>!?|@\.~:]/.test(n))return"operator";if(/[\w\xa1-\uffff]/.test(n)){o.eatWhile(/[\w\xa1-\uffff]/);var i=o.current();return r.hasOwnProperty(i)?r[i]:"variable"}return null}function k(e,r){for(var o,t=!1,n=!1;null!=(o=e.next());){if('"'===o&&!n){t=!0;break}n=!n&&"\\"===o}return t&&!n&&(r.tokenize=d),"string"}function w(e,r){for(var o,t;r.commentLevel>0&&null!=(t=e.next());)"("===o&&"*"===t&&r.commentLevel++,"*"===o&&")"===t&&r.commentLevel--,o=t;return r.commentLevel<=0&&(r.tokenize=d),"comment"}function y(e,r){for(var o,t;r.longString&&null!=(t=e.next());)"|"===o&&"}"===t&&(r.longString=!1),o=t;return r.longString||(r.tokenize=d),"string"}return{startState:function(){return{tokenize:d,commentLevel:0,longString:!1}},token:function(e,r){return e.eatSpace()?null:r.tokenize(e,r)},languageData:{autocomplete:n,commentTokens:{line:e.slashComments?"//":void 0,block:{open:"(*",close:"*)"}}}}}o.r(r),o.d(r,{fSharp:function(){return i},oCaml:function(){return n},sml:function(){return d}});let n=t({name:"ocaml",extraWords:{and:"keyword",assert:"keyword",begin:"keyword",class:"keyword",constraint:"keyword",done:"keyword",downto:"keyword",external:"keyword",function:"keyword",initializer:"keyword",lazy:"keyword",match:"keyword",method:"keyword",module:"keyword",mutable:"keyword",new:"keyword",nonrec:"keyword",object:"keyword",private:"keyword",sig:"keyword",to:"keyword",try:"keyword",value:"keyword",virtual:"keyword",when:"keyword",raise:"builtin",failwith:"builtin",true:"builtin",false:"builtin",asr:"builtin",land:"builtin",lor:"builtin",lsl:"builtin",lsr:"builtin",lxor:"builtin",mod:"builtin",or:"builtin",raise_notrace:"builtin",trace:"builtin",exit:"builtin",print_string:"builtin",print_endline:"builtin",int:"type",float:"type",bool:"type",char:"type",string:"type",unit:"type",List:"builtin"}}),i=t({name:"fsharp",extraWords:{abstract:"keyword",assert:"keyword",base:"keyword",begin:"keyword",class:"keyword",default:"keyword",delegate:"keyword","do!":"keyword",done:"keyword",downcast:"keyword",downto:"keyword",elif:"keyword",extern:"keyword",finally:"keyword",for:"keyword",function:"keyword",global:"keyword",inherit:"keyword",inline:"keyword",interface:"keyword",internal:"keyword",lazy:"keyword","let!":"keyword",match:"keyword",member:"keyword",module:"keyword",mutable:"keyword",namespace:"keyword",new:"keyword",null:"keyword",override:"keyword",private:"keyword",public:"keyword","return!":"keyword",return:"keyword",select:"keyword",static:"keyword",to:"keyword",try:"keyword",upcast:"keyword","use!":"keyword",use:"keyword",void:"keyword",when:"keyword","yield!":"keyword",yield:"keyword",atomic:"keyword",break:"keyword",checked:"keyword",component:"keyword",const:"keyword",constraint:"keyword",constructor:"keyword",continue:"keyword",eager:"keyword",event:"keyword",external:"keyword",fixed:"keyword",method:"keyword",mixin:"keyword",object:"keyword",parallel:"keyword",process:"keyword",protected:"keyword",pure:"keyword",sealed:"keyword",tailcall:"keyword",trait:"keyword",virtual:"keyword",volatile:"keyword",List:"builtin",Seq:"builtin",Map:"builtin",Set:"builtin",Option:"builtin",int:"builtin",string:"builtin",not:"builtin",true:"builtin",false:"builtin",raise:"builtin",failwith:"builtin"},slashComments:!0}),d=t({name:"sml",extraWords:{abstype:"keyword",and:"keyword",andalso:"keyword",case:"keyword",datatype:"keyword",fn:"keyword",handle:"keyword",infix:"keyword",infixr:"keyword",local:"keyword",nonfix:"keyword",op:"keyword",orelse:"keyword",raise:"keyword",withtype:"keyword",eqtype:"keyword",sharing:"keyword",sig:"keyword",signature:"keyword",structure:"keyword",where:"keyword",true:"keyword",false:"keyword",int:"builtin",real:"builtin",string:"builtin",char:"builtin",bool:"builtin"},slashComments:!0})}}]); "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1027],{96121:function(e,r,o){function t(e){var r={as:"keyword",do:"keyword",else:"keyword",end:"keyword",exception:"keyword",fun:"keyword",functor:"keyword",if:"keyword",in:"keyword",include:"keyword",let:"keyword",of:"keyword",open:"keyword",rec:"keyword",struct:"keyword",then:"keyword",type:"keyword",val:"keyword",while:"keyword",with:"keyword"},o=e.extraWords||{};for(var t in o)o.hasOwnProperty(t)&&(r[t]=e.extraWords[t]);var n=[];for(var i in r)n.push(i);function d(o,t){var n=o.next();if('"'===n)return t.tokenize=k,t.tokenize(o,t);if("{"===n&&o.eat("|"))return t.longString=!0,t.tokenize=y,t.tokenize(o,t);if("("===n&&o.match(/^\*(?!\))/))return t.commentLevel++,t.tokenize=w,t.tokenize(o,t);if("~"===n||"?"===n)return o.eatWhile(/\w/),"variableName.special";if("`"===n)return o.eatWhile(/\w/),"quote";if("/"===n&&e.slashComments&&o.eat("/"))return o.skipToEnd(),"comment";if(/\d/.test(n))return"0"===n&&o.eat(/[bB]/)&&o.eatWhile(/[01]/),"0"===n&&o.eat(/[xX]/)&&o.eatWhile(/[0-9a-fA-F]/),"0"===n&&o.eat(/[oO]/)?o.eatWhile(/[0-7]/):(o.eatWhile(/[\d_]/),o.eat(".")&&o.eatWhile(/[\d]/),o.eat(/[eE]/)&&o.eatWhile(/[\d\-+]/)),"number";if(/[+\-*&%=<>!?|@\.~:]/.test(n))return"operator";if(/[\w\xa1-\uffff]/.test(n)){o.eatWhile(/[\w\xa1-\uffff]/);var i=o.current();return r.hasOwnProperty(i)?r[i]:"variable"}return null}function k(e,r){for(var o,t=!1,n=!1;null!=(o=e.next());){if('"'===o&&!n){t=!0;break}n=!n&&"\\"===o}return t&&!n&&(r.tokenize=d),"string"}function w(e,r){for(var o,t;r.commentLevel>0&&null!=(t=e.next());)"("===o&&"*"===t&&r.commentLevel++,"*"===o&&")"===t&&r.commentLevel--,o=t;return r.commentLevel<=0&&(r.tokenize=d),"comment"}function y(e,r){for(var o,t;r.longString&&null!=(t=e.next());)"|"===o&&"}"===t&&(r.longString=!1),o=t;return r.longString||(r.tokenize=d),"string"}return{startState:function(){return{tokenize:d,commentLevel:0,longString:!1}},token:function(e,r){return e.eatSpace()?null:r.tokenize(e,r)},languageData:{autocomplete:n,commentTokens:{line:e.slashComments?"//":void 0,block:{open:"(*",close:"*)"}}}}}o.r(r),o.d(r,{fSharp:function(){return i},oCaml:function(){return n},sml:function(){return d}});let n=t({name:"ocaml",extraWords:{and:"keyword",assert:"keyword",begin:"keyword",class:"keyword",constraint:"keyword",done:"keyword",downto:"keyword",external:"keyword",function:"keyword",initializer:"keyword",lazy:"keyword",match:"keyword",method:"keyword",module:"keyword",mutable:"keyword",new:"keyword",nonrec:"keyword",object:"keyword",private:"keyword",sig:"keyword",to:"keyword",try:"keyword",value:"keyword",virtual:"keyword",when:"keyword",raise:"builtin",failwith:"builtin",true:"builtin",false:"builtin",asr:"builtin",land:"builtin",lor:"builtin",lsl:"builtin",lsr:"builtin",lxor:"builtin",mod:"builtin",or:"builtin",raise_notrace:"builtin",trace:"builtin",exit:"builtin",print_string:"builtin",print_endline:"builtin",int:"type",float:"type",bool:"type",char:"type",string:"type",unit:"type",List:"builtin"}}),i=t({name:"fsharp",extraWords:{abstract:"keyword",assert:"keyword",base:"keyword",begin:"keyword",class:"keyword",default:"keyword",delegate:"keyword","do!":"keyword",done:"keyword",downcast:"keyword",downto:"keyword",elif:"keyword",extern:"keyword",finally:"keyword",for:"keyword",function:"keyword",global:"keyword",inherit:"keyword",inline:"keyword",interface:"keyword",internal:"keyword",lazy:"keyword","let!":"keyword",match:"keyword",member:"keyword",module:"keyword",mutable:"keyword",namespace:"keyword",new:"keyword",null:"keyword",override:"keyword",private:"keyword",public:"keyword","return!":"keyword",return:"keyword",select:"keyword",static:"keyword",to:"keyword",try:"keyword",upcast:"keyword","use!":"keyword",use:"keyword",void:"keyword",when:"keyword","yield!":"keyword",yield:"keyword",atomic:"keyword",break:"keyword",checked:"keyword",component:"keyword",const:"keyword",constraint:"keyword",constructor:"keyword",continue:"keyword",eager:"keyword",event:"keyword",external:"keyword",fixed:"keyword",method:"keyword",mixin:"keyword",object:"keyword",parallel:"keyword",process:"keyword",protected:"keyword",pure:"keyword",sealed:"keyword",tailcall:"keyword",trait:"keyword",virtual:"keyword",volatile:"keyword",List:"builtin",Seq:"builtin",Map:"builtin",Set:"builtin",Option:"builtin",int:"builtin",string:"builtin",not:"builtin",true:"builtin",false:"builtin",raise:"builtin",failwith:"builtin"},slashComments:!0}),d=t({name:"sml",extraWords:{abstype:"keyword",and:"keyword",andalso:"keyword",case:"keyword",datatype:"keyword",fn:"keyword",handle:"keyword",infix:"keyword",infixr:"keyword",local:"keyword",nonfix:"keyword",op:"keyword",orelse:"keyword",raise:"keyword",withtype:"keyword",eqtype:"keyword",sharing:"keyword",sig:"keyword",signature:"keyword",structure:"keyword",where:"keyword",true:"keyword",false:"keyword",int:"builtin",real:"builtin",string:"builtin",char:"builtin",bool:"builtin"},slashComments:!0})}}]);
@@ -0,0 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[108],{65089:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 464h-68V240c0-70.7-57.3-128-128-128H388c-70.7 0-128 57.3-128 128v224h-68c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V496c0-17.7-14.3-32-32-32zM332 240c0-30.9 25.1-56 56-56h248c30.9 0 56 25.1 56 56v224H332V240zm460 600H232V536h560v304zM484 701v53c0 4.4 3.6 8 8 8h40c4.4 0 8-3.6 8-8v-53a48.01 48.01 0 10-56 0z"}}]},name:"lock",theme:"outlined"}},30108:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,u=(n=r(34811))&&n.__esModule?n:{default:n};t.default=u,e.exports=u},34811:function(e,t,r){var n=r(64836),u=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=n(r(42122)),f=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=u(e)&&"function"!=typeof e)return{default:e};var r=l(void 0);if(r&&r.has(e))return r.get(e);var n={__proto__:null},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var o=a?Object.getOwnPropertyDescriptor(e,f):null;o&&(o.get||o.set)?Object.defineProperty(n,f,o):n[f]=e[f]}return n.default=e,r&&r.set(e,n),n}(r(67294)),o=n(r(65089)),c=n(r(3908));function l(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(l=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(c.default,(0,a.default)((0,a.default)({},e),{},{ref:t,icon:o.default}))});t.default=i}}]);
@@ -1 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2807],{98494:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M881.7 187.4l-45.1-45.1a8.03 8.03 0 00-11.3 0L667.8 299.9l-54.7-54.7a7.94 7.94 0 00-13.5 4.7L576.1 439c-.6 5.2 3.7 9.5 8.9 8.9l189.2-23.5c6.6-.8 9.3-8.8 4.7-13.5l-54.7-54.7 157.6-157.6c3-3 3-8.1-.1-11.2zM439 576.1l-189.2 23.5c-6.6.8-9.3 8.9-4.7 13.5l54.7 54.7-157.5 157.5a8.03 8.03 0 000 11.3l45.1 45.1c3.1 3.1 8.2 3.1 11.3 0l157.6-157.6 54.7 54.7a7.94 7.94 0 0013.5-4.7L447.9 585a7.9 7.9 0 00-8.9-8.9z"}}]},name:"shrink",theme:"outlined"}},42807:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(6044))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},6044:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(t);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&({}).hasOwnProperty.call(e,l)){var f=u?Object.getOwnPropertyDescriptor(e,l):null;f&&(f.get||f.set)?Object.defineProperty(a,l,f):a[l]=e[l]}return a.default=e,r&&r.set(e,a),a}(r(67294)),f=a(r(98494)),o=a(r(17298));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=l.forwardRef(function(e,t){return l.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:f.default}))});t.default=i}}]); "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1189],{92262:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M881.7 187.4l-45.1-45.1a8.03 8.03 0 00-11.3 0L667.8 299.9l-54.7-54.7a7.94 7.94 0 00-13.5 4.7L576.1 439c-.6 5.2 3.7 9.5 8.9 8.9l189.2-23.5c6.6-.8 9.3-8.8 4.7-13.5l-54.7-54.7 157.6-157.6c3-3 3-8.1-.1-11.2zM439 576.1l-189.2 23.5c-6.6.8-9.3 8.9-4.7 13.5l54.7 54.7-157.5 157.5a8.03 8.03 0 000 11.3l45.1 45.1c3.1 3.1 8.2 3.1 11.3 0l157.6-157.6 54.7 54.7a7.94 7.94 0 0013.5-4.7L447.9 585a7.9 7.9 0 00-8.9-8.9z"}}]},name:"shrink",theme:"outlined"}},91189:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(85e3))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},85e3:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),l=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(void 0);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&({}).hasOwnProperty.call(e,l)){var f=u?Object.getOwnPropertyDescriptor(e,l):null;f&&(f.get||f.set)?Object.defineProperty(a,l,f):a[l]=e[l]}return a.default=e,r&&r.set(e,a),a}(r(67294)),f=a(r(92262)),o=a(r(3908));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=l.forwardRef(function(e,t){return l.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:f.default}))});t.default=i}}]);
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1377],{3797:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"}}]},name:"loading",theme:"outlined"}},81377:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,a=(n=r(66443))&&n.__esModule?n:{default:n};t.default=a,e.exports=a},66443:function(e,t,r){var n=r(64836),a=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=n(r(42122)),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=a(e)&&"function"!=typeof e)return{default:e};var r=c(t);if(r&&r.has(e))return r.get(e);var n={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var o=u?Object.getOwnPropertyDescriptor(e,f):null;o&&(o.get||o.set)?Object.defineProperty(n,f,o):n[f]=e[f]}return n.default=e,r&&r.set(e,n),n}(r(67294)),o=n(r(3797)),l=n(r(17298));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(l.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:o.default}))});t.default=i}}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1460],{76545:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"}}]},name:"edit",theme:"outlined"}},51460:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,a=(n=r(65689))&&n.__esModule?n:{default:n};t.default=a,e.exports=a},65689:function(e,t,r){var n=r(64836),a=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=n(r(42122)),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=a(e)&&"function"!=typeof e)return{default:e};var r=c(t);if(r&&r.has(e))return r.get(e);var n={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var l=u?Object.getOwnPropertyDescriptor(e,f):null;l&&(l.get||l.set)?Object.defineProperty(n,f,l):n[f]=e[f]}return n.default=e,r&&r.set(e,n),n}(r(67294)),l=n(r(76545)),o=n(r(17298));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:l.default}))});t.default=i}}]);
@@ -1 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3801],{60762:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"}}]},name:"exclamation-circle",theme:"filled"}},83801:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(35398))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},35398:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(t);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var l=u?Object.getOwnPropertyDescriptor(e,f):null;l&&(l.get||l.set)?Object.defineProperty(a,f,l):a[f]=e[f]}return a.default=e,r&&r.set(e,a),a}(r(67294)),l=a(r(60762)),o=a(r(17298));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:l.default}))});t.default=i}}]); "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1483],{86233:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"}}]},name:"exclamation-circle",theme:"filled"}},31483:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(75036))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},75036:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),f=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(void 0);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var l=u?Object.getOwnPropertyDescriptor(e,f):null;l&&(l.get||l.set)?Object.defineProperty(a,f,l):a[f]=e[f]}return a.default=e,r&&r.set(e,a),a}(r(67294)),l=a(r(86233)),o=a(r(3908));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:l.default}))});t.default=i}}]);
@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1557],{96994:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M876.6 239.5c-.5-.9-1.2-1.8-2-2.5-5-5-13.1-5-18.1 0L684.2 409.3l-67.9-67.9L788.7 169c.8-.8 1.4-1.6 2-2.5 3.6-6.1 1.6-13.9-4.5-17.5-98.2-58-226.8-44.7-311.3 39.7-67 67-89.2 162-66.5 247.4l-293 293c-3 3-2.8 7.9.3 11l169.7 169.7c3.1 3.1 8.1 3.3 11 .3l292.9-292.9c85.5 22.8 180.5.7 247.6-66.4 84.4-84.5 97.7-213.1 39.7-311.3zM786 499.8c-58.1 58.1-145.3 69.3-214.6 33.6l-8.8 8.8-.1-.1-274 274.1-79.2-79.2 230.1-230.1s0 .1.1.1l52.8-52.8c-35.7-69.3-24.5-156.5 33.6-214.6a184.2 184.2 0 01144-53.5L537 318.9a32.05 32.05 0 000 45.3l124.5 124.5a32.05 32.05 0 0045.3 0l132.8-132.8c3.7 51.8-14.4 104.8-53.6 143.9z"}}]},name:"tool",theme:"outlined"}},61557:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(4388))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},4388:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(t);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&({}).hasOwnProperty.call(e,l)){var f=u?Object.getOwnPropertyDescriptor(e,l):null;f&&(f.get||f.set)?Object.defineProperty(a,l,f):a[l]=e[l]}return a.default=e,r&&r.set(e,a),a}(r(67294)),f=a(r(96994)),o=a(r(17298));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=l.forwardRef(function(e,t){return l.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:f.default}))});t.default=i}}]);
@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1576],{73551:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M888 792H200V168c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v688c0 4.4 3.6 8 8 8h752c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM305.8 637.7c3.1 3.1 8.1 3.1 11.3 0l138.3-137.6L583 628.5c3.1 3.1 8.2 3.1 11.3 0l275.4-275.3c3.1-3.1 3.1-8.2 0-11.3l-39.6-39.6a8.03 8.03 0 00-11.3 0l-230 229.9L461.4 404a8.03 8.03 0 00-11.3 0L266.3 586.7a8.03 8.03 0 000 11.3l39.5 39.7z"}}]},name:"line-chart",theme:"outlined"}},71576:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(38099))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},38099:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(t);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&({}).hasOwnProperty.call(e,l)){var f=u?Object.getOwnPropertyDescriptor(e,l):null;f&&(f.get||f.set)?Object.defineProperty(a,l,f):a[l]=e[l]}return a.default=e,r&&r.set(e,a),a}(r(67294)),f=a(r(73551)),o=a(r(17298));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=l.forwardRef(function(e,t){return l.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:f.default}))});t.default=i}}]);
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1664],{49659:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M866.9 169.9L527.1 54.1C523 52.7 517.5 52 512 52s-11 .7-15.1 2.1L157.1 169.9c-8.3 2.8-15.1 12.4-15.1 21.2v482.4c0 8.8 5.7 20.4 12.6 25.9L499.3 968c3.5 2.7 8 4.1 12.6 4.1s9.2-1.4 12.6-4.1l344.7-268.6c6.9-5.4 12.6-17 12.6-25.9V191.1c.2-8.8-6.6-18.3-14.9-21.2zM694.5 340.7L481.9 633.4a16.1 16.1 0 01-26 0l-126.4-174c-3.8-5.3 0-12.7 6.5-12.7h55.2c5.1 0 10 2.5 13 6.6l64.7 89 150.9-207.8c3-4.1 7.8-6.6 13-6.6H688c6.5.1 10.3 7.5 6.5 12.8z"}}]},name:"safety-certificate",theme:"filled"}},1664:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(94992))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},94992:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=o(t);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var l=u?Object.getOwnPropertyDescriptor(e,f):null;l&&(l.get||l.set)?Object.defineProperty(a,f,l):a[f]=e[f]}return a.default=e,r&&r.set(e,a),a}(r(67294)),l=a(r(49659)),c=a(r(17298));function o(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(o=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(c.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:l.default}))});t.default=i}}]);
@@ -0,0 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1680],{76456:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M946.5 505L560.1 118.8l-25.9-25.9a31.5 31.5 0 00-44.4 0L77.5 505a63.9 63.9 0 00-18.8 46c.4 35.2 29.7 63.3 64.9 63.3h42.5V940h691.8V614.3h43.4c17.1 0 33.2-6.7 45.3-18.8a63.6 63.6 0 0018.7-45.3c0-17-6.7-33.1-18.8-45.2zM568 868H456V664h112v204zm217.9-325.7V868H632V640c0-22.1-17.9-40-40-40H432c-22.1 0-40 17.9-40 40v228H238.1V542.3h-96l370-369.7 23.1 23.1L882 542.3h-96.1z"}}]},name:"home",theme:"outlined"}},81680:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(13127))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},13127:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),f=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(void 0);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var o=u?Object.getOwnPropertyDescriptor(e,f):null;o&&(o.get||o.set)?Object.defineProperty(a,f,o):a[f]=e[f]}return a.default=e,r&&r.set(e,a),a}(r(67294)),o=a(r(76456)),l=a(r(3908));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(l.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:o.default}))});t.default=i}}]);
@@ -0,0 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[177],{98226:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M288 421a48 48 0 1096 0 48 48 0 10-96 0zm352 0a48 48 0 1096 0 48 48 0 10-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 01248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 01249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 01775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 01775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 00-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 00-8-8.4z"}}]},name:"smile",theme:"outlined"}},80177:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a,n=(a=r(70232))&&a.__esModule?a:{default:a};t.default=n,e.exports=n},70232:function(e,t,r){var a=r(64836),n=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=a(r(42122)),f=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=n(e)&&"function"!=typeof e)return{default:e};var r=c(void 0);if(r&&r.has(e))return r.get(e);var a={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var o=u?Object.getOwnPropertyDescriptor(e,f):null;o&&(o.get||o.set)?Object.defineProperty(a,f,o):a[f]=e[f]}return a.default=e,r&&r.set(e,a),a}(r(67294)),o=a(r(98226)),l=a(r(3908));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(l.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:o.default}))});t.default=i}}]);
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7418],{12578:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:function(e,t){return{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M761.1 288.3L687.8 215 325.1 577.6l-15.6 89 88.9-15.7z",fill:t}},{tag:"path",attrs:{d:"M880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32zm-622.3-84c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89z",fill:e}}]}},name:"edit",theme:"twotone"}},37418:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,a=(n=r(89099))&&n.__esModule?n:{default:n};t.default=a,e.exports=a},89099:function(e,t,r){var n=r(64836),a=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=n(r(42122)),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=a(e)&&"function"!=typeof e)return{default:e};var r=c(t);if(r&&r.has(e))return r.get(e);var n={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var l=u?Object.getOwnPropertyDescriptor(e,f):null;l&&(l.get||l.set)?Object.defineProperty(n,f,l):n[f]=e[f]}return n.default=e,r&&r.set(e,n),n}(r(67294)),l=n(r(12578)),o=n(r(17298));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:l.default}))});t.default=i}}]); "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1825],{17139:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={icon:function(e,t){return{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M761.1 288.3L687.8 215 325.1 577.6l-15.6 89 88.9-15.7z",fill:t}},{tag:"path",attrs:{d:"M880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32zm-622.3-84c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89z",fill:e}}]}},name:"edit",theme:"twotone"}},61825:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,a=(n=r(20524))&&n.__esModule?n:{default:n};t.default=a,e.exports=a},20524:function(e,t,r){var n=r(64836),a=r(18698);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u=n(r(42122)),f=function(e,t){if(e&&e.__esModule)return e;if(null===e||"object"!=a(e)&&"function"!=typeof e)return{default:e};var r=c(void 0);if(r&&r.has(e))return r.get(e);var n={__proto__:null},u=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in e)if("default"!==f&&({}).hasOwnProperty.call(e,f)){var l=u?Object.getOwnPropertyDescriptor(e,f):null;l&&(l.get||l.set)?Object.defineProperty(n,f,l):n[f]=e[f]}return n.default=e,r&&r.set(e,n),n}(r(67294)),l=n(r(17139)),o=n(r(3908));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(c=function(e){return e?r:t})(e)}var i=f.forwardRef(function(e,t){return f.createElement(o.default,(0,u.default)((0,u.default)({},e),{},{ref:t,icon:l.default}))});t.default=i}}]);
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