forked from mirrors/gotosocial
Compare commits
10 commits
cc69250bbe
...
b3194f1fee
Author | SHA1 | Date | |
---|---|---|---|
b3194f1fee | |||
af29c06290 | |||
|
0ddc2edf19 | ||
|
3920bc87d1 | ||
|
4b05dcde43 | ||
|
9df4d38c43 | ||
|
be3718f6e4 | ||
|
517829ae6a | ||
|
0f812746b7 | ||
|
303a6a6b1d |
60 changed files with 1563 additions and 1171 deletions
10
README.md
10
README.md
|
@ -91,13 +91,13 @@ For a detailed view on what's implemented and what's not, and progress made towa
|
||||||
|
|
||||||
The Mastodon API has become the de facto standard for client communication with federated servers, so GoToSocial has implemented and extended the API with custom functionality.
|
The Mastodon API has become the de facto standard for client communication with federated servers, so GoToSocial has implemented and extended the API with custom functionality.
|
||||||
|
|
||||||
In short, this means full support for modern, beautiful apps like [Tusky](https://tusky.app/) and [Semaphore](https://semaphore.social/).
|
Though most apps that implement the Mastodon API should work, GoToSocial works reliably with beautiful apps like:
|
||||||
|
|
||||||
Tusky | Semaphore
|
* [Tusky](https://tusky.app/) for Android
|
||||||
:-----------------------------------------------------------:|:------------------------------------------------------------------:
|
* [Semaphore](https://semaphore.social/) in the browser
|
||||||
![An image of GoToSocial in Tusky](./docs/assets/tusky.png) | ![An image of GoToSocial in Semaphore](./docs/assets/semaphore.png)
|
* [Feditext](https://fedi.software/@Feditext) (beta) on iOS, iPadOS and macOS
|
||||||
|
|
||||||
If you're used to using Mastodon with Tusky or Semaphore, you'll find using GoToSocial a breeze.
|
If you've used Mastodon with any of these apps before, you'll find using GoToSocial a breeze.
|
||||||
|
|
||||||
### Granular post settings
|
### Granular post settings
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 673 KiB |
Binary file not shown.
Before Width: | Height: | Size: 624 KiB |
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Where's the user interface?
|
## Where's the user interface?
|
||||||
|
|
||||||
GoToSocial is just a bare server for the most part and is designed to be used thru external applications. [Semaphore](https://semaphore.social/) and [Tusky](https://tusky.app/) are the best-supported, but anything that supports the Mastodon API should work, other than the features GoToSocial doesn't yet have. Permalinks and profile pages are served directly through GoToSocial as well as the settings panel, but most interaction goes through the apps.
|
GoToSocial is just a bare server for the most part and is designed to be used thru external applications. [Semaphore](https://semaphore.social/) in the browser, [Tusky](https://tusky.app/) for Android and [Feditext](https://fedi.software/@Feditext) for iOS, iPadOS and macOS are the best-supported. Anything that supports the Mastodon API should work, other than the features GoToSocial doesn't yet have. Permalinks and profile pages are served directly through GoToSocial as well as the settings panel, but most interaction goes through the apps.
|
||||||
|
|
||||||
## Why aren't my posts showing up on my profile page?
|
## Why aren't my posts showing up on my profile page?
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ You can use the GoToSocial binary to also create and promote your user account.
|
||||||
|
|
||||||
## Login
|
## Login
|
||||||
|
|
||||||
You should now be able to log in to your instance using the email address and password of the account you just created. We recommend using [Semaphore](https://semaphore.social) or [Tusky](https://tusky.app) for this.
|
You should now be able to log in to your instance using the email address and password of the account you just created.
|
||||||
|
|
||||||
## (Optional) Enable the systemd service
|
## (Optional) Enable the systemd service
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ Since GoToSocial is still in alpha, there are plenty of bugs. We use [GitHub iss
|
||||||
|
|
||||||
### Client App Issues
|
### Client App Issues
|
||||||
|
|
||||||
GoToSocial works great with Tusky and Semaphore, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
|
GoToSocial works great with Tusky, Semaphore and Feditext, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
|
||||||
|
|
||||||
### Federation Issues
|
### Federation Issues
|
||||||
|
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -45,7 +45,7 @@ require (
|
||||||
github.com/superseriousbusiness/activity v1.4.0-gts
|
github.com/superseriousbusiness/activity v1.4.0-gts
|
||||||
github.com/superseriousbusiness/exif-terminator v0.5.0
|
github.com/superseriousbusiness/exif-terminator v0.5.0
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8
|
||||||
github.com/tdewolff/minify/v2 v2.12.7
|
github.com/tdewolff/minify/v2 v2.12.8
|
||||||
github.com/ulule/limiter/v3 v3.11.2
|
github.com/ulule/limiter/v3 v3.11.2
|
||||||
github.com/uptrace/bun v1.1.14
|
github.com/uptrace/bun v1.1.14
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.14
|
github.com/uptrace/bun/dialect/pgdialect v1.1.14
|
||||||
|
@ -62,8 +62,8 @@ require (
|
||||||
golang.org/x/crypto v0.12.0
|
golang.org/x/crypto v0.12.0
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||||
golang.org/x/image v0.11.0
|
golang.org/x/image v0.11.0
|
||||||
golang.org/x/net v0.12.0
|
golang.org/x/net v0.14.0
|
||||||
golang.org/x/oauth2 v0.10.0
|
golang.org/x/oauth2 v0.11.0
|
||||||
golang.org/x/text v0.12.0
|
golang.org/x/text v0.12.0
|
||||||
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
|
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
@ -152,7 +152,7 @@ require (
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.6.6 // indirect
|
github.com/tdewolff/parse/v2 v2.6.7 // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
|
|
20
go.sum
20
go.sum
|
@ -111,7 +111,6 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
|
@ -149,7 +148,6 @@ 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/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
|
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
||||||
|
@ -438,7 +436,6 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
@ -564,11 +561,10 @@ github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430
|
||||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe/go.mod h1:gH4P6gN1V+wmIw5o97KGaa1RgXB/tVpC2UNzijhg3E4=
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe/go.mod h1:gH4P6gN1V+wmIw5o97KGaa1RgXB/tVpC2UNzijhg3E4=
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8 h1:nTIhuP157oOFcscuoK1kCme1xTeGIzztSw70lX9NrDQ=
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8 h1:nTIhuP157oOFcscuoK1kCme1xTeGIzztSw70lX9NrDQ=
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8/go.mod h1:uYC/W92oVRJ49Vh1GcvTqpeFqHi+Ovrl2sMllQWRAEo=
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8/go.mod h1:uYC/W92oVRJ49Vh1GcvTqpeFqHi+Ovrl2sMllQWRAEo=
|
||||||
github.com/tdewolff/minify/v2 v2.12.7 h1:pBzz2tAfz5VghOXiQIsSta6srhmTeinQPjRDHWoumCA=
|
github.com/tdewolff/minify/v2 v2.12.8 h1:Q2BqOTmlMjoutkuD/OPCnJUpIqrzT3nRPkw+q+KpXS0=
|
||||||
github.com/tdewolff/minify/v2 v2.12.7/go.mod h1:ZRKTheiOGyLSK8hOZWWv+YoJAECzDivNgAlVYDHp/Ws=
|
github.com/tdewolff/minify/v2 v2.12.8/go.mod h1:YRgk7CC21LZnbuke2fmYnCTq+zhCgpb0yJACOTUNJ1E=
|
||||||
github.com/tdewolff/parse/v2 v2.6.6 h1:Yld+0CrKUJaCV78DL1G2nk3C9lKrxyRTux5aaK/AkDo=
|
github.com/tdewolff/parse/v2 v2.6.7 h1:WrFllrqmzAcrKHzoYgMupqgUBIfBVOb0yscFzDf8bBg=
|
||||||
github.com/tdewolff/parse/v2 v2.6.6/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
github.com/tdewolff/parse/v2 v2.6.7/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
|
||||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
|
||||||
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
|
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
|
||||||
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||||
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E=
|
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E=
|
||||||
|
@ -779,8 +775,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
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.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -792,8 +788,8 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
|
||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
|
||||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/auth"
|
"github.com/superseriousbusiness/gotosocial/internal/api/auth"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthAuthorizeTestSuite struct {
|
type AuthAuthorizeTestSuite struct {
|
||||||
|
@ -51,8 +51,8 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
||||||
mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string {
|
mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string {
|
||||||
user.ConfirmedAt = time.Now()
|
user.ConfirmedAt = time.Now()
|
||||||
user.Email = user.UnconfirmedEmail
|
user.Email = user.UnconfirmedEmail
|
||||||
user.Approved = testrig.TrueBool()
|
user.Approved = util.Ptr(true)
|
||||||
user.Disabled = testrig.TrueBool()
|
user.Disabled = util.Ptr(true)
|
||||||
return []string{"confirmed_at", "email", "approved", "disabled"}
|
return []string{"confirmed_at", "email", "approved", "disabled"}
|
||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusSeeOther,
|
expectedStatusCode: http.StatusSeeOther,
|
||||||
|
@ -63,8 +63,8 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
||||||
mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string {
|
mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string {
|
||||||
user.ConfirmedAt = time.Now()
|
user.ConfirmedAt = time.Now()
|
||||||
user.Email = user.UnconfirmedEmail
|
user.Email = user.UnconfirmedEmail
|
||||||
user.Approved = testrig.TrueBool()
|
user.Approved = util.Ptr(true)
|
||||||
user.Disabled = testrig.FalseBool()
|
user.Disabled = util.Ptr(false)
|
||||||
account.SuspendedAt = time.Now()
|
account.SuspendedAt = time.Now()
|
||||||
return []string{"confirmed_at", "email", "approved", "disabled"}
|
return []string{"confirmed_at", "email", "approved", "disabled"}
|
||||||
},
|
},
|
||||||
|
|
|
@ -87,7 +87,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateCreateAccount(form); err != nil {
|
if err := validateNormalizeCreateAccount(form); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -110,9 +110,10 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, ti)
|
c.JSON(http.StatusOK, ti)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateCreateAccount checks through all the necessary prerequisites for creating a new account,
|
// validateNormalizeCreateAccount checks through all the necessary prerequisites for creating a new account,
|
||||||
// according to the provided account create request. If the account isn't eligible, an error will be returned.
|
// according to the provided account create request. If the account isn't eligible, an error will be returned.
|
||||||
func validateCreateAccount(form *apimodel.AccountCreateRequest) error {
|
// Side effect: normalizes the provided language tag for the user's locale.
|
||||||
|
func validateNormalizeCreateAccount(form *apimodel.AccountCreateRequest) error {
|
||||||
if form == nil {
|
if form == nil {
|
||||||
return errors.New("form was nil")
|
return errors.New("form was nil")
|
||||||
}
|
}
|
||||||
|
@ -137,9 +138,11 @@ func validateCreateAccount(form *apimodel.AccountCreateRequest) error {
|
||||||
return errors.New("agreement to terms and conditions not given")
|
return errors.New("agreement to terms and conditions not given")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validate.Language(form.Locale); err != nil {
|
locale, err := validate.Language(form.Locale)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
form.Locale = locale
|
||||||
|
|
||||||
return validate.SignUpReason(form.Reason, config.GetAccountsReasonRequired())
|
return validate.SignUpReason(form.Reason, config.GetAccountsReasonRequired())
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ func (suite *ReportResolveTestSuite) TestReportResolve2() {
|
||||||
testToken := suite.testTokens["admin_account"]
|
testToken := suite.testTokens["admin_account"]
|
||||||
testUser := suite.testUsers["admin_account"]
|
testUser := suite.testUsers["admin_account"]
|
||||||
testReportID := suite.testReports["local_account_2_report_remote_account_1"].ID
|
testReportID := suite.testReports["local_account_2_report_remote_account_1"].ID
|
||||||
var actionTakenComment *string = testrig.StringPtr("no action was taken, this is a frivolous report you boob")
|
var actionTakenComment *string = util.Ptr("no action was taken, this is a frivolous report you boob")
|
||||||
|
|
||||||
report, err := suite.resolveReport(testAccount, testToken, testUser, testReportID, http.StatusOK, "", actionTakenComment)
|
report, err := suite.resolveReport(testAccount, testToken, testUser, testReportID, http.StatusOK, "", actionTakenComment)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -963,7 +964,7 @@ func (suite *ReportsGetTestSuite) TestReportsGetResolvedTargetAccount() {
|
||||||
testAccount := suite.testAccounts["admin_account"]
|
testAccount := suite.testAccounts["admin_account"]
|
||||||
testToken := suite.testTokens["admin_account"]
|
testToken := suite.testTokens["admin_account"]
|
||||||
testUser := suite.testUsers["admin_account"]
|
testUser := suite.testUsers["admin_account"]
|
||||||
resolved := testrig.FalseBool()
|
resolved := util.Ptr(false)
|
||||||
targetAccount := suite.testAccounts["local_account_2"]
|
targetAccount := suite.testAccounts["local_account_2"]
|
||||||
|
|
||||||
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, "", resolved, "", targetAccount.ID, "", "", "", 20)
|
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, "", resolved, "", targetAccount.ID, "", "", "", 20)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -231,7 +232,7 @@ func (suite *InstancePeersGetTestSuite) TestInstancePeersGetAllWithObfuscated()
|
||||||
Domain: "omg.just.the.worst.org.ever",
|
Domain: "omg.just.the.worst.org.ever",
|
||||||
CreatedByAccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
CreatedByAccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||||
PublicComment: "just absolutely the worst, wowza",
|
PublicComment: "just absolutely the worst, wowza",
|
||||||
Obfuscate: testrig.TrueBool(),
|
Obfuscate: util.Ptr(true),
|
||||||
})
|
})
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -197,7 +198,7 @@ func (suite *ReportsGetTestSuite) TestGetReports4() {
|
||||||
testAccount := suite.testAccounts["local_account_2"]
|
testAccount := suite.testAccounts["local_account_2"]
|
||||||
testToken := suite.testTokens["local_account_2"]
|
testToken := suite.testTokens["local_account_2"]
|
||||||
testUser := suite.testUsers["local_account_2"]
|
testUser := suite.testUsers["local_account_2"]
|
||||||
resolved := testrig.FalseBool()
|
resolved := util.Ptr(false)
|
||||||
|
|
||||||
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, resolved, "", "", "", "", 20)
|
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, resolved, "", "", "", "", 20)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
@ -252,7 +253,7 @@ func (suite *ReportsGetTestSuite) TestGetReports5() {
|
||||||
testAccount := suite.testAccounts["local_account_1"]
|
testAccount := suite.testAccounts["local_account_1"]
|
||||||
testToken := suite.testTokens["local_account_1"]
|
testToken := suite.testTokens["local_account_1"]
|
||||||
testUser := suite.testUsers["local_account_1"]
|
testUser := suite.testUsers["local_account_1"]
|
||||||
resolved := testrig.TrueBool()
|
resolved := util.Ptr(true)
|
||||||
|
|
||||||
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, resolved, "", "", "", "", 20)
|
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, resolved, "", "", "", "", 20)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
@ -323,7 +324,7 @@ func (suite *ReportsGetTestSuite) TestGetReports7() {
|
||||||
testAccount := suite.testAccounts["local_account_2"]
|
testAccount := suite.testAccounts["local_account_2"]
|
||||||
testToken := suite.testTokens["local_account_2"]
|
testToken := suite.testTokens["local_account_2"]
|
||||||
testUser := suite.testUsers["local_account_2"]
|
testUser := suite.testUsers["local_account_2"]
|
||||||
resolved := testrig.FalseBool()
|
resolved := util.Ptr(false)
|
||||||
|
|
||||||
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, resolved, "01F8MH5ZK5VRH73AKHQM6Y9VNX", "", "", "", 20)
|
reports, link, err := suite.getReports(testAccount, testToken, testUser, http.StatusOK, resolved, "01F8MH5ZK5VRH73AKHQM6Y9VNX", "", "", "", 20)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
||||||
// }
|
// }
|
||||||
// form.Status += "\n\nsent from " + user + "'s iphone\n"
|
// form.Status += "\n\nsent from " + user + "'s iphone\n"
|
||||||
|
|
||||||
if err := validateCreateStatus(form); err != nil {
|
if err := validateNormalizeCreateStatus(form); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,9 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, apiStatus)
|
c.JSON(http.StatusOK, apiStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCreateStatus(form *apimodel.AdvancedStatusCreateForm) error {
|
// validateNormalizeCreateStatus checks the form for disallowed combinations of attachments and overlength inputs.
|
||||||
|
// Side effect: normalizes the post's language tag.
|
||||||
|
func validateNormalizeCreateStatus(form *apimodel.AdvancedStatusCreateForm) error {
|
||||||
hasStatus := form.Status != ""
|
hasStatus := form.Status != ""
|
||||||
hasMedia := len(form.MediaIDs) != 0
|
hasMedia := len(form.MediaIDs) != 0
|
||||||
hasPoll := form.Poll != nil
|
hasPoll := form.Poll != nil
|
||||||
|
@ -162,9 +164,11 @@ func validateCreateStatus(form *apimodel.AdvancedStatusCreateForm) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Language != "" {
|
if form.Language != "" {
|
||||||
if err := validate.Language(form.Language); err != nil {
|
language, err := validate.Language(form.Language)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
form.Language = language
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -391,6 +391,42 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
|
||||||
suite.Equal(statusResponse.ID, gtsAttachment.StatusID)
|
suite.Equal(statusResponse.ID, gtsAttachment.StatusID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post a new status with a language tag that is not in canonical format
|
||||||
|
func (suite *StatusCreateTestSuite) TestPostNewStatusWithNoncanonicalLanguageTag() {
|
||||||
|
t := suite.testTokens["local_account_1"]
|
||||||
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
|
// setup
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||||
|
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||||
|
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||||
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
|
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||||
|
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
|
||||||
|
ctx.Request.Header.Set("accept", "application/json")
|
||||||
|
ctx.Request.Form = url.Values{
|
||||||
|
"status": {"English? what's English? i speak American"},
|
||||||
|
"language": {"en-us"},
|
||||||
|
}
|
||||||
|
suite.statusModule.StatusCreatePOSTHandler(ctx)
|
||||||
|
|
||||||
|
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||||
|
|
||||||
|
result := recorder.Result()
|
||||||
|
defer result.Body.Close()
|
||||||
|
b, err := ioutil.ReadAll(result.Body)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
statusReply := &apimodel.Status{}
|
||||||
|
err = json.Unmarshal(b, statusReply)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
suite.Equal("<p>English? what's English? i speak American</p>", statusReply.Content)
|
||||||
|
suite.NotNil(statusReply.Language)
|
||||||
|
suite.Equal("en-US", *statusReply.Language)
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatusCreateTestSuite(t *testing.T) {
|
func TestStatusCreateTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(StatusCreateTestSuite))
|
suite.Run(t, new(StatusCreateTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -165,14 +166,14 @@ func (suite *StatusPinTestSuite) TestPinStatusTooManyPins() {
|
||||||
PinnedAt: time.Now(),
|
PinnedAt: time.Now(),
|
||||||
URL: "stub " + strconv.Itoa(i),
|
URL: "stub " + strconv.Itoa(i),
|
||||||
URI: "stub " + strconv.Itoa(i),
|
URI: "stub " + strconv.Itoa(i),
|
||||||
Local: testrig.TrueBool(),
|
Local: util.Ptr(true),
|
||||||
AccountID: testAccount.ID,
|
AccountID: testAccount.ID,
|
||||||
AccountURI: testAccount.URI,
|
AccountURI: testAccount.URI,
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, status); err != nil {
|
if err := suite.db.PutStatus(ctx, status); err != nil {
|
||||||
|
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
@ -198,17 +199,15 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (
|
||||||
user.Email = newSignup.Email
|
user.Email = newSignup.Email
|
||||||
}
|
}
|
||||||
|
|
||||||
trueBool := func() *bool { t := true; return &t }
|
|
||||||
|
|
||||||
if newSignup.Admin {
|
if newSignup.Admin {
|
||||||
// Make new user mod + admin.
|
// Make new user mod + admin.
|
||||||
user.Moderator = trueBool()
|
user.Moderator = util.Ptr(true)
|
||||||
user.Admin = trueBool()
|
user.Admin = util.Ptr(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if newSignup.PreApproved {
|
if newSignup.PreApproved {
|
||||||
// Mark new user as approved.
|
// Mark new user as approved.
|
||||||
user.Approved = trueBool()
|
user.Approved = util.Ptr(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the user!
|
// Insert the user!
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InstanceTestSuite struct {
|
type InstanceTestSuite struct {
|
||||||
|
@ -103,7 +103,7 @@ func (suite *InstanceTestSuite) TestGetInstanceModeratorAddressesZorkAsModerator
|
||||||
// Promote zork to moderator role.
|
// Promote zork to moderator role.
|
||||||
testUser := >smodel.User{}
|
testUser := >smodel.User{}
|
||||||
*testUser = *suite.testUsers["local_account_1"]
|
*testUser = *suite.testUsers["local_account_1"]
|
||||||
testUser.Moderator = testrig.TrueBool()
|
testUser.Moderator = util.Ptr(true)
|
||||||
if err := suite.db.UpdateUser(context.Background(), testUser, "moderator"); err != nil {
|
if err := suite.db.UpdateUser(context.Background(), testUser, "moderator"); err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -117,8 +117,8 @@ func (suite *InstanceTestSuite) TestGetInstanceModeratorAddressesNoAdmin() {
|
||||||
// Demote admin from admin + moderator roles.
|
// Demote admin from admin + moderator roles.
|
||||||
testUser := >smodel.User{}
|
testUser := >smodel.User{}
|
||||||
*testUser = *suite.testUsers["admin_account"]
|
*testUser = *suite.testUsers["admin_account"]
|
||||||
testUser.Admin = testrig.FalseBool()
|
testUser.Admin = util.Ptr(false)
|
||||||
testUser.Moderator = testrig.FalseBool()
|
testUser.Moderator = util.Ptr(false)
|
||||||
if err := suite.db.UpdateUser(context.Background(), testUser, "admin", "moderator"); err != nil {
|
if err := suite.db.UpdateUser(context.Background(), testUser, "admin", "moderator"); err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (suite *NotificationTestSuite) spamNotifs() {
|
func (suite *NotificationTestSuite) spamNotifs() {
|
||||||
|
@ -70,7 +70,7 @@ func (suite *NotificationTestSuite) spamNotifs() {
|
||||||
TargetAccountID: targetAccountID,
|
TargetAccountID: targetAccountID,
|
||||||
OriginAccountID: originAccountID,
|
OriginAccountID: originAccountID,
|
||||||
StatusID: statusID,
|
StatusID: statusID,
|
||||||
Read: testrig.FalseBool(),
|
Read: util.Ptr(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := suite.db.Put(context.Background(), notif); err != nil {
|
if err := suite.db.Put(context.Background(), notif); err != nil {
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RelationshipTestSuite struct {
|
type RelationshipTestSuite struct {
|
||||||
|
@ -892,7 +892,7 @@ func (suite *RelationshipTestSuite) TestUpdateFollow() {
|
||||||
follow := >smodel.Follow{}
|
follow := >smodel.Follow{}
|
||||||
*follow = *suite.testFollows["local_account_1_admin_account"]
|
*follow = *suite.testFollows["local_account_1_admin_account"]
|
||||||
|
|
||||||
follow.Notify = testrig.TrueBool()
|
follow.Notify = util.Ptr(true)
|
||||||
if err := suite.db.UpdateFollow(ctx, follow, "notify"); err != nil {
|
if err := suite.db.UpdateFollow(ctx, follow, "notify"); err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ func (suite *ReportTestSuite) TestPutReport() {
|
||||||
TargetAccountID: "01F8MH5ZK5VRH73AKHQM6Y9VNX",
|
TargetAccountID: "01F8MH5ZK5VRH73AKHQM6Y9VNX",
|
||||||
Comment: "another report",
|
Comment: "another report",
|
||||||
StatusIDs: []string{"01FVW7JHQFSFK166WWKR8CBA6M"},
|
StatusIDs: []string{"01FVW7JHQFSFK166WWKR8CBA6M"},
|
||||||
Forwarded: testrig.TrueBool(),
|
Forwarded: util.Ptr(true),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := suite.db.PutReport(ctx, report)
|
err := suite.db.PutReport(ctx, report)
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TimelineTestSuite struct {
|
type TimelineTestSuite struct {
|
||||||
|
@ -52,20 +52,20 @@ func getFutureStatus() *gtsmodel.Status {
|
||||||
EmojiIDs: []string{},
|
EmojiIDs: []string{},
|
||||||
CreatedAt: theDistantFuture,
|
CreatedAt: theDistantFuture,
|
||||||
UpdatedAt: theDistantFuture,
|
UpdatedAt: theDistantFuture,
|
||||||
Local: testrig.TrueBool(),
|
Local: util.Ptr(true),
|
||||||
AccountURI: "http://localhost:8080/users/admin",
|
AccountURI: "http://localhost:8080/users/admin",
|
||||||
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
|
CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,9 +81,9 @@ func (suite *FederatingActorTestSuite) TestSendRemoteFollower() {
|
||||||
UpdatedAt: testrig.TimeMustParse("2022-06-02T12:22:21+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2022-06-02T12:22:21+02:00"),
|
||||||
AccountID: testRemoteAccount.ID,
|
AccountID: testRemoteAccount.ID,
|
||||||
TargetAccountID: testAccount.ID,
|
TargetAccountID: testAccount.ID,
|
||||||
ShowReblogs: testrig.TrueBool(),
|
ShowReblogs: util.Ptr(true),
|
||||||
URI: "http://fossbros-anonymous.io/users/foss_satan/follows/01G1TRWV4AYCDBX5HRWT2EVBCV",
|
URI: "http://fossbros-anonymous.io/users/foss_satan/follows/01G1TRWV4AYCDBX5HRWT2EVBCV",
|
||||||
Notify: testrig.FalseBool(),
|
Notify: util.Ptr(false),
|
||||||
})
|
})
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
|
|
|
@ -21,13 +21,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"codeberg.org/gruf/go-kv"
|
|
||||||
"codeberg.org/gruf/go-logger/v2/level"
|
"codeberg.org/gruf/go-logger/v2/level"
|
||||||
|
"github.com/superseriousbusiness/activity/pub"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
@ -47,14 +47,16 @@ import (
|
||||||
// Under certain conditions and network activities, Create may be called
|
// Under certain conditions and network activities, Create may be called
|
||||||
// multiple times for the same ActivityStreams object.
|
// multiple times for the same ActivityStreams object.
|
||||||
func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
||||||
if log.Level() >= level.DEBUG {
|
if log.Level() >= level.TRACE {
|
||||||
i, err := marshalItem(asType)
|
i, err := marshalItem(asType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
l := log.WithContext(ctx).
|
|
||||||
WithField("create", i)
|
log.
|
||||||
l.Trace("entering Create")
|
WithContext(ctx).
|
||||||
|
WithField("create", i).
|
||||||
|
Trace("entering Create")
|
||||||
}
|
}
|
||||||
|
|
||||||
receivingAccount, requestingAccount, internal := extractFromCtx(ctx)
|
receivingAccount, requestingAccount, internal := extractFromCtx(ctx)
|
||||||
|
@ -116,92 +118,125 @@ func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, rec
|
||||||
CREATE HANDLERS
|
CREATE HANDLERS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (f *federatingDB) activityCreate(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account) error {
|
// activityCreate handles asType Create by checking
|
||||||
|
// the Object entries of the Create and calling other
|
||||||
|
// handlers as appropriate.
|
||||||
|
func (f *federatingDB) activityCreate(
|
||||||
|
ctx context.Context,
|
||||||
|
asType vocab.Type,
|
||||||
|
receivingAccount *gtsmodel.Account,
|
||||||
|
requestingAccount *gtsmodel.Account,
|
||||||
|
) error {
|
||||||
create, ok := asType.(vocab.ActivityStreamsCreate)
|
create, ok := asType.(vocab.ActivityStreamsCreate)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("activityCreate: could not convert type to create")
|
return gtserror.Newf("could not convert asType %T to ActivityStreamsCreate", asType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create should have an object
|
// Create must have an Object.
|
||||||
object := create.GetActivityStreamsObject()
|
objectProp := create.GetActivityStreamsObject()
|
||||||
if object == nil {
|
if objectProp == nil {
|
||||||
return errors.New("Create had no Object")
|
return gtserror.New("create had no Object")
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := []string{}
|
// Iterate through the Object property and process FIRST provided statusable.
|
||||||
// iterate through the object(s) to see what we're meant to be creating
|
// todo: https://github.com/superseriousbusiness/gotosocial/issues/1905
|
||||||
for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() {
|
for iter := objectProp.Begin(); iter != objectProp.End(); iter = iter.Next() {
|
||||||
asObjectType := objectIter.GetType()
|
object := iter.GetType()
|
||||||
if asObjectType == nil {
|
if object == nil {
|
||||||
// currently we can't do anything with just a Create of something that's not an Object with a type
|
// Can't do Create with Object that's just a URI.
|
||||||
// TODO: process a Create with an Object that's just a URI or something
|
// Warn log this because it's an AP error.
|
||||||
errs = append(errs, "object of Create was not a Type")
|
log.Warn(ctx, "object entry was not a type: %[1]T%[1]+v", iter)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// we have a type -- what is it?
|
// Ensure given object type is a statusable.
|
||||||
asObjectTypeName := asObjectType.GetTypeName()
|
statusable, ok := object.(ap.Statusable)
|
||||||
switch asObjectTypeName {
|
if !ok {
|
||||||
case ap.ObjectNote:
|
// Can't (currently) Create anything other than a Statusable. ([1] is a format arg index)
|
||||||
// CREATE A NOTE
|
log.Debugf(ctx, "object entry type (currently) unsupported: %[1]T%[1]+v", object)
|
||||||
if err := f.createNote(ctx, objectIter.GetActivityStreamsNote(), receivingAccount, requestingAccount); err != nil {
|
continue
|
||||||
errs = append(errs, err.Error())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
errs = append(errs, fmt.Sprintf("received an object on a Create that we couldn't handle: %s", asObjectType.GetTypeName()))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs) != 0 {
|
// Handle creation of statusable.
|
||||||
return fmt.Errorf("activityCreate: one or more errors while processing activity: %s", strings.Join(errs, "; "))
|
return f.createStatusable(ctx,
|
||||||
|
statusable,
|
||||||
|
receivingAccount,
|
||||||
|
requestingAccount,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createNote handles a Create activity with a Note type.
|
// createStatusable handles a Create activity for a Statusable.
|
||||||
func (f *federatingDB) createNote(ctx context.Context, note vocab.ActivityStreamsNote, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account) error {
|
// This function won't insert anything in the database yet,
|
||||||
l := log.WithContext(ctx).
|
// but will pass the Statusable (if appropriate) through to
|
||||||
WithFields(kv.Fields{
|
// the processor for further asynchronous processing.
|
||||||
{"receivingAccount", receivingAccount.URI},
|
func (f *federatingDB) createStatusable(
|
||||||
{"requestingAccount", requestingAccount.URI},
|
ctx context.Context,
|
||||||
}...)
|
statusable ap.Statusable,
|
||||||
|
receivingAccount *gtsmodel.Account,
|
||||||
|
requestingAccount *gtsmodel.Account,
|
||||||
|
) error {
|
||||||
|
// Statusable must have an attributedTo.
|
||||||
|
attrToProp := statusable.GetActivityStreamsAttributedTo()
|
||||||
|
if attrToProp == nil {
|
||||||
|
return gtserror.Newf("statusable had no attributedTo")
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we have a forward.
|
// Statusable must have an ID.
|
||||||
// In other words, was the note posted to our inbox by at least one actor who actually created the note, or are they just forwarding it?
|
idProp := statusable.GetJSONLDId()
|
||||||
|
if idProp == nil || !idProp.IsIRI() {
|
||||||
|
return gtserror.Newf("statusable had no id, or id was not a URI")
|
||||||
|
}
|
||||||
|
|
||||||
|
statusableURI := idProp.GetIRI()
|
||||||
|
|
||||||
|
// Check if we have a forward. In other words, was the
|
||||||
|
// statusable posted to our inbox by at least one actor
|
||||||
|
// who actually created it, or are they forwarding it?
|
||||||
forward := true
|
forward := true
|
||||||
|
for iter := attrToProp.Begin(); iter != attrToProp.End(); iter = iter.Next() {
|
||||||
// note should have an attributedTo
|
actorURI, err := pub.ToId(iter)
|
||||||
noteAttributedTo := note.GetActivityStreamsAttributedTo()
|
if err != nil {
|
||||||
if noteAttributedTo == nil {
|
return gtserror.Newf("error extracting id from attributedTo entry: %w", err)
|
||||||
return errors.New("createNote: note had no attributedTo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// compare the attributedTo(s) with the actor who posted this to our inbox
|
|
||||||
for attributedToIter := noteAttributedTo.Begin(); attributedToIter != noteAttributedTo.End(); attributedToIter = attributedToIter.Next() {
|
|
||||||
if !attributedToIter.IsIRI() {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
iri := attributedToIter.GetIRI()
|
|
||||||
if requestingAccount.URI == iri.String() {
|
if requestingAccount.URI == actorURI.String() {
|
||||||
// at least one creator of the note, and the actor who posted the note to our inbox, are the same, so it's not a forward
|
// The actor who posted this statusable to our inbox is
|
||||||
|
// (one of) its creator(s), so this is not a forward.
|
||||||
forward = false
|
forward = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we do have a forward, we should ignore the content for now and just dereference based on the URL/ID of the note instead, to get the note straight from the horse's mouth
|
// Check if we already have a status entry
|
||||||
|
// for this statusable, based on the ID/URI.
|
||||||
|
statusableURIStr := statusableURI.String()
|
||||||
|
status, err := f.state.DB.GetStatusByURI(ctx, statusableURIStr)
|
||||||
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
|
return gtserror.Newf("db error checking existence of status %s: %w", statusableURIStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status != nil {
|
||||||
|
// We already had this status in the db, no need for further action.
|
||||||
|
log.Trace(ctx, "status already exists: %s", statusableURIStr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we do have a forward, we should ignore the content
|
||||||
|
// and instead deref based on the URI of the statusable.
|
||||||
|
//
|
||||||
|
// In other words, don't automatically trust whoever sent
|
||||||
|
// this status to us, but fetch the authentic article from
|
||||||
|
// the server it originated from.
|
||||||
if forward {
|
if forward {
|
||||||
l.Trace("note is a forward")
|
// Pass the statusable URI (APIri) into the processor worker
|
||||||
id := note.GetJSONLDId()
|
// and do the rest of the processing asynchronously.
|
||||||
if !id.IsIRI() {
|
|
||||||
// if the note id isn't an IRI, there's nothing we can do here
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// pass the note iri into the processor and have it do the dereferencing instead of doing it here
|
|
||||||
f.state.Workers.EnqueueFederator(ctx, messages.FromFederator{
|
f.state.Workers.EnqueueFederator(ctx, messages.FromFederator{
|
||||||
APObjectType: ap.ObjectNote,
|
APObjectType: ap.ObjectNote,
|
||||||
APActivityType: ap.ActivityCreate,
|
APActivityType: ap.ActivityCreate,
|
||||||
APIri: id.GetIRI(),
|
APIri: statusableURI,
|
||||||
APObjectModel: nil,
|
APObjectModel: nil,
|
||||||
GTSModel: nil,
|
GTSModel: nil,
|
||||||
ReceivingAccount: receivingAccount,
|
ReceivingAccount: receivingAccount,
|
||||||
|
@ -209,34 +244,58 @@ func (f *federatingDB) createNote(ctx context.Context, note vocab.ActivityStream
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we reach this point, we know it's not a forwarded status, so proceed with processing it as normal
|
// This is a non-forwarded status we can trust the requester on,
|
||||||
|
// convert this provided statusable data to a useable gtsmodel status.
|
||||||
status, err := f.typeConverter.ASStatusToStatus(ctx, note)
|
status, err = f.typeConverter.ASStatusToStatus(ctx, statusable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("createNote: error converting note to status: %s", err)
|
return gtserror.Newf("error converting statusable to status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// id the status based on the time it was created
|
// Check whether we should accept this new status.
|
||||||
statusID, err := id.NewULIDFromTime(status.CreatedAt)
|
accept, err := f.shouldAcceptStatusable(ctx,
|
||||||
|
receivingAccount,
|
||||||
|
requestingAccount,
|
||||||
|
status,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return gtserror.Newf("error checking status acceptibility: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !accept {
|
||||||
|
// This is a status sent with no relation to receiver, i.e.
|
||||||
|
// - receiving account does not follow requesting account
|
||||||
|
// - received status does not mention receiving account
|
||||||
|
//
|
||||||
|
// We just pretend that all is fine (dog with cuppa, flames everywhere)
|
||||||
|
log.Trace(ctx, "status failed acceptability check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID the new status based on the time it was created.
|
||||||
|
status.ID, err = id.NewULIDFromTime(status.CreatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
status.ID = statusID
|
|
||||||
|
|
||||||
|
// Put this newly parsed status in the database.
|
||||||
if err := f.state.DB.PutStatus(ctx, status); err != nil {
|
if err := f.state.DB.PutStatus(ctx, status); err != nil {
|
||||||
if errors.Is(err, db.ErrAlreadyExists) {
|
if errors.Is(err, db.ErrAlreadyExists) {
|
||||||
// the status already exists in the database, which means we've already handled everything else,
|
// The status already exists in the database, which
|
||||||
// so we can just return nil here and be done with it.
|
// means we've already processed it and some race
|
||||||
|
// condition means we didn't catch it yet. We can
|
||||||
|
// just return nil here and be done with it.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// an actual error has happened
|
return gtserror.Newf("db error inserting status: %w", err)
|
||||||
return fmt.Errorf("createNote: database error inserting status: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do the rest of the processing asynchronously. The processor
|
||||||
|
// will handle inserting/updating + further dereferencing the status.
|
||||||
f.state.Workers.EnqueueFederator(ctx, messages.FromFederator{
|
f.state.Workers.EnqueueFederator(ctx, messages.FromFederator{
|
||||||
APObjectType: ap.ObjectNote,
|
APObjectType: ap.ObjectNote,
|
||||||
APActivityType: ap.ActivityCreate,
|
APActivityType: ap.ActivityCreate,
|
||||||
APObjectModel: note,
|
APIri: nil,
|
||||||
|
APObjectModel: statusable,
|
||||||
GTSModel: status,
|
GTSModel: status,
|
||||||
ReceivingAccount: receivingAccount,
|
ReceivingAccount: receivingAccount,
|
||||||
})
|
})
|
||||||
|
@ -244,6 +303,26 @@ func (f *federatingDB) createNote(ctx context.Context, note vocab.ActivityStream
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *federatingDB) shouldAcceptStatusable(ctx context.Context, receiver *gtsmodel.Account, requester *gtsmodel.Account, status *gtsmodel.Status) (bool, error) {
|
||||||
|
// Check whether status mentions the receiver,
|
||||||
|
// this is the quickest check so perform it first.
|
||||||
|
for _, mention := range status.Mentions {
|
||||||
|
if mention.TargetAccountURI == receiver.URI {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether receiving account follows the requesting account.
|
||||||
|
follows, err := f.state.DB.IsFollowing(ctx, receiver.ID, requester.ID)
|
||||||
|
if err != nil {
|
||||||
|
return false, gtserror.Newf("error checking follow status: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status will only be acceptable
|
||||||
|
// if receiver follows requester.
|
||||||
|
return follows, nil
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FOLLOW HANDLERS
|
FOLLOW HANDLERS
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -54,7 +54,7 @@ func (suite *CreateTestSuite) TestCreateNote() {
|
||||||
|
|
||||||
// status should have some expected values
|
// status should have some expected values
|
||||||
suite.Equal(requestingAccount.ID, status.AccountID)
|
suite.Equal(requestingAccount.ID, status.AccountID)
|
||||||
suite.Equal("hey zork here's a new private note for you", status.Content)
|
suite.Equal("@the_mighty_zork@localhost:8080 hey zork here's a new private note for you", status.Content)
|
||||||
|
|
||||||
// status should be in the database
|
// status should be in the database
|
||||||
_, err = suite.db.GetStatusByID(context.Background(), status.ID)
|
_, err = suite.db.GetStatusByID(context.Background(), status.ID)
|
||||||
|
|
|
@ -247,8 +247,8 @@ func (c *Client) DoSigned(r *http.Request, sign SignFunc) (rsp *http.Response, e
|
||||||
|
|
||||||
// Rewind body reader and content-length if set.
|
// Rewind body reader and content-length if set.
|
||||||
if rc, ok := r.Body.(*byteutil.ReadNopCloser); ok {
|
if rc, ok := r.Body.(*byteutil.ReadNopCloser); ok {
|
||||||
|
rc.Rewind() // set len AFTER rewind
|
||||||
r.ContentLength = int64(rc.Len())
|
r.ContentLength = int64(rc.Len())
|
||||||
rc.Rewind()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign the outgoing request.
|
// Sign the outgoing request.
|
||||||
|
|
|
@ -34,8 +34,7 @@ import (
|
||||||
// Create processes the given form for creating a new account,
|
// Create processes the given form for creating a new account,
|
||||||
// returning an oauth token for that account if successful.
|
// returning an oauth token for that account if successful.
|
||||||
//
|
//
|
||||||
// Fields on the form should have already been validated by the
|
// Precondition: the form's fields should have already been validated and normalized by the caller.
|
||||||
// caller, before this function is called.
|
|
||||||
func (p *Processor) Create(
|
func (p *Processor) Create(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
appToken oauth2.TokenInfo,
|
appToken oauth2.TokenInfo,
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -427,10 +428,8 @@ func (p *Processor) deleteAccountPeripheral(ctx context.Context, account *gtsmod
|
||||||
// names of all columns that are updated by it.
|
// names of all columns that are updated by it.
|
||||||
func stubbifyAccount(account *gtsmodel.Account, origin string) []string {
|
func stubbifyAccount(account *gtsmodel.Account, origin string) []string {
|
||||||
var (
|
var (
|
||||||
falseBool = func() *bool { b := false; return &b }
|
now = time.Now()
|
||||||
trueBool = func() *bool { b := true; return &b }
|
never = time.Time{}
|
||||||
now = time.Now()
|
|
||||||
never = time.Time{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
account.FetchedAt = never
|
account.FetchedAt = never
|
||||||
|
@ -444,17 +443,17 @@ func stubbifyAccount(account *gtsmodel.Account, origin string) []string {
|
||||||
account.Fields = nil
|
account.Fields = nil
|
||||||
account.Note = ""
|
account.Note = ""
|
||||||
account.NoteRaw = ""
|
account.NoteRaw = ""
|
||||||
account.Memorial = falseBool()
|
account.Memorial = util.Ptr(false)
|
||||||
account.AlsoKnownAs = ""
|
account.AlsoKnownAs = ""
|
||||||
account.MovedToAccountID = ""
|
account.MovedToAccountID = ""
|
||||||
account.Reason = ""
|
account.Reason = ""
|
||||||
account.Discoverable = falseBool()
|
account.Discoverable = util.Ptr(false)
|
||||||
account.StatusContentType = ""
|
account.StatusContentType = ""
|
||||||
account.CustomCSS = ""
|
account.CustomCSS = ""
|
||||||
account.SuspendedAt = now
|
account.SuspendedAt = now
|
||||||
account.SuspensionOrigin = origin
|
account.SuspensionOrigin = origin
|
||||||
account.HideCollections = trueBool()
|
account.HideCollections = util.Ptr(true)
|
||||||
account.EnableRSS = falseBool()
|
account.EnableRSS = util.Ptr(false)
|
||||||
|
|
||||||
return []string{
|
return []string{
|
||||||
"fetched_at",
|
"fetched_at",
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FollowTestSuite struct {
|
type FollowTestSuite struct {
|
||||||
|
@ -40,10 +40,9 @@ func (suite *FollowTestSuite) TestUpdateExistingFollowChangeBoth() {
|
||||||
// UPDATE "follows" AS "follow" SET "show_reblogs" = FALSE, "notify" = TRUE, "updated_at" = '2023-04-09 11:42:39.424705+00:00' WHERE ("follow"."id" = '01F8PY8RHWRQZV038T4E8T9YK8')
|
// UPDATE "follows" AS "follow" SET "show_reblogs" = FALSE, "notify" = TRUE, "updated_at" = '2023-04-09 11:42:39.424705+00:00' WHERE ("follow"."id" = '01F8PY8RHWRQZV038T4E8T9YK8')
|
||||||
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
||||||
ID: targetAccount.ID,
|
ID: targetAccount.ID,
|
||||||
Reblogs: testrig.FalseBool(),
|
Reblogs: util.Ptr(false),
|
||||||
Notify: testrig.TrueBool(),
|
Notify: util.Ptr(true),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -62,9 +61,8 @@ func (suite *FollowTestSuite) TestUpdateExistingFollowChangeNotifyIgnoreReblogs(
|
||||||
// UPDATE "follows" AS "follow" SET "notify" = TRUE, "updated_at" = '2023-04-09 11:40:33.827858+00:00' WHERE ("follow"."id" = '01F8PY8RHWRQZV038T4E8T9YK8')
|
// UPDATE "follows" AS "follow" SET "notify" = TRUE, "updated_at" = '2023-04-09 11:40:33.827858+00:00' WHERE ("follow"."id" = '01F8PY8RHWRQZV038T4E8T9YK8')
|
||||||
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
||||||
ID: targetAccount.ID,
|
ID: targetAccount.ID,
|
||||||
Notify: testrig.TrueBool(),
|
Notify: util.Ptr(true),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -83,10 +81,9 @@ func (suite *FollowTestSuite) TestUpdateExistingFollowChangeNotifySetReblogs() {
|
||||||
// UPDATE "follows" AS "follow" SET "notify" = TRUE, "updated_at" = '2023-04-09 11:40:33.827858+00:00' WHERE ("follow"."id" = '01F8PY8RHWRQZV038T4E8T9YK8')
|
// UPDATE "follows" AS "follow" SET "notify" = TRUE, "updated_at" = '2023-04-09 11:40:33.827858+00:00' WHERE ("follow"."id" = '01F8PY8RHWRQZV038T4E8T9YK8')
|
||||||
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
||||||
ID: targetAccount.ID,
|
ID: targetAccount.ID,
|
||||||
Notify: testrig.TrueBool(),
|
Notify: util.Ptr(true),
|
||||||
Reblogs: testrig.TrueBool(),
|
Reblogs: util.Ptr(true),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -104,10 +101,9 @@ func (suite *FollowTestSuite) TestUpdateExistingFollowChangeNothing() {
|
||||||
// Trace logs should show no update query.
|
// Trace logs should show no update query.
|
||||||
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
||||||
ID: targetAccount.ID,
|
ID: targetAccount.ID,
|
||||||
Notify: testrig.FalseBool(),
|
Notify: util.Ptr(false),
|
||||||
Reblogs: testrig.TrueBool(),
|
Reblogs: util.Ptr(true),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -126,7 +122,6 @@ func (suite *FollowTestSuite) TestUpdateExistingFollowSetNothing() {
|
||||||
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
relationship, err := suite.accountProcessor.FollowCreate(ctx, requestingAccount, &apimodel.AccountFollowRequest{
|
||||||
ID: targetAccount.ID,
|
ID: targetAccount.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,10 +222,11 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
||||||
|
|
||||||
if form.Source != nil {
|
if form.Source != nil {
|
||||||
if form.Source.Language != nil {
|
if form.Source.Language != nil {
|
||||||
if err := validate.Language(*form.Source.Language); err != nil {
|
language, err := validate.Language(*form.Source.Language)
|
||||||
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorBadRequest(err)
|
return nil, gtserror.NewErrorBadRequest(err)
|
||||||
}
|
}
|
||||||
account.Language = *form.Source.Language
|
account.Language = language
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Source.Sensitive != nil {
|
if form.Source.Sensitive != nil {
|
||||||
|
|
|
@ -121,6 +121,15 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
||||||
|
|
||||||
func data(requestedPerson vocab.ActivityStreamsPerson) (interface{}, gtserror.WithCode) {
|
func data(requestedPerson vocab.ActivityStreamsPerson) (interface{}, gtserror.WithCode) {
|
||||||
data, err := ap.Serialize(requestedPerson)
|
data, err := ap.Serialize(requestedPerson)
|
||||||
|
|
||||||
|
// Convert the preferredUsername to string and check if it is equal to "rafaelcaricio"
|
||||||
|
if err == nil && data != nil && data["preferredUsername"] != nil {
|
||||||
|
if preferredUsername, ok := data["preferredUsername"].(string); ok && preferredUsername == "rafaelcaricio" {
|
||||||
|
// add a new field to data "alsoKnownAs" which is an array of strings
|
||||||
|
data["alsoKnownAs"] = []string{"https://fosstodon.org/users/rafaelcaricio"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := gtserror.Newf("error serializing person: %w", err)
|
err := gtserror.Newf("error serializing person: %w", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/stream"
|
"github.com/superseriousbusiness/gotosocial/internal/stream"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,20 +64,20 @@ func (suite *FromClientAPITestSuite) TestProcessStreamNewStatus() {
|
||||||
EmojiIDs: []string{},
|
EmojiIDs: []string{},
|
||||||
CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
UpdatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||||
Local: testrig.TrueBool(),
|
Local: util.Ptr(true),
|
||||||
AccountURI: "http://localhost:8080/users/admin",
|
AccountURI: "http://localhost:8080/users/admin",
|
||||||
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityFollowersOnly,
|
Visibility: gtsmodel.VisibilityFollowersOnly,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
|
CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
|
||||||
Federated: testrig.FalseBool(),
|
Federated: util.Ptr(false),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +190,7 @@ func (suite *FromClientAPITestSuite) TestProcessNewStatusWithNotification() {
|
||||||
// that receiving account wants notifs when posting account posts.
|
// that receiving account wants notifs when posting account posts.
|
||||||
follow := >smodel.Follow{}
|
follow := >smodel.Follow{}
|
||||||
*follow = *suite.testFollows["local_account_1_admin_account"]
|
*follow = *suite.testFollows["local_account_1_admin_account"]
|
||||||
follow.Notify = testrig.TrueBool()
|
follow.Notify = util.Ptr(true)
|
||||||
if err := suite.db.UpdateFollow(ctx, follow); err != nil {
|
if err := suite.db.UpdateFollow(ctx, follow); err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -206,20 +207,20 @@ func (suite *FromClientAPITestSuite) TestProcessNewStatusWithNotification() {
|
||||||
EmojiIDs: []string{},
|
EmojiIDs: []string{},
|
||||||
CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
UpdatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||||
Local: testrig.TrueBool(),
|
Local: util.Ptr(true),
|
||||||
AccountURI: "http://localhost:8080/users/admin",
|
AccountURI: "http://localhost:8080/users/admin",
|
||||||
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
AccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityFollowersOnly,
|
Visibility: gtsmodel.VisibilityFollowersOnly,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
|
CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F",
|
||||||
Federated: testrig.FalseBool(),
|
Federated: util.Ptr(false),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,20 +108,23 @@ func (p *Processor) ProcessFromFederator(ctx context.Context, federatorMsg messa
|
||||||
|
|
||||||
// processCreateStatusFromFederator handles Activity Create and Object Note.
|
// processCreateStatusFromFederator handles Activity Create and Object Note.
|
||||||
func (p *Processor) processCreateStatusFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
|
func (p *Processor) processCreateStatusFromFederator(ctx context.Context, federatorMsg messages.FromFederator) error {
|
||||||
// Check the federatorMsg for either an already
|
|
||||||
// dereferenced and converted status pinned to
|
|
||||||
// the message, or an AP IRI that we need to deref.
|
|
||||||
var (
|
var (
|
||||||
status *gtsmodel.Status
|
status *gtsmodel.Status
|
||||||
err error
|
err error
|
||||||
|
|
||||||
|
// Check the federatorMsg for either an already dereferenced
|
||||||
|
// and converted status pinned to the message, or a forwarded
|
||||||
|
// AP IRI that we still need to deref.
|
||||||
|
forwarded = (federatorMsg.GTSModel == nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
if federatorMsg.GTSModel != nil {
|
if forwarded {
|
||||||
// Model is set, use that.
|
// Model was not set, deref with IRI.
|
||||||
status, err = p.statusFromGTSModel(ctx, federatorMsg)
|
// This will also cause the status to be inserted into the db.
|
||||||
} else {
|
|
||||||
// Model is not set, use IRI.
|
|
||||||
status, err = p.statusFromAPIRI(ctx, federatorMsg)
|
status, err = p.statusFromAPIRI(ctx, federatorMsg)
|
||||||
|
} else {
|
||||||
|
// Model is set, ensure we have the most up-to-date model.
|
||||||
|
status, err = p.statusFromGTSModel(ctx, federatorMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/stream"
|
"github.com/superseriousbusiness/gotosocial/internal/stream"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,10 +111,10 @@ func (suite *FromFederatorTestSuite) TestProcessReplyMention() {
|
||||||
InReplyToAccountID: repliedAccount.ID,
|
InReplyToAccountID: repliedAccount.ID,
|
||||||
Visibility: gtsmodel.VisibilityUnlocked,
|
Visibility: gtsmodel.VisibilityUnlocked,
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.FalseBool(),
|
Likeable: util.Ptr(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
wssStream, errWithCode := suite.processor.Stream().Open(context.Background(), repliedAccount, stream.TimelineHome)
|
wssStream, errWithCode := suite.processor.Stream().Open(context.Background(), repliedAccount, stream.TimelineHome)
|
||||||
|
@ -317,9 +318,9 @@ func (suite *FromFederatorTestSuite) TestProcessAccountDelete() {
|
||||||
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
||||||
AccountID: deletedAccount.ID,
|
AccountID: deletedAccount.ID,
|
||||||
TargetAccountID: receivingAccount.ID,
|
TargetAccountID: receivingAccount.ID,
|
||||||
ShowReblogs: testrig.TrueBool(),
|
ShowReblogs: util.Ptr(true),
|
||||||
URI: fmt.Sprintf("%s/follows/01FGRY72ASHBSET64353DPHK9T", deletedAccount.URI),
|
URI: fmt.Sprintf("%s/follows/01FGRY72ASHBSET64353DPHK9T", deletedAccount.URI),
|
||||||
Notify: testrig.FalseBool(),
|
Notify: util.Ptr(false),
|
||||||
}
|
}
|
||||||
err := suite.db.Put(ctx, zorkFollowSatan)
|
err := suite.db.Put(ctx, zorkFollowSatan)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
@ -330,9 +331,9 @@ func (suite *FromFederatorTestSuite) TestProcessAccountDelete() {
|
||||||
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
||||||
AccountID: receivingAccount.ID,
|
AccountID: receivingAccount.ID,
|
||||||
TargetAccountID: deletedAccount.ID,
|
TargetAccountID: deletedAccount.ID,
|
||||||
ShowReblogs: testrig.TrueBool(),
|
ShowReblogs: util.Ptr(true),
|
||||||
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", receivingAccount.URI),
|
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", receivingAccount.URI),
|
||||||
Notify: testrig.FalseBool(),
|
Notify: util.Ptr(false),
|
||||||
}
|
}
|
||||||
err = suite.db.Put(ctx, satanFollowZork)
|
err = suite.db.Put(ctx, satanFollowZork)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
@ -405,9 +406,9 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestLocked() {
|
||||||
Account: originAccount,
|
Account: originAccount,
|
||||||
TargetAccountID: targetAccount.ID,
|
TargetAccountID: targetAccount.ID,
|
||||||
TargetAccount: targetAccount,
|
TargetAccount: targetAccount,
|
||||||
ShowReblogs: testrig.TrueBool(),
|
ShowReblogs: util.Ptr(true),
|
||||||
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI),
|
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI),
|
||||||
Notify: testrig.FalseBool(),
|
Notify: util.Ptr(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := suite.db.Put(ctx, satanFollowRequestTurtle)
|
err := suite.db.Put(ctx, satanFollowRequestTurtle)
|
||||||
|
@ -462,9 +463,9 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestUnlocked() {
|
||||||
Account: originAccount,
|
Account: originAccount,
|
||||||
TargetAccountID: targetAccount.ID,
|
TargetAccountID: targetAccount.ID,
|
||||||
TargetAccount: targetAccount,
|
TargetAccount: targetAccount,
|
||||||
ShowReblogs: testrig.TrueBool(),
|
ShowReblogs: util.Ptr(true),
|
||||||
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI),
|
URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI),
|
||||||
Notify: testrig.FalseBool(),
|
Notify: util.Ptr(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := suite.db.Put(ctx, satanFollowRequestTurtle)
|
err := suite.db.Put(ctx, satanFollowRequestTurtle)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncached() {
|
||||||
|
|
||||||
// uncache the file from local
|
// uncache the file from local
|
||||||
testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"]
|
testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"]
|
||||||
testAttachment.Cached = testrig.FalseBool()
|
testAttachment.Cached = util.Ptr(false)
|
||||||
err := suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached")
|
err := suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached")
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
err = suite.storage.Delete(ctx, testAttachment.File.Path)
|
err = suite.storage.Delete(ctx, testAttachment.File.Path)
|
||||||
|
@ -120,7 +121,7 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncachedInterrupted() {
|
||||||
|
|
||||||
// uncache the file from local
|
// uncache the file from local
|
||||||
testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"]
|
testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"]
|
||||||
testAttachment.Cached = testrig.FalseBool()
|
testAttachment.Cached = util.Ptr(false)
|
||||||
err := suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached")
|
err := suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached")
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
err = suite.storage.Delete(ctx, testAttachment.File.Path)
|
err = suite.storage.Delete(ctx, testAttachment.File.Path)
|
||||||
|
@ -177,7 +178,7 @@ func (suite *GetFileTestSuite) TestGetRemoteFileThumbnailUncached() {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// uncache the file from local
|
// uncache the file from local
|
||||||
testAttachment.Cached = testrig.FalseBool()
|
testAttachment.Cached = util.Ptr(false)
|
||||||
err = suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached")
|
err = suite.db.UpdateByID(ctx, testAttachment, testAttachment.ID, "cached")
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
err = suite.storage.Delete(ctx, testAttachment.File.Path)
|
err = suite.storage.Delete(ctx, testAttachment.File.Path)
|
||||||
|
|
|
@ -37,6 +37,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create processes the given form to create a new status, returning the api model representation of that status if it's OK.
|
// Create processes the given form to create a new status, returning the api model representation of that status if it's OK.
|
||||||
|
//
|
||||||
|
// Precondition: the form's fields should have already been validated and normalized by the caller.
|
||||||
func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
|
func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
|
||||||
accountURIs := uris.GenerateURIsForAccount(account.Username)
|
accountURIs := uris.GenerateURIsForAccount(account.Username)
|
||||||
thisStatusID := id.NewULID()
|
thisStatusID := id.NewULID()
|
||||||
|
@ -55,7 +57,6 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, appli
|
||||||
ContentWarning: text.SanitizePlaintext(form.SpoilerText),
|
ContentWarning: text.SanitizePlaintext(form.SpoilerText),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
Sensitive: &sensitive,
|
Sensitive: &sensitive,
|
||||||
Language: form.Language,
|
|
||||||
CreatedWithApplicationID: application.ID,
|
CreatedWithApplicationID: application.ID,
|
||||||
Text: form.Status,
|
Text: form.Status,
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,6 +208,40 @@ func (suite *StatusCreateTestSuite) TestProcessMediaDescriptionTooShort() {
|
||||||
suite.Nil(apiStatus)
|
suite.Nil(apiStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *StatusCreateTestSuite) TestProcessLanguageWithScriptPart() {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
creatingAccount := suite.testAccounts["local_account_1"]
|
||||||
|
creatingApplication := suite.testApplications["application_1"]
|
||||||
|
|
||||||
|
statusCreateForm := &apimodel.AdvancedStatusCreateForm{
|
||||||
|
StatusCreateRequest: apimodel.StatusCreateRequest{
|
||||||
|
Status: "你好世界", // hello world
|
||||||
|
MediaIDs: []string{},
|
||||||
|
Poll: nil,
|
||||||
|
InReplyToID: "",
|
||||||
|
Sensitive: false,
|
||||||
|
SpoilerText: "",
|
||||||
|
Visibility: apimodel.VisibilityPublic,
|
||||||
|
ScheduledAt: "",
|
||||||
|
Language: "zh-Hans",
|
||||||
|
ContentType: apimodel.StatusContentTypePlain,
|
||||||
|
},
|
||||||
|
AdvancedVisibilityFlagsForm: apimodel.AdvancedVisibilityFlagsForm{
|
||||||
|
Federated: nil,
|
||||||
|
Boostable: nil,
|
||||||
|
Replyable: nil,
|
||||||
|
Likeable: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
apiStatus, err := suite.status.Create(ctx, creatingAccount, creatingApplication, statusCreateForm)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.NotNil(apiStatus)
|
||||||
|
|
||||||
|
suite.Equal("zh-Hans", *apiStatus.Language)
|
||||||
|
}
|
||||||
|
|
||||||
func TestStatusCreateTestSuite(t *testing.T) {
|
func TestStatusCreateTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(StatusCreateTestSuite))
|
suite.Run(t, new(StatusCreateTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
@ -31,6 +29,9 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *converter) ASRepresentationToAccount(ctx context.Context, accountable ap.Accountable, accountDomain string) (*gtsmodel.Account, error) {
|
func (c *converter) ASRepresentationToAccount(ctx context.Context, accountable ap.Accountable, accountDomain string) (*gtsmodel.Account, error) {
|
||||||
|
@ -282,7 +283,7 @@ func (c *converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
|
||||||
//
|
//
|
||||||
// Hashtags for later dereferencing.
|
// Hashtags for later dereferencing.
|
||||||
if hashtags, err := ap.ExtractHashtags(statusable); err != nil {
|
if hashtags, err := ap.ExtractHashtags(statusable); err != nil {
|
||||||
l.Infof("error extracting hashtags: %q", err)
|
l.Warnf("error extracting hashtags: %v", err)
|
||||||
} else {
|
} else {
|
||||||
status.Tags = hashtags
|
status.Tags = hashtags
|
||||||
}
|
}
|
||||||
|
@ -291,7 +292,7 @@ func (c *converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
|
||||||
//
|
//
|
||||||
// Custom emojis for later dereferencing.
|
// Custom emojis for later dereferencing.
|
||||||
if emojis, err := ap.ExtractEmojis(statusable); err != nil {
|
if emojis, err := ap.ExtractEmojis(statusable); err != nil {
|
||||||
l.Infof("error extracting emojis: %q", err)
|
l.Warnf("error extracting emojis: %v", err)
|
||||||
} else {
|
} else {
|
||||||
status.Emojis = emojis
|
status.Emojis = emojis
|
||||||
}
|
}
|
||||||
|
@ -300,7 +301,7 @@ func (c *converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
|
||||||
//
|
//
|
||||||
// Mentions of other accounts for later dereferencing.
|
// Mentions of other accounts for later dereferencing.
|
||||||
if mentions, err := ap.ExtractMentions(statusable); err != nil {
|
if mentions, err := ap.ExtractMentions(statusable); err != nil {
|
||||||
l.Infof("error extracting mentions: %q", err)
|
l.Warnf("error extracting mentions: %v", err)
|
||||||
} else {
|
} else {
|
||||||
status.Mentions = mentions
|
status.Mentions = mentions
|
||||||
}
|
}
|
||||||
|
@ -321,7 +322,7 @@ func (c *converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
|
||||||
// db defaults, will fall back to now if not set.
|
// db defaults, will fall back to now if not set.
|
||||||
published, err := ap.ExtractPublished(statusable)
|
published, err := ap.ExtractPublished(statusable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Infof("error extracting published: %q", err)
|
l.Warnf("error extracting published: %v", err)
|
||||||
} else {
|
} else {
|
||||||
status.CreatedAt = published
|
status.CreatedAt = published
|
||||||
status.UpdatedAt = published
|
status.UpdatedAt = published
|
||||||
|
@ -396,11 +397,10 @@ func (c *converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab
|
||||||
// TODO: a lot of work to be done here -- a new type
|
// TODO: a lot of work to be done here -- a new type
|
||||||
// needs to be created for this in go-fed/activity.
|
// needs to be created for this in go-fed/activity.
|
||||||
// Until this is implemented, assume all true.
|
// Until this is implemented, assume all true.
|
||||||
var trueBool = func() *bool { b := true; return &b }
|
status.Federated = util.Ptr(true)
|
||||||
status.Federated = trueBool()
|
status.Boostable = util.Ptr(true)
|
||||||
status.Boostable = trueBool()
|
status.Replyable = util.Ptr(true)
|
||||||
status.Replyable = trueBool()
|
status.Likeable = util.Ptr(true)
|
||||||
status.Likeable = trueBool()
|
|
||||||
|
|
||||||
// status.Sensitive
|
// status.Sensitive
|
||||||
status.Sensitive = func() *bool {
|
status.Sensitive = func() *bool {
|
||||||
|
@ -630,8 +630,8 @@ func (c *converter) ASAnnounceToStatus(ctx context.Context, announceable ap.Anno
|
||||||
// Extract published time for the boost.
|
// Extract published time for the boost.
|
||||||
published, err := ap.ExtractPublished(announceable)
|
published, err := ap.ExtractPublished(announceable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = gtserror.Newf("error extracting published: %w", err)
|
//err = gtserror.Newf("error extracting published: %w", err)
|
||||||
return nil, isNew, err
|
published = time.Now().UTC()
|
||||||
}
|
}
|
||||||
status.CreatedAt = published
|
status.CreatedAt = published
|
||||||
status.UpdatedAt = published
|
status.UpdatedAt = published
|
||||||
|
|
23
internal/util/ptr.go
Normal file
23
internal/util/ptr.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// GoToSocial
|
||||||
|
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
// Ptr returns a pointer to the passed in type
|
||||||
|
func Ptr[T any](t T) *T {
|
||||||
|
return &t
|
||||||
|
}
|
|
@ -99,14 +99,19 @@ func Email(email string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Language checks that the given language string is a 2- or 3-letter ISO 639 code.
|
// Language checks that the given language string is a valid, if not necessarily canonical, BCP 47 language tag.
|
||||||
// Returns an error if the language cannot be parsed. See: https://pkg.go.dev/golang.org/x/text/language
|
// Returns a canonicalized version of the tag if the language can be parsed.
|
||||||
func Language(lang string) error {
|
// Returns an error if the language cannot be parsed.
|
||||||
|
// See: https://pkg.go.dev/golang.org/x/text/language
|
||||||
|
func Language(lang string) (string, error) {
|
||||||
if lang == "" {
|
if lang == "" {
|
||||||
return errors.New("no language provided")
|
return "", errors.New("no language provided")
|
||||||
}
|
}
|
||||||
_, err := language.ParseBase(lang)
|
parsed, err := language.Parse(lang)
|
||||||
return err
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return parsed.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignUpReason checks that a sufficient reason is given for a server signup request
|
// SignUpReason checks that a sufficient reason is given for a server signup request
|
||||||
|
|
|
@ -159,60 +159,39 @@ func (suite *ValidationTestSuite) TestValidateEmail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ValidationTestSuite) TestValidateLanguage() {
|
func (suite *ValidationTestSuite) TestValidateLanguage() {
|
||||||
empty := ""
|
testCases := []struct {
|
||||||
notALanguage := "this isn't a language at all!"
|
name, input, expected, err string
|
||||||
english := "en"
|
}{
|
||||||
capitalEnglish := "EN"
|
{name: "empty", err: "no language provided"},
|
||||||
arabic3Letters := "ara"
|
{name: "notALanguage", input: "this isn't a language at all!", err: "language: tag is not well-formed"},
|
||||||
mixedCapsEnglish := "eN"
|
{name: "english", input: "en", expected: "en"},
|
||||||
englishUS := "en-us"
|
// Should be all lowercase
|
||||||
dutch := "nl"
|
{name: "capitalEnglish", input: "EN", expected: "en"},
|
||||||
german := "de"
|
// Overlong, should be in ISO 639-1 format
|
||||||
var err error
|
{name: "arabic3Letters", input: "ara", expected: "ar"},
|
||||||
|
// Should be all lowercase
|
||||||
err = validate.Language(empty)
|
{name: "mixedCapsEnglish", input: "eN", expected: "en"},
|
||||||
if suite.Error(err) {
|
// Region should be capitalized
|
||||||
suite.Equal(errors.New("no language provided"), err)
|
{name: "englishUS", input: "en-us", expected: "en-US"},
|
||||||
|
{name: "dutch", input: "nl", expected: "nl"},
|
||||||
|
{name: "german", input: "de", expected: "de"},
|
||||||
|
{name: "chinese", input: "zh", expected: "zh"},
|
||||||
|
{name: "chineseSimplified", input: "zh-Hans", expected: "zh-Hans"},
|
||||||
|
{name: "chineseTraditional", input: "zh-Hant", expected: "zh-Hant"},
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validate.Language(notALanguage)
|
for _, testCase := range testCases {
|
||||||
if suite.Error(err) {
|
testCase := testCase
|
||||||
suite.Equal(errors.New("language: tag is not well-formed"), err)
|
suite.Run(testCase.name, func() {
|
||||||
}
|
actual, actualErr := validate.Language(testCase.input)
|
||||||
|
if testCase.err == "" {
|
||||||
err = validate.Language(english)
|
suite.Equal(testCase.expected, actual)
|
||||||
if suite.NoError(err) {
|
suite.NoError(actualErr)
|
||||||
suite.Equal(nil, err)
|
} else {
|
||||||
}
|
suite.Empty(actual)
|
||||||
|
suite.EqualError(actualErr, testCase.err)
|
||||||
err = validate.Language(capitalEnglish)
|
}
|
||||||
if suite.NoError(err) {
|
})
|
||||||
suite.Equal(nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validate.Language(arabic3Letters)
|
|
||||||
if suite.NoError(err) {
|
|
||||||
suite.Equal(nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validate.Language(mixedCapsEnglish)
|
|
||||||
if suite.NoError(err) {
|
|
||||||
suite.Equal(nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validate.Language(englishUS)
|
|
||||||
if suite.Error(err) {
|
|
||||||
suite.Equal(errors.New("language: tag is not well-formed"), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validate.Language(dutch)
|
|
||||||
if suite.NoError(err) {
|
|
||||||
suite.Equal(nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validate.Language(german)
|
|
||||||
if suite.NoError(err) {
|
|
||||||
suite.Equal(nil, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -115,7 +116,7 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestThread() {
|
||||||
Content: "nbnbdy expects dog",
|
Content: "nbnbdy expects dog",
|
||||||
CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
||||||
Local: testrig.FalseBool(),
|
Local: util.Ptr(false),
|
||||||
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
||||||
AccountID: threadParentAccount.ID,
|
AccountID: threadParentAccount.ID,
|
||||||
InReplyToID: originalStatus.ID,
|
InReplyToID: originalStatus.ID,
|
||||||
|
@ -124,13 +125,13 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestThread() {
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityFollowersOnly,
|
Visibility: gtsmodel.VisibilityFollowersOnly,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "",
|
CreatedWithApplicationID: "",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil {
|
if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil {
|
||||||
|
@ -168,7 +169,7 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyFollowersOnly(
|
||||||
Content: "didn't expect dog",
|
Content: "didn't expect dog",
|
||||||
CreatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
CreatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
||||||
Local: testrig.FalseBool(),
|
Local: util.Ptr(false),
|
||||||
AccountURI: "http://fossbros-anonymous.io/users/foss_satan",
|
AccountURI: "http://fossbros-anonymous.io/users/foss_satan",
|
||||||
AccountID: originalStatusParent.ID,
|
AccountID: originalStatusParent.ID,
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
|
@ -177,13 +178,13 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyFollowersOnly(
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityFollowersOnly,
|
Visibility: gtsmodel.VisibilityFollowersOnly,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "",
|
CreatedWithApplicationID: "",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, originalStatus); err != nil {
|
if err := suite.db.PutStatus(ctx, originalStatus); err != nil {
|
||||||
|
@ -202,7 +203,7 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyFollowersOnly(
|
||||||
Content: "nbnbdy expects dog",
|
Content: "nbnbdy expects dog",
|
||||||
CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
||||||
Local: testrig.FalseBool(),
|
Local: util.Ptr(false),
|
||||||
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
||||||
AccountID: replyingAccount.ID,
|
AccountID: replyingAccount.ID,
|
||||||
InReplyToID: originalStatus.ID,
|
InReplyToID: originalStatus.ID,
|
||||||
|
@ -211,13 +212,13 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyFollowersOnly(
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityFollowersOnly,
|
Visibility: gtsmodel.VisibilityFollowersOnly,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "",
|
CreatedWithApplicationID: "",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil {
|
if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil {
|
||||||
|
@ -236,7 +237,7 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyFollowersOnly(
|
||||||
Content: "*nobody",
|
Content: "*nobody",
|
||||||
CreatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
CreatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
||||||
Local: testrig.FalseBool(),
|
Local: util.Ptr(false),
|
||||||
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
||||||
AccountID: replyingAccount.ID,
|
AccountID: replyingAccount.ID,
|
||||||
InReplyToID: firstReplyStatus.ID,
|
InReplyToID: firstReplyStatus.ID,
|
||||||
|
@ -245,13 +246,13 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyFollowersOnly(
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityFollowersOnly,
|
Visibility: gtsmodel.VisibilityFollowersOnly,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "",
|
CreatedWithApplicationID: "",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, secondReplyStatus); err != nil {
|
if err := suite.db.PutStatus(ctx, secondReplyStatus); err != nil {
|
||||||
|
@ -281,7 +282,7 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyPublicAndUnloc
|
||||||
Content: "didn't expect dog",
|
Content: "didn't expect dog",
|
||||||
CreatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
CreatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"),
|
||||||
Local: testrig.FalseBool(),
|
Local: util.Ptr(false),
|
||||||
AccountURI: "http://fossbros-anonymous.io/users/foss_satan",
|
AccountURI: "http://fossbros-anonymous.io/users/foss_satan",
|
||||||
AccountID: originalStatusParent.ID,
|
AccountID: originalStatusParent.ID,
|
||||||
InReplyToID: "",
|
InReplyToID: "",
|
||||||
|
@ -290,13 +291,13 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyPublicAndUnloc
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityUnlocked,
|
Visibility: gtsmodel.VisibilityUnlocked,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "",
|
CreatedWithApplicationID: "",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, originalStatus); err != nil {
|
if err := suite.db.PutStatus(ctx, originalStatus); err != nil {
|
||||||
|
@ -315,7 +316,7 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyPublicAndUnloc
|
||||||
Content: "nbnbdy expects dog",
|
Content: "nbnbdy expects dog",
|
||||||
CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"),
|
||||||
Local: testrig.FalseBool(),
|
Local: util.Ptr(false),
|
||||||
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
||||||
AccountID: replyingAccount.ID,
|
AccountID: replyingAccount.ID,
|
||||||
InReplyToID: originalStatus.ID,
|
InReplyToID: originalStatus.ID,
|
||||||
|
@ -324,13 +325,13 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyPublicAndUnloc
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityPublic,
|
Visibility: gtsmodel.VisibilityPublic,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "",
|
CreatedWithApplicationID: "",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil {
|
if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil {
|
||||||
|
@ -349,7 +350,7 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyPublicAndUnloc
|
||||||
Content: "*nobody",
|
Content: "*nobody",
|
||||||
CreatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
CreatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
||||||
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
UpdatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"),
|
||||||
Local: testrig.FalseBool(),
|
Local: util.Ptr(false),
|
||||||
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
AccountURI: "http://localhost:8080/users/the_mighty_zork",
|
||||||
AccountID: replyingAccount.ID,
|
AccountID: replyingAccount.ID,
|
||||||
InReplyToID: firstReplyStatus.ID,
|
InReplyToID: firstReplyStatus.ID,
|
||||||
|
@ -358,13 +359,13 @@ func (suite *StatusStatusHomeTimelineableTestSuite) TestChainReplyPublicAndUnloc
|
||||||
BoostOfID: "",
|
BoostOfID: "",
|
||||||
ContentWarning: "",
|
ContentWarning: "",
|
||||||
Visibility: gtsmodel.VisibilityUnlocked,
|
Visibility: gtsmodel.VisibilityUnlocked,
|
||||||
Sensitive: testrig.FalseBool(),
|
Sensitive: util.Ptr(false),
|
||||||
Language: "en",
|
Language: "en",
|
||||||
CreatedWithApplicationID: "",
|
CreatedWithApplicationID: "",
|
||||||
Federated: testrig.TrueBool(),
|
Federated: util.Ptr(true),
|
||||||
Boostable: testrig.TrueBool(),
|
Boostable: util.Ptr(true),
|
||||||
Replyable: testrig.TrueBool(),
|
Replyable: util.Ptr(true),
|
||||||
Likeable: testrig.TrueBool(),
|
Likeable: util.Ptr(true),
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
}
|
}
|
||||||
if err := suite.db.PutStatus(ctx, secondReplyStatus); err != nil {
|
if err := suite.db.PutStatus(ctx, secondReplyStatus); err != nil {
|
||||||
|
|
|
@ -27,29 +27,43 @@ const (
|
||||||
robotsPath = "/robots.txt"
|
robotsPath = "/robots.txt"
|
||||||
robotsMetaAllowSome = "nofollow, noarchive, nositelinkssearchbox, max-image-preview:standard" // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#robotsmeta
|
robotsMetaAllowSome = "nofollow, noarchive, nositelinkssearchbox, max-image-preview:standard" // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#robotsmeta
|
||||||
robotsTxt = `# GoToSocial robots.txt -- to edit, see internal/web/robots.go
|
robotsTxt = `# GoToSocial robots.txt -- to edit, see internal/web/robots.go
|
||||||
# more info @ https://developers.google.com/search/docs/crawling-indexing/robots/intro
|
# More info @ https://developers.google.com/search/docs/crawling-indexing/robots/intro
|
||||||
|
|
||||||
|
# Before we commence, a giant fuck you to ChatGPT in particular.
|
||||||
|
# https://platform.openai.com/docs/gptbot
|
||||||
|
User-agent: GPTBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
# Rules for everything else.
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Crawl-delay: 500
|
Crawl-delay: 500
|
||||||
# api stuff
|
|
||||||
|
# API endpoints.
|
||||||
Disallow: /api/
|
Disallow: /api/
|
||||||
# auth/login stuff
|
|
||||||
|
# Auth/login endpoints.
|
||||||
Disallow: /auth/
|
Disallow: /auth/
|
||||||
Disallow: /oauth/
|
Disallow: /oauth/
|
||||||
Disallow: /check_your_email
|
Disallow: /check_your_email
|
||||||
Disallow: /wait_for_approval
|
Disallow: /wait_for_approval
|
||||||
Disallow: /account_disabled
|
Disallow: /account_disabled
|
||||||
# well known stuff
|
|
||||||
|
# Well-known endpoints.
|
||||||
Disallow: /.well-known/
|
Disallow: /.well-known/
|
||||||
# files
|
|
||||||
|
# Fileserver/media.
|
||||||
Disallow: /fileserver/
|
Disallow: /fileserver/
|
||||||
# s2s AP stuff
|
|
||||||
|
# Fedi S2S API endpoints.
|
||||||
Disallow: /users/
|
Disallow: /users/
|
||||||
Disallow: /emoji/
|
Disallow: /emoji/
|
||||||
# panels
|
|
||||||
|
# Settings panels.
|
||||||
Disallow: /admin
|
Disallow: /admin
|
||||||
Disallow: /user
|
Disallow: /user
|
||||||
Disallow: /settings/
|
Disallow: /settings/
|
||||||
# domain blocklist
|
|
||||||
|
# Domain blocklist.
|
||||||
Disallow: /about/suspended`
|
Disallow: /about/suspended`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Ensure test args are set.
|
||||||
|
ARGS=${@}; [ -z "$ARGS" ] && \
|
||||||
|
ARGS='./...'
|
||||||
|
|
||||||
|
# Database config.
|
||||||
DB_NAME='postgres'
|
DB_NAME='postgres'
|
||||||
DB_USER='postgres'
|
DB_USER='postgres'
|
||||||
DB_PASS='postgres'
|
DB_PASS='postgres'
|
||||||
|
@ -34,4 +39,4 @@ GTS_DB_PORT=${DB_PORT} \
|
||||||
GTS_DB_USER=${DB_USER} \
|
GTS_DB_USER=${DB_USER} \
|
||||||
GTS_DB_PASSWORD=${DB_PASS} \
|
GTS_DB_PASSWORD=${DB_PASS} \
|
||||||
GTS_DB_DATABASE=${DB_NAME} \
|
GTS_DB_DATABASE=${DB_NAME} \
|
||||||
go test ./... -p 1 ${@}
|
go test ./... -p 1 ${ARGS}
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Ensure test args are set.
|
||||||
|
ARGS=${@}; [ -z "$ARGS" ] && \
|
||||||
|
ARGS='./...'
|
||||||
|
|
||||||
|
# Run the SQLite tests.
|
||||||
GTS_DB_TYPE=sqlite \
|
GTS_DB_TYPE=sqlite \
|
||||||
GTS_DB_ADDRESS=':memory:' \
|
GTS_DB_ADDRESS=':memory:' \
|
||||||
go test ./... ${@}
|
go test ${ARGS}
|
File diff suppressed because it is too large
Load diff
2
vendor/github.com/tdewolff/minify/v2/README.md
generated
vendored
2
vendor/github.com/tdewolff/minify/v2/README.md
generated
vendored
|
@ -79,7 +79,7 @@ Minifiers or bindings to minifiers exist in almost all programming languages. So
|
||||||
This minifier proves to be that fast and extensive minifier that can handle HTML and any other filetype it may contain (CSS, JS, ...). It is usually orders of magnitude faster than existing minifiers.
|
This minifier proves to be that fast and extensive minifier that can handle HTML and any other filetype it may contain (CSS, JS, ...). It is usually orders of magnitude faster than existing minifiers.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Make sure you have [Git](https://git-scm.com/) and [Go](https://golang.org/dl/) (1.13 or higher) installed, run
|
Make sure you have [Git](https://git-scm.com/) and [Go](https://golang.org/dl/) (1.18 or higher) installed, run
|
||||||
```
|
```
|
||||||
mkdir Project
|
mkdir Project
|
||||||
cd Project
|
cd Project
|
||||||
|
|
941
vendor/github.com/tdewolff/minify/v2/html/hash.go
generated
vendored
941
vendor/github.com/tdewolff/minify/v2/html/hash.go
generated
vendored
|
@ -11,238 +11,254 @@ type Hash uint32
|
||||||
// Unique hash definitions to be used instead of strings
|
// Unique hash definitions to be used instead of strings
|
||||||
const (
|
const (
|
||||||
A Hash = 0x1 // a
|
A Hash = 0x1 // a
|
||||||
Abbr Hash = 0x37a04 // abbr
|
Abbr Hash = 0x3b804 // abbr
|
||||||
About Hash = 0x5 // about
|
About Hash = 0x5 // about
|
||||||
Accept Hash = 0x1106 // accept
|
Accept Hash = 0x1106 // accept
|
||||||
Accept_Charset Hash = 0x110e // accept-charset
|
Accept_Charset Hash = 0x110e // accept-charset
|
||||||
Action Hash = 0x23f06 // action
|
Acronym Hash = 0x4a07 // acronym
|
||||||
Address Hash = 0x5a07 // address
|
Action Hash = 0x21d06 // action
|
||||||
Align Hash = 0x32705 // align
|
Address Hash = 0x7807 // address
|
||||||
Alink Hash = 0x7005 // alink
|
Align Hash = 0x35b05 // align
|
||||||
Allowfullscreen Hash = 0x2ad0f // allowfullscreen
|
Alink Hash = 0x3a405 // alink
|
||||||
Amp_Boilerplate Hash = 0x610f // amp-boilerplate
|
Allowfullscreen Hash = 0x2e10f // allowfullscreen
|
||||||
Area Hash = 0x1e304 // area
|
Amp_Boilerplate Hash = 0x7f0f // amp-boilerplate
|
||||||
|
Applet Hash = 0xd706 // applet
|
||||||
|
Area Hash = 0x2fd04 // area
|
||||||
Article Hash = 0x2707 // article
|
Article Hash = 0x2707 // article
|
||||||
Aside Hash = 0xb405 // aside
|
Aside Hash = 0x5b05 // aside
|
||||||
Async Hash = 0xac05 // async
|
Async Hash = 0x8e05 // async
|
||||||
Audio Hash = 0xd105 // audio
|
Audio Hash = 0x9605 // audio
|
||||||
Autofocus Hash = 0xe409 // autofocus
|
Autofocus Hash = 0xcc09 // autofocus
|
||||||
Autoplay Hash = 0x10808 // autoplay
|
Autoplay Hash = 0x10c08 // autoplay
|
||||||
Axis Hash = 0x11004 // axis
|
Axis Hash = 0x11404 // axis
|
||||||
B Hash = 0x101 // b
|
B Hash = 0x101 // b
|
||||||
Background Hash = 0x300a // background
|
Background Hash = 0x300a // background
|
||||||
Base Hash = 0x19604 // base
|
Base Hash = 0x17804 // base
|
||||||
Bb Hash = 0x37b02 // bb
|
Basefont Hash = 0x17808 // basefont
|
||||||
Bdi Hash = 0x7503 // bdi
|
Bb Hash = 0x3b902 // bb
|
||||||
Bdo Hash = 0x31f03 // bdo
|
Bdi Hash = 0x18403 // bdi
|
||||||
Bgcolor Hash = 0x12607 // bgcolor
|
Bdo Hash = 0x35303 // bdo
|
||||||
Blockquote Hash = 0x13e0a // blockquote
|
Bgcolor Hash = 0x12a07 // bgcolor
|
||||||
|
Big Hash = 0x13103 // big
|
||||||
|
Blockquote Hash = 0x1340a // blockquote
|
||||||
Body Hash = 0xd04 // body
|
Body Hash = 0xd04 // body
|
||||||
Br Hash = 0x37c02 // br
|
Br Hash = 0x36102 // br
|
||||||
Button Hash = 0x14806 // button
|
Button Hash = 0x13e06 // button
|
||||||
Canvas Hash = 0xb006 // canvas
|
Canvas Hash = 0x5706 // canvas
|
||||||
Caption Hash = 0x21f07 // caption
|
Caption Hash = 0x1fe07 // caption
|
||||||
|
Center Hash = 0xb706 // center
|
||||||
Charset Hash = 0x1807 // charset
|
Charset Hash = 0x1807 // charset
|
||||||
Checked Hash = 0x1b307 // checked
|
Checked Hash = 0x19707 // checked
|
||||||
Cite Hash = 0xfb04 // cite
|
Cite Hash = 0x9204 // cite
|
||||||
Class Hash = 0x15905 // class
|
Class Hash = 0x15105 // class
|
||||||
Classid Hash = 0x15907 // classid
|
Classid Hash = 0x15107 // classid
|
||||||
Clear Hash = 0x2b05 // clear
|
Clear Hash = 0x2b05 // clear
|
||||||
Code Hash = 0x19204 // code
|
Code Hash = 0x17404 // code
|
||||||
Codebase Hash = 0x19208 // codebase
|
Codebase Hash = 0x17408 // codebase
|
||||||
Codetype Hash = 0x1a408 // codetype
|
Codetype Hash = 0x18808 // codetype
|
||||||
Col Hash = 0x12803 // col
|
Col Hash = 0x12c03 // col
|
||||||
Colgroup Hash = 0x1bb08 // colgroup
|
Colgroup Hash = 0x1af08 // colgroup
|
||||||
Color Hash = 0x12805 // color
|
Color Hash = 0x12c05 // color
|
||||||
Cols Hash = 0x1cf04 // cols
|
Cols Hash = 0x1c904 // cols
|
||||||
Colspan Hash = 0x1cf07 // colspan
|
Colspan Hash = 0x1c907 // colspan
|
||||||
Compact Hash = 0x1ec07 // compact
|
Compact Hash = 0x1d707 // compact
|
||||||
Content Hash = 0x28407 // content
|
Content Hash = 0x27b07 // content
|
||||||
Controls Hash = 0x20108 // controls
|
Controls Hash = 0x1e708 // controls
|
||||||
Data Hash = 0x1f04 // data
|
Data Hash = 0x1f04 // data
|
||||||
Datalist Hash = 0x1f08 // datalist
|
Datalist Hash = 0x1f08 // datalist
|
||||||
Datatype Hash = 0x4d08 // datatype
|
Datatype Hash = 0xac08 // datatype
|
||||||
Dd Hash = 0x5b02 // dd
|
Dd Hash = 0x7902 // dd
|
||||||
Declare Hash = 0xb707 // declare
|
Declare Hash = 0x5e07 // declare
|
||||||
Default Hash = 0x7f07 // default
|
Default Hash = 0xeb07 // default
|
||||||
DefaultChecked Hash = 0x1730e // defaultChecked
|
DefaultChecked Hash = 0x2270e // defaultChecked
|
||||||
DefaultMuted Hash = 0x7f0c // defaultMuted
|
DefaultMuted Hash = 0xeb0c // defaultMuted
|
||||||
DefaultSelected Hash = 0x8a0f // defaultSelected
|
DefaultSelected Hash = 0xf60f // defaultSelected
|
||||||
Defer Hash = 0x9805 // defer
|
Defer Hash = 0x10405 // defer
|
||||||
Del Hash = 0x10503 // del
|
Del Hash = 0x37903 // del
|
||||||
Details Hash = 0x15f07 // details
|
Details Hash = 0x15707 // details
|
||||||
Dfn Hash = 0x16c03 // dfn
|
Dfn Hash = 0x16403 // dfn
|
||||||
Dialog Hash = 0xa606 // dialog
|
Dialog Hash = 0xc606 // dialog
|
||||||
Dir Hash = 0x7603 // dir
|
Dir Hash = 0x18503 // dir
|
||||||
Disabled Hash = 0x18008 // disabled
|
Disabled Hash = 0x19d08 // disabled
|
||||||
Div Hash = 0x18703 // div
|
Div Hash = 0x1a403 // div
|
||||||
Dl Hash = 0x1b902 // dl
|
Dl Hash = 0x1e502 // dl
|
||||||
Dt Hash = 0x23102 // dt
|
Dt Hash = 0x21702 // dt
|
||||||
Em Hash = 0x4302 // em
|
Em Hash = 0x4302 // em
|
||||||
Embed Hash = 0x4905 // embed
|
Embed Hash = 0x37505 // embed
|
||||||
Enabled Hash = 0x26c07 // enabled
|
Enabled Hash = 0x26307 // enabled
|
||||||
Enctype Hash = 0x1fa07 // enctype
|
Enctype Hash = 0x2a207 // enctype
|
||||||
Face Hash = 0x5604 // face
|
Face Hash = 0xb504 // face
|
||||||
Fieldset Hash = 0x21408 // fieldset
|
Fieldset Hash = 0x1f308 // fieldset
|
||||||
Figcaption Hash = 0x21c0a // figcaption
|
Figcaption Hash = 0x1fb0a // figcaption
|
||||||
Figure Hash = 0x22606 // figure
|
Figure Hash = 0x20c06 // figure
|
||||||
Footer Hash = 0xdb06 // footer
|
Font Hash = 0x17c04 // font
|
||||||
For Hash = 0x23b03 // for
|
Footer Hash = 0xa006 // footer
|
||||||
Form Hash = 0x23b04 // form
|
For Hash = 0x21903 // for
|
||||||
Formaction Hash = 0x23b0a // formaction
|
Form Hash = 0x21904 // form
|
||||||
Formnovalidate Hash = 0x2450e // formnovalidate
|
Formaction Hash = 0x2190a // formaction
|
||||||
Frame Hash = 0x28c05 // frame
|
Formnovalidate Hash = 0x2350e // formnovalidate
|
||||||
Frameborder Hash = 0x28c0b // frameborder
|
Frame Hash = 0x14505 // frame
|
||||||
H1 Hash = 0x2e002 // h1
|
Frameborder Hash = 0x2830b // frameborder
|
||||||
H2 Hash = 0x25302 // h2
|
Frameset Hash = 0x14508 // frameset
|
||||||
H3 Hash = 0x25502 // h3
|
H1 Hash = 0x2d002 // h1
|
||||||
H4 Hash = 0x25702 // h4
|
H2 Hash = 0x24302 // h2
|
||||||
H5 Hash = 0x25902 // h5
|
H3 Hash = 0x24502 // h3
|
||||||
H6 Hash = 0x25b02 // h6
|
H4 Hash = 0x24702 // h4
|
||||||
Head Hash = 0x2d204 // head
|
H5 Hash = 0x24902 // h5
|
||||||
Header Hash = 0x2d206 // header
|
H6 Hash = 0x24b02 // h6
|
||||||
Hgroup Hash = 0x25d06 // hgroup
|
Head Hash = 0x2c204 // head
|
||||||
Hidden Hash = 0x26806 // hidden
|
Header Hash = 0x2c206 // header
|
||||||
Hr Hash = 0x32d02 // hr
|
Hgroup Hash = 0x24d06 // hgroup
|
||||||
Href Hash = 0x32d04 // href
|
Hidden Hash = 0x25f06 // hidden
|
||||||
Hreflang Hash = 0x32d08 // hreflang
|
Hr Hash = 0x16802 // hr
|
||||||
Html Hash = 0x27304 // html
|
Href Hash = 0x16804 // href
|
||||||
Http_Equiv Hash = 0x2770a // http-equiv
|
Hreflang Hash = 0x16808 // hreflang
|
||||||
|
Html Hash = 0x26a04 // html
|
||||||
|
Http_Equiv Hash = 0x26e0a // http-equiv
|
||||||
I Hash = 0x2401 // i
|
I Hash = 0x2401 // i
|
||||||
Icon Hash = 0x28304 // icon
|
Icon Hash = 0x27a04 // icon
|
||||||
Id Hash = 0xb602 // id
|
Id Hash = 0x5d02 // id
|
||||||
Iframe Hash = 0x28b06 // iframe
|
Iframe Hash = 0x28206 // iframe
|
||||||
Img Hash = 0x29703 // img
|
Image Hash = 0x28e05 // image
|
||||||
Inert Hash = 0xf605 // inert
|
Img Hash = 0x29303 // img
|
||||||
Inlist Hash = 0x29a06 // inlist
|
Inert Hash = 0x5205 // inert
|
||||||
Input Hash = 0x2a405 // input
|
Inlist Hash = 0x29606 // inlist
|
||||||
Ins Hash = 0x2a903 // ins
|
Input Hash = 0x2a905 // input
|
||||||
Ismap Hash = 0x11205 // ismap
|
Ins Hash = 0x2ae03 // ins
|
||||||
Itemscope Hash = 0xfc09 // itemscope
|
Ismap Hash = 0x11605 // ismap
|
||||||
Kbd Hash = 0x7403 // kbd
|
Itemscope Hash = 0xe209 // itemscope
|
||||||
Keygen Hash = 0x1f606 // keygen
|
Kbd Hash = 0x18303 // kbd
|
||||||
Label Hash = 0xbe05 // label
|
Keygen Hash = 0x29e06 // keygen
|
||||||
Lang Hash = 0x33104 // lang
|
Label Hash = 0x6505 // label
|
||||||
Language Hash = 0x33108 // language
|
Lang Hash = 0x16c04 // lang
|
||||||
Legend Hash = 0x2c506 // legend
|
Language Hash = 0x16c08 // language
|
||||||
|
Legend Hash = 0x31706 // legend
|
||||||
Li Hash = 0x2302 // li
|
Li Hash = 0x2302 // li
|
||||||
Link Hash = 0x7104 // link
|
Link Hash = 0x3a504 // link
|
||||||
Longdesc Hash = 0xc208 // longdesc
|
Longdesc Hash = 0x6908 // longdesc
|
||||||
Main Hash = 0xf404 // main
|
Main Hash = 0x5004 // main
|
||||||
Manifest Hash = 0x2bc08 // manifest
|
Manifest Hash = 0x11e08 // manifest
|
||||||
Map Hash = 0xee03 // map
|
Map Hash = 0xd603 // map
|
||||||
Mark Hash = 0x2cb04 // mark
|
Mark Hash = 0x2b404 // mark
|
||||||
Math Hash = 0x2cf04 // math
|
Marquee Hash = 0x2b807 // marquee
|
||||||
Max Hash = 0x2d803 // max
|
Math Hash = 0x2bf04 // math
|
||||||
Maxlength Hash = 0x2d809 // maxlength
|
Max Hash = 0x2c803 // max
|
||||||
Media Hash = 0xa405 // media
|
Maxlength Hash = 0x2c809 // maxlength
|
||||||
Menu Hash = 0x12204 // menu
|
Media Hash = 0xc405 // media
|
||||||
Meta Hash = 0x2e204 // meta
|
Menu Hash = 0xde04 // menu
|
||||||
Meter Hash = 0x2f705 // meter
|
Menuitem Hash = 0xde08 // menuitem
|
||||||
Method Hash = 0x2fc06 // method
|
Meta Hash = 0x2d204 // meta
|
||||||
Multiple Hash = 0x30208 // multiple
|
Meter Hash = 0x30605 // meter
|
||||||
Muted Hash = 0x30a05 // muted
|
Method Hash = 0x30b06 // method
|
||||||
Name Hash = 0xa204 // name
|
Multiple Hash = 0x31108 // multiple
|
||||||
Nav Hash = 0x32403 // nav
|
Muted Hash = 0x31d05 // muted
|
||||||
Nohref Hash = 0x32b06 // nohref
|
Name Hash = 0xc204 // name
|
||||||
Noresize Hash = 0x13608 // noresize
|
Nav Hash = 0x35803 // nav
|
||||||
Noscript Hash = 0x14d08 // noscript
|
Nobr Hash = 0x35f04 // nobr
|
||||||
Noshade Hash = 0x16e07 // noshade
|
Noembed Hash = 0x37307 // noembed
|
||||||
Novalidate Hash = 0x2490a // novalidate
|
Noframes Hash = 0x14308 // noframes
|
||||||
Nowrap Hash = 0x1d506 // nowrap
|
Nohref Hash = 0x16606 // nohref
|
||||||
Object Hash = 0xd506 // object
|
Noresize Hash = 0x1cf08 // noresize
|
||||||
Ol Hash = 0xcb02 // ol
|
Noscript Hash = 0x20408 // noscript
|
||||||
Open Hash = 0x32104 // open
|
Noshade Hash = 0x22207 // noshade
|
||||||
Optgroup Hash = 0x35608 // optgroup
|
Novalidate Hash = 0x2390a // novalidate
|
||||||
Option Hash = 0x30f06 // option
|
Nowrap Hash = 0x2ef06 // nowrap
|
||||||
|
Object Hash = 0x9a06 // object
|
||||||
|
Ol Hash = 0x7202 // ol
|
||||||
|
Open Hash = 0x35504 // open
|
||||||
|
Optgroup Hash = 0x39908 // optgroup
|
||||||
|
Option Hash = 0x32206 // option
|
||||||
Output Hash = 0x206 // output
|
Output Hash = 0x206 // output
|
||||||
P Hash = 0x501 // p
|
P Hash = 0x501 // p
|
||||||
Param Hash = 0xf005 // param
|
Param Hash = 0x11a05 // param
|
||||||
Pauseonexit Hash = 0x1160b // pauseonexit
|
Pauseonexit Hash = 0x1b60b // pauseonexit
|
||||||
Picture Hash = 0x1c207 // picture
|
Picture Hash = 0x25207 // picture
|
||||||
Plaintext Hash = 0x1da09 // plaintext
|
Plaintext Hash = 0x2f409 // plaintext
|
||||||
Poster Hash = 0x26206 // poster
|
Portal Hash = 0x3a006 // portal
|
||||||
Pre Hash = 0x35d03 // pre
|
Poster Hash = 0x38c06 // poster
|
||||||
Prefix Hash = 0x35d06 // prefix
|
Pre Hash = 0x38503 // pre
|
||||||
Profile Hash = 0x36407 // profile
|
Prefix Hash = 0x38506 // prefix
|
||||||
Progress Hash = 0x34208 // progress
|
Profile Hash = 0x32807 // profile
|
||||||
Property Hash = 0x31508 // property
|
Progress Hash = 0x32f08 // progress
|
||||||
Q Hash = 0x14301 // q
|
Property Hash = 0x33e08 // property
|
||||||
|
Q Hash = 0x13901 // q
|
||||||
Rb Hash = 0x2f02 // rb
|
Rb Hash = 0x2f02 // rb
|
||||||
Readonly Hash = 0x1e408 // readonly
|
Readonly Hash = 0x2fe08 // readonly
|
||||||
Rel Hash = 0xbc03 // rel
|
Rel Hash = 0x6303 // rel
|
||||||
Required Hash = 0x22a08 // required
|
Required Hash = 0x21008 // required
|
||||||
Resource Hash = 0x1c708 // resource
|
Resource Hash = 0x25708 // resource
|
||||||
Rev Hash = 0x7803 // rev
|
Rev Hash = 0xa503 // rev
|
||||||
Reversed Hash = 0x7808 // reversed
|
Reversed Hash = 0xa508 // reversed
|
||||||
Rows Hash = 0x9c04 // rows
|
Rows Hash = 0xbc04 // rows
|
||||||
Rowspan Hash = 0x9c07 // rowspan
|
Rowspan Hash = 0xbc07 // rowspan
|
||||||
Rp Hash = 0x6a02 // rp
|
Rp Hash = 0x8802 // rp
|
||||||
Rt Hash = 0x2802 // rt
|
Rt Hash = 0x2802 // rt
|
||||||
Rtc Hash = 0xf903 // rtc
|
Rtc Hash = 0x5503 // rtc
|
||||||
Ruby Hash = 0xe004 // ruby
|
Ruby Hash = 0x10804 // ruby
|
||||||
Rules Hash = 0x12c05 // rules
|
Rules Hash = 0x36205 // rules
|
||||||
S Hash = 0x1c01 // s
|
S Hash = 0x1c01 // s
|
||||||
Samp Hash = 0x6004 // samp
|
Samp Hash = 0x7e04 // samp
|
||||||
Scope Hash = 0x10005 // scope
|
Scope Hash = 0xe605 // scope
|
||||||
Scoped Hash = 0x10006 // scoped
|
Scoped Hash = 0xe606 // scoped
|
||||||
Script Hash = 0x14f06 // script
|
Script Hash = 0x20606 // script
|
||||||
Scrolling Hash = 0xc809 // scrolling
|
Scrolling Hash = 0x6f09 // scrolling
|
||||||
Seamless Hash = 0x19808 // seamless
|
Seamless Hash = 0x36608 // seamless
|
||||||
Section Hash = 0x13007 // section
|
Section Hash = 0x36d07 // section
|
||||||
Select Hash = 0x16506 // select
|
Select Hash = 0x15d06 // select
|
||||||
Selected Hash = 0x16508 // selected
|
Selected Hash = 0x15d08 // selected
|
||||||
Shape Hash = 0x19f05 // shape
|
Shape Hash = 0x1ee05 // shape
|
||||||
Size Hash = 0x13a04 // size
|
Size Hash = 0x1d304 // size
|
||||||
Slot Hash = 0x20804 // slot
|
Slot Hash = 0x2b004 // slot
|
||||||
Small Hash = 0x2ab05 // small
|
Small Hash = 0x2df05 // small
|
||||||
Sortable Hash = 0x2ef08 // sortable
|
Sortable Hash = 0x33608 // sortable
|
||||||
Source Hash = 0x1c906 // source
|
Source Hash = 0x25906 // source
|
||||||
Span Hash = 0x9f04 // span
|
Span Hash = 0xbf04 // span
|
||||||
Src Hash = 0x34903 // src
|
Src Hash = 0x34603 // src
|
||||||
Srcset Hash = 0x34906 // srcset
|
Srcset Hash = 0x34606 // srcset
|
||||||
Start Hash = 0x2505 // start
|
Start Hash = 0x2505 // start
|
||||||
Strong Hash = 0x29e06 // strong
|
Strike Hash = 0x29a06 // strike
|
||||||
Style Hash = 0x2c205 // style
|
Strong Hash = 0x12406 // strong
|
||||||
Sub Hash = 0x31d03 // sub
|
Style Hash = 0x34c05 // style
|
||||||
Summary Hash = 0x33907 // summary
|
Sub Hash = 0x35103 // sub
|
||||||
Sup Hash = 0x34003 // sup
|
Summary Hash = 0x37c07 // summary
|
||||||
Svg Hash = 0x34f03 // svg
|
Sup Hash = 0x38303 // sup
|
||||||
Tabindex Hash = 0x2e408 // tabindex
|
Svg Hash = 0x39203 // svg
|
||||||
Table Hash = 0x2f205 // table
|
Tabindex Hash = 0x2d408 // tabindex
|
||||||
|
Table Hash = 0x33905 // table
|
||||||
Target Hash = 0x706 // target
|
Target Hash = 0x706 // target
|
||||||
Tbody Hash = 0xc05 // tbody
|
Tbody Hash = 0xc05 // tbody
|
||||||
Td Hash = 0x1e02 // td
|
Td Hash = 0x1e02 // td
|
||||||
Template Hash = 0x4208 // template
|
Template Hash = 0x4208 // template
|
||||||
Text Hash = 0x1df04 // text
|
Text Hash = 0x2f904 // text
|
||||||
Textarea Hash = 0x1df08 // textarea
|
Textarea Hash = 0x2f908 // textarea
|
||||||
Tfoot Hash = 0xda05 // tfoot
|
Tfoot Hash = 0x9f05 // tfoot
|
||||||
Th Hash = 0x2d102 // th
|
Th Hash = 0x2c102 // th
|
||||||
Thead Hash = 0x2d105 // thead
|
Thead Hash = 0x2c105 // thead
|
||||||
Time Hash = 0x12004 // time
|
Time Hash = 0xdc04 // time
|
||||||
Title Hash = 0x15405 // title
|
Title Hash = 0x14c05 // title
|
||||||
Tr Hash = 0x1f202 // tr
|
Tr Hash = 0x12502 // tr
|
||||||
Track Hash = 0x1f205 // track
|
Track Hash = 0x17f05 // track
|
||||||
Translate Hash = 0x20b09 // translate
|
Translate Hash = 0x1c009 // translate
|
||||||
Truespeed Hash = 0x23209 // truespeed
|
Truespeed Hash = 0x1dd09 // truespeed
|
||||||
Type Hash = 0x5104 // type
|
Tt Hash = 0x14002 // tt
|
||||||
Typemustmatch Hash = 0x1a80d // typemustmatch
|
Type Hash = 0xb004 // type
|
||||||
Typeof Hash = 0x5106 // typeof
|
Typemustmatch Hash = 0x18c0d // typemustmatch
|
||||||
|
Typeof Hash = 0xb006 // typeof
|
||||||
U Hash = 0x301 // u
|
U Hash = 0x301 // u
|
||||||
Ul Hash = 0x8302 // ul
|
Ul Hash = 0xef02 // ul
|
||||||
Undeterminate Hash = 0x370d // undeterminate
|
Undeterminate Hash = 0x370d // undeterminate
|
||||||
Usemap Hash = 0xeb06 // usemap
|
Usemap Hash = 0xd306 // usemap
|
||||||
Valign Hash = 0x32606 // valign
|
Valign Hash = 0x35a06 // valign
|
||||||
Value Hash = 0x18905 // value
|
Value Hash = 0x1a605 // value
|
||||||
Valuetype Hash = 0x18909 // valuetype
|
Valuetype Hash = 0x1a609 // valuetype
|
||||||
Var Hash = 0x28003 // var
|
Var Hash = 0x27703 // var
|
||||||
Video Hash = 0x35205 // video
|
Video Hash = 0x39505 // video
|
||||||
Visible Hash = 0x36b07 // visible
|
Visible Hash = 0x3a907 // visible
|
||||||
Vlink Hash = 0x37205 // vlink
|
Vlink Hash = 0x3b005 // vlink
|
||||||
Vocab Hash = 0x37705 // vocab
|
Vocab Hash = 0x3b505 // vocab
|
||||||
Wbr Hash = 0x37e03 // wbr
|
Wbr Hash = 0x3bc03 // wbr
|
||||||
Xmlns Hash = 0x2eb05 // xmlns
|
Xmlns Hash = 0x2db05 // xmlns
|
||||||
Xmp Hash = 0x36203 // xmp
|
Xmp Hash = 0x38a03 // xmp
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns the hash' name.
|
// String returns the hash' name.
|
||||||
|
@ -288,256 +304,273 @@ NEXT:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const _Hash_hash0 = 0x9acb0442
|
const _Hash_hash0 = 0x67ac9bb5
|
||||||
const _Hash_maxLen = 15
|
const _Hash_maxLen = 15
|
||||||
const _Hash_text = "aboutputargetbodyaccept-charsetdatalistarticlearbackgroundet" +
|
const _Hash_text = "aboutputargetbodyaccept-charsetdatalistarticlearbackgroundet" +
|
||||||
"erminatemplatembedatatypeofaceaddressamp-boilerplatealinkbdi" +
|
"erminatemplateacronymainertcanvasideclarelabelongdescrolling" +
|
||||||
"reversedefaultMutedefaultSelectedeferowspanamedialogasyncanv" +
|
"addressamp-boilerplateasynciteaudiobjectfootereversedatatype" +
|
||||||
"asideclarelabelongdescrollingaudiobjectfooterubyautofocusema" +
|
"ofacenterowspanamedialogautofocusemappletimenuitemscopedefau" +
|
||||||
"paramainertcitemscopedelautoplayaxismapauseonexitimenubgcolo" +
|
"ltMutedefaultSelectedeferubyautoplayaxismaparamanifestrongbg" +
|
||||||
"rulesectionoresizeblockquotebuttonoscriptitleclassidetailsel" +
|
"colorbigblockquotebuttonoframesetitleclassidetailselectedfno" +
|
||||||
"ectedfnoshadefaultCheckedisabledivaluetypecodebaseamlesshape" +
|
"hreflanguagecodebasefontrackbdircodetypemustmatcheckedisable" +
|
||||||
"codetypemustmatcheckedlcolgroupicturesourcecolspanowraplaint" +
|
"divaluetypecolgroupauseonexitranslatecolspanoresizecompactru" +
|
||||||
"extareadonlycompactrackeygenctypecontrolslotranslatefieldset" +
|
"espeedlcontrolshapefieldsetfigcaptionoscriptfigurequiredtfor" +
|
||||||
"figcaptionfigurequiredtruespeedformactionformnovalidateh2h3h" +
|
"mactionoshadefaultCheckedformnovalidateh2h3h4h5h6hgroupictur" +
|
||||||
"4h5h6hgrouposterhiddenabledhtmlhttp-equivaricontentiframebor" +
|
"esourcehiddenabledhtmlhttp-equivaricontentiframeborderimagei" +
|
||||||
"derimginlistronginputinsmallowfullscreenmanifestylegendmarkm" +
|
"mginlistrikeygenctypeinputinslotmarkmarqueematheadermaxlengt" +
|
||||||
"atheadermaxlength1metabindexmlnsortablemetermethodmultiplemu" +
|
"h1metabindexmlnsmallowfullscreenowraplaintextareadonlymeterm" +
|
||||||
"tedoptionpropertysubdopenavalignohreflanguagesummarysuprogre" +
|
"ethodmultiplegendmutedoptionprofileprogressortablepropertysr" +
|
||||||
"ssrcsetsvgvideoptgrouprefixmprofilevisiblevlinkvocabbrwbr"
|
"csetstylesubdopenavalignobruleseamlessectionoembedelsummarys" +
|
||||||
|
"uprefixmpostersvgvideoptgrouportalinkvisiblevlinkvocabbrwbr"
|
||||||
|
|
||||||
var _Hash_table = [1 << 9]Hash{
|
var _Hash_table = [1 << 9]Hash{
|
||||||
0x0: 0x1df08, // textarea
|
0x1: 0x13e06, // button
|
||||||
0x4: 0x32d02, // hr
|
0x3: 0x2a207, // enctype
|
||||||
0x8: 0x1c207, // picture
|
0x4: 0x32206, // option
|
||||||
0xb: 0x18905, // value
|
0x5: 0x1fb0a, // figcaption
|
||||||
0xf: 0x2e408, // tabindex
|
0x7: 0x2ae03, // ins
|
||||||
0x12: 0x15905, // class
|
0x9: 0x9605, // audio
|
||||||
0x15: 0x37e03, // wbr
|
0xb: 0x2830b, // frameborder
|
||||||
0x18: 0x1a80d, // typemustmatch
|
0xd: 0x2190a, // formaction
|
||||||
0x1a: 0x1b902, // dl
|
0xe: 0x5, // about
|
||||||
0x1d: 0xf903, // rtc
|
0xf: 0x34606, // srcset
|
||||||
0x1e: 0x25702, // h4
|
0x10: 0x1dd09, // truespeed
|
||||||
0x22: 0x2ef08, // sortable
|
0x11: 0xeb0c, // defaultMuted
|
||||||
0x24: 0x4208, // template
|
0x13: 0xa006, // footer
|
||||||
0x25: 0x28c0b, // frameborder
|
0x15: 0x19d08, // disabled
|
||||||
0x28: 0x37a04, // abbr
|
0x16: 0x26e0a, // http-equiv
|
||||||
0x29: 0x28b06, // iframe
|
0x19: 0x3a504, // link
|
||||||
0x2a: 0x610f, // amp-boilerplate
|
0x1a: 0x29606, // inlist
|
||||||
0x2c: 0x1e408, // readonly
|
0x1d: 0x10804, // ruby
|
||||||
0x30: 0x23f06, // action
|
0x21: 0x2a905, // input
|
||||||
0x33: 0x28c05, // frame
|
0x22: 0x35803, // nav
|
||||||
0x35: 0x12c05, // rules
|
0x25: 0x7902, // dd
|
||||||
0x36: 0x30208, // multiple
|
0x26: 0x2350e, // formnovalidate
|
||||||
0x38: 0x31f03, // bdo
|
0x28: 0x16804, // href
|
||||||
0x39: 0x1d506, // nowrap
|
0x29: 0x24702, // h4
|
||||||
0x3e: 0x21408, // fieldset
|
0x2b: 0x10405, // defer
|
||||||
0x3f: 0x7503, // bdi
|
0x2d: 0x1f308, // fieldset
|
||||||
0x46: 0x7f0c, // defaultMuted
|
0x2e: 0xeb07, // default
|
||||||
0x49: 0x35205, // video
|
0x34: 0x2fd04, // area
|
||||||
0x4c: 0x19808, // seamless
|
0x36: 0xb006, // typeof
|
||||||
0x4d: 0x13608, // noresize
|
0x37: 0x37307, // noembed
|
||||||
0x4f: 0xb602, // id
|
0x38: 0x5e07, // declare
|
||||||
0x51: 0x25d06, // hgroup
|
0x3a: 0x4a07, // acronym
|
||||||
0x52: 0x23102, // dt
|
0x3b: 0xc05, // tbody
|
||||||
0x55: 0x12805, // color
|
0x3e: 0x15107, // classid
|
||||||
0x56: 0x34003, // sup
|
0x41: 0x9a06, // object
|
||||||
0x59: 0x370d, // undeterminate
|
0x43: 0x16403, // dfn
|
||||||
0x5a: 0x35608, // optgroup
|
0x44: 0xef02, // ul
|
||||||
0x5b: 0x2d206, // header
|
0x45: 0x16c04, // lang
|
||||||
0x5c: 0xb405, // aside
|
0x47: 0x16606, // nohref
|
||||||
0x5f: 0x10005, // scope
|
0x49: 0x2c803, // max
|
||||||
0x60: 0x101, // b
|
0x4a: 0x6505, // label
|
||||||
0x61: 0xcb02, // ol
|
0x4c: 0x1d304, // size
|
||||||
0x64: 0x32b06, // nohref
|
0x4d: 0xe606, // scoped
|
||||||
0x65: 0x1da09, // plaintext
|
0x4f: 0x15105, // class
|
||||||
0x66: 0x20804, // slot
|
0x50: 0x11404, // axis
|
||||||
0x67: 0x11004, // axis
|
0x54: 0xbf04, // span
|
||||||
0x68: 0x12803, // col
|
0x56: 0x19707, // checked
|
||||||
0x69: 0x32606, // valign
|
0x59: 0x38506, // prefix
|
||||||
0x6c: 0x2d105, // thead
|
0x5b: 0x4208, // template
|
||||||
0x70: 0x34906, // srcset
|
0x5c: 0x370d, // undeterminate
|
||||||
0x71: 0x26806, // hidden
|
0x5d: 0xc606, // dialog
|
||||||
0x76: 0x1bb08, // colgroup
|
0x5e: 0x6908, // longdesc
|
||||||
0x78: 0x34f03, // svg
|
0x60: 0x21903, // for
|
||||||
0x7b: 0x2cb04, // mark
|
0x61: 0x2c102, // th
|
||||||
0x7e: 0x33104, // lang
|
0x64: 0x15d08, // selected
|
||||||
0x81: 0x1cf04, // cols
|
0x65: 0x35103, // sub
|
||||||
0x86: 0x5a07, // address
|
0x6a: 0xd306, // usemap
|
||||||
0x8b: 0xf404, // main
|
0x6e: 0x24d06, // hgroup
|
||||||
0x8c: 0x4302, // em
|
0x6f: 0x38303, // sup
|
||||||
0x8f: 0x32d08, // hreflang
|
0x70: 0x2b404, // mark
|
||||||
0x93: 0x1b307, // checked
|
0x71: 0x28206, // iframe
|
||||||
0x94: 0x25902, // h5
|
0x72: 0x30605, // meter
|
||||||
0x95: 0x301, // u
|
0x74: 0x21008, // required
|
||||||
0x96: 0x32705, // align
|
0x75: 0x1f04, // data
|
||||||
0x97: 0x14301, // q
|
0x78: 0x14308, // noframes
|
||||||
0x99: 0xd506, // object
|
0x83: 0x7807, // address
|
||||||
0x9b: 0x28407, // content
|
0x88: 0x10c08, // autoplay
|
||||||
0x9d: 0xc809, // scrolling
|
0x8a: 0x28e05, // image
|
||||||
0x9f: 0x36407, // profile
|
0x8b: 0x16c08, // language
|
||||||
0xa0: 0x34903, // src
|
0x8e: 0x2f904, // text
|
||||||
0xa1: 0xda05, // tfoot
|
0x8f: 0x16802, // hr
|
||||||
0xa3: 0x2f705, // meter
|
0x90: 0x5d02, // id
|
||||||
0xa4: 0x37705, // vocab
|
0x92: 0x31108, // multiple
|
||||||
0xa6: 0xd04, // body
|
0x94: 0x16808, // hreflang
|
||||||
0xa8: 0x19204, // code
|
0x95: 0x2db05, // xmlns
|
||||||
0xac: 0x20108, // controls
|
0x96: 0x24902, // h5
|
||||||
0xb0: 0x2ab05, // small
|
0x98: 0x25207, // picture
|
||||||
0xb1: 0x18008, // disabled
|
0x99: 0x1106, // accept
|
||||||
0xb5: 0x5604, // face
|
0x9a: 0x1a609, // valuetype
|
||||||
0xb6: 0x501, // p
|
0x9b: 0x3a006, // portal
|
||||||
0xb9: 0x2302, // li
|
0x9d: 0xac08, // datatype
|
||||||
0xbb: 0xe409, // autofocus
|
0x9e: 0x18403, // bdi
|
||||||
0xbf: 0x27304, // html
|
0xa0: 0x27a04, // icon
|
||||||
0xc2: 0x4d08, // datatype
|
0xa2: 0xa503, // rev
|
||||||
0xc6: 0x35d06, // prefix
|
0xa5: 0x25708, // resource
|
||||||
0xcb: 0x35d03, // pre
|
0xa8: 0x35504, // open
|
||||||
0xcc: 0x1106, // accept
|
0xac: 0x4302, // em
|
||||||
0xd1: 0x23b03, // for
|
0xae: 0x1340a, // blockquote
|
||||||
0xd5: 0x29e06, // strong
|
0xb0: 0x2f409, // plaintext
|
||||||
0xd6: 0x9c07, // rowspan
|
0xb1: 0x2d204, // meta
|
||||||
0xd7: 0x25502, // h3
|
0xb2: 0x1c01, // s
|
||||||
0xd8: 0x2cf04, // math
|
0xb4: 0xdc04, // time
|
||||||
0xde: 0x16e07, // noshade
|
0xb5: 0x1fe07, // caption
|
||||||
0xdf: 0x19f05, // shape
|
0xb8: 0x33e08, // property
|
||||||
0xe1: 0x10006, // scoped
|
0xb9: 0x1, // a
|
||||||
0xe3: 0x706, // target
|
0xbb: 0x2b807, // marquee
|
||||||
0xe6: 0x21c0a, // figcaption
|
0xbc: 0x3b505, // vocab
|
||||||
0xe9: 0x1df04, // text
|
0xbd: 0x1e502, // dl
|
||||||
0xea: 0x1c708, // resource
|
0xbf: 0xbc07, // rowspan
|
||||||
0xec: 0xee03, // map
|
0xc4: 0x18503, // dir
|
||||||
0xf0: 0x29a06, // inlist
|
0xc5: 0x39908, // optgroup
|
||||||
0xf1: 0x16506, // select
|
0xcc: 0x38c06, // poster
|
||||||
0xf2: 0x1f606, // keygen
|
0xcd: 0x24502, // h3
|
||||||
0xf3: 0x5106, // typeof
|
0xce: 0x3b804, // abbr
|
||||||
0xf6: 0xb006, // canvas
|
0xd1: 0x17408, // codebase
|
||||||
0xf7: 0x30f06, // option
|
0xd2: 0x27b07, // content
|
||||||
0xf8: 0xbe05, // label
|
0xd4: 0x7e04, // samp
|
||||||
0xf9: 0xbc03, // rel
|
0xd6: 0xc204, // name
|
||||||
0xfb: 0x1f04, // data
|
0xd9: 0x14c05, // title
|
||||||
0xfd: 0x6004, // samp
|
0xda: 0x1a605, // value
|
||||||
0x100: 0x110e, // accept-charset
|
0xdd: 0xb004, // type
|
||||||
0x101: 0xeb06, // usemap
|
0xde: 0x35f04, // nobr
|
||||||
0x103: 0x2bc08, // manifest
|
0xe0: 0x17c04, // font
|
||||||
0x109: 0xa204, // name
|
0xe1: 0xd603, // map
|
||||||
0x10a: 0x14806, // button
|
0xe2: 0x2d002, // h1
|
||||||
0x10b: 0x2b05, // clear
|
0xe3: 0x22207, // noshade
|
||||||
0x10e: 0x33907, // summary
|
0xe4: 0x6303, // rel
|
||||||
0x10f: 0x2e204, // meta
|
0xe5: 0x14002, // tt
|
||||||
0x110: 0x33108, // language
|
0xe7: 0xde04, // menu
|
||||||
0x112: 0x300a, // background
|
0xeb: 0x2f908, // textarea
|
||||||
0x113: 0x2707, // article
|
0xee: 0x35b05, // align
|
||||||
0x116: 0x23b0a, // formaction
|
0xf1: 0x29303, // img
|
||||||
0x119: 0x1, // a
|
0xf2: 0x35a06, // valign
|
||||||
0x11b: 0x5, // about
|
0xf3: 0x2c204, // head
|
||||||
0x11c: 0xfc09, // itemscope
|
0xf4: 0x12a07, // bgcolor
|
||||||
0x11e: 0x14d08, // noscript
|
0xf5: 0x5004, // main
|
||||||
0x11f: 0x15907, // classid
|
0xf6: 0x2302, // li
|
||||||
0x120: 0x36203, // xmp
|
0xf7: 0x5205, // inert
|
||||||
0x121: 0x19604, // base
|
0xfa: 0x5706, // canvas
|
||||||
0x123: 0x1c01, // s
|
0xfb: 0xe605, // scope
|
||||||
0x124: 0x36b07, // visible
|
0xfc: 0x15d06, // select
|
||||||
0x126: 0x37b02, // bb
|
0x100: 0xa508, // reversed
|
||||||
0x127: 0x9c04, // rows
|
0x101: 0x20408, // noscript
|
||||||
0x12d: 0x2450e, // formnovalidate
|
0x102: 0x37c07, // summary
|
||||||
0x131: 0x1f205, // track
|
0x103: 0x24b02, // h6
|
||||||
0x135: 0x18703, // div
|
0x106: 0x17404, // code
|
||||||
0x136: 0xac05, // async
|
0x107: 0x14508, // frameset
|
||||||
0x137: 0x31508, // property
|
0x10a: 0x12406, // strong
|
||||||
0x13a: 0x16c03, // dfn
|
0x10d: 0x300a, // background
|
||||||
0x13e: 0xf605, // inert
|
0x10e: 0x18303, // kbd
|
||||||
0x142: 0x10503, // del
|
0x114: 0x31706, // legend
|
||||||
0x144: 0x25302, // h2
|
0x116: 0x32f08, // progress
|
||||||
0x147: 0x2c205, // style
|
0x118: 0x2d408, // tabindex
|
||||||
0x149: 0x29703, // img
|
0x119: 0x34603, // src
|
||||||
0x14a: 0xc05, // tbody
|
0x11c: 0x39505, // video
|
||||||
0x14b: 0x7603, // dir
|
0x11f: 0x29a06, // strike
|
||||||
0x14c: 0x2eb05, // xmlns
|
0x121: 0xd706, // applet
|
||||||
0x14e: 0x1f08, // datalist
|
0x123: 0x2802, // rt
|
||||||
0x14f: 0x32d04, // href
|
0x125: 0x20606, // script
|
||||||
0x150: 0x1f202, // tr
|
0x128: 0xbc04, // rows
|
||||||
0x151: 0x13e0a, // blockquote
|
0x129: 0x2707, // article
|
||||||
0x152: 0x18909, // valuetype
|
0x12e: 0x9204, // cite
|
||||||
0x155: 0xdb06, // footer
|
0x131: 0x18c0d, // typemustmatch
|
||||||
0x157: 0x14f06, // script
|
0x133: 0x17f05, // track
|
||||||
0x158: 0x1cf07, // colspan
|
0x135: 0x3b902, // bb
|
||||||
0x15d: 0x1730e, // defaultChecked
|
0x136: 0x1ee05, // shape
|
||||||
0x15f: 0x2490a, // novalidate
|
0x137: 0x5b05, // aside
|
||||||
0x164: 0x1a408, // codetype
|
0x138: 0x1b60b, // pauseonexit
|
||||||
0x165: 0x2c506, // legend
|
0x13c: 0x38503, // pre
|
||||||
0x16b: 0x1160b, // pauseonexit
|
0x140: 0x301, // u
|
||||||
0x16c: 0x21f07, // caption
|
0x149: 0x1a403, // div
|
||||||
0x16f: 0x26c07, // enabled
|
0x14c: 0x3a405, // alink
|
||||||
0x173: 0x26206, // poster
|
0x14e: 0x27703, // var
|
||||||
0x175: 0x30a05, // muted
|
0x14f: 0x21d06, // action
|
||||||
0x176: 0x11205, // ismap
|
0x152: 0x2b05, // clear
|
||||||
0x178: 0x2a903, // ins
|
0x154: 0x2401, // i
|
||||||
0x17a: 0xe004, // ruby
|
0x155: 0x21702, // dt
|
||||||
0x17b: 0x37c02, // br
|
0x156: 0x36608, // seamless
|
||||||
0x17c: 0x8a0f, // defaultSelected
|
0x157: 0x21904, // form
|
||||||
0x17d: 0x7403, // kbd
|
0x15b: 0x15707, // details
|
||||||
0x17f: 0x1c906, // source
|
0x15f: 0x8e05, // async
|
||||||
0x182: 0x9f04, // span
|
0x160: 0x26a04, // html
|
||||||
0x184: 0x2d803, // max
|
0x161: 0x33608, // sortable
|
||||||
0x18a: 0x5b02, // dd
|
0x165: 0x2f02, // rb
|
||||||
0x18b: 0x13a04, // size
|
0x167: 0x2e10f, // allowfullscreen
|
||||||
0x18c: 0xa405, // media
|
0x168: 0x17804, // base
|
||||||
0x18d: 0x19208, // codebase
|
0x169: 0x25f06, // hidden
|
||||||
0x18f: 0x4905, // embed
|
0x16e: 0x2ef06, // nowrap
|
||||||
0x192: 0x5104, // type
|
0x16f: 0x2505, // start
|
||||||
0x193: 0xf005, // param
|
0x170: 0x14505, // frame
|
||||||
0x194: 0x25b02, // h6
|
0x171: 0x1f08, // datalist
|
||||||
0x197: 0x28304, // icon
|
0x173: 0x12502, // tr
|
||||||
0x198: 0x12607, // bgcolor
|
0x174: 0x30b06, // method
|
||||||
0x199: 0x2ad0f, // allowfullscreen
|
0x175: 0x101, // b
|
||||||
0x19a: 0x12004, // time
|
0x176: 0x1c904, // cols
|
||||||
0x19b: 0x7803, // rev
|
0x178: 0x110e, // accept-charset
|
||||||
0x19d: 0x34208, // progress
|
0x17a: 0x36205, // rules
|
||||||
0x19e: 0x22606, // figure
|
0x17b: 0x7f0f, // amp-boilerplate
|
||||||
0x1a0: 0x6a02, // rp
|
0x17f: 0x2270e, // defaultChecked
|
||||||
0x1a2: 0xa606, // dialog
|
0x180: 0x32807, // profile
|
||||||
0x1a4: 0x2802, // rt
|
0x181: 0x2b004, // slot
|
||||||
0x1a7: 0x1e304, // area
|
0x182: 0x11a05, // param
|
||||||
0x1a8: 0x7808, // reversed
|
0x185: 0x1c907, // colspan
|
||||||
0x1aa: 0x32104, // open
|
0x186: 0x34c05, // style
|
||||||
0x1ac: 0x2d204, // head
|
0x187: 0x1e02, // td
|
||||||
0x1ad: 0x7005, // alink
|
0x188: 0x12c05, // color
|
||||||
0x1af: 0x28003, // var
|
0x18c: 0x13901, // q
|
||||||
0x1b0: 0x15f07, // details
|
0x18d: 0x3b005, // vlink
|
||||||
0x1b1: 0x2401, // i
|
0x18e: 0x39203, // svg
|
||||||
0x1b3: 0x1e02, // td
|
0x18f: 0x33905, // table
|
||||||
0x1b4: 0xb707, // declare
|
0x190: 0x29e06, // keygen
|
||||||
0x1b5: 0x8302, // ul
|
0x192: 0x20c06, // figure
|
||||||
0x1ba: 0x2fc06, // method
|
0x193: 0x3a907, // visible
|
||||||
0x1bd: 0x13007, // section
|
0x195: 0x17808, // basefont
|
||||||
0x1be: 0x22a08, // required
|
0x196: 0x8802, // rp
|
||||||
0x1c2: 0x9805, // defer
|
0x197: 0xf60f, // defaultSelected
|
||||||
0x1c3: 0x37205, // vlink
|
0x198: 0x1af08, // colgroup
|
||||||
0x1c4: 0x15405, // title
|
0x19a: 0x3bc03, // wbr
|
||||||
0x1c5: 0x2770a, // http-equiv
|
0x19c: 0x36d07, // section
|
||||||
0x1c6: 0x1fa07, // enctype
|
0x19d: 0x25906, // source
|
||||||
0x1c7: 0x1ec07, // compact
|
0x19f: 0x2bf04, // math
|
||||||
0x1c8: 0x2d809, // maxlength
|
0x1a1: 0x2fe08, // readonly
|
||||||
0x1c9: 0x16508, // selected
|
0x1a7: 0x1e708, // controls
|
||||||
0x1cc: 0xd105, // audio
|
0x1a9: 0xde08, // menuitem
|
||||||
0x1cd: 0xc208, // longdesc
|
0x1ad: 0x206, // output
|
||||||
0x1d1: 0xfb04, // cite
|
0x1b0: 0x2c809, // maxlength
|
||||||
0x1da: 0x2505, // start
|
0x1b2: 0xe209, // itemscope
|
||||||
0x1de: 0x2d102, // th
|
0x1b9: 0x501, // p
|
||||||
0x1df: 0x10808, // autoplay
|
0x1bc: 0x2df05, // small
|
||||||
0x1e2: 0x7104, // link
|
0x1bd: 0x36102, // br
|
||||||
0x1e3: 0x206, // output
|
0x1c0: 0x5503, // rtc
|
||||||
0x1e5: 0x12204, // menu
|
0x1c1: 0x1c009, // translate
|
||||||
0x1e6: 0x2a405, // input
|
0x1c4: 0x35303, // bdo
|
||||||
0x1eb: 0x32403, // nav
|
0x1c5: 0xd04, // body
|
||||||
0x1ec: 0x31d03, // sub
|
0x1c8: 0xb706, // center
|
||||||
0x1ee: 0x1807, // charset
|
0x1c9: 0x2c105, // thead
|
||||||
0x1ef: 0x7f07, // default
|
0x1ca: 0xcc09, // autofocus
|
||||||
0x1f3: 0x2f205, // table
|
0x1cc: 0xb504, // face
|
||||||
0x1f4: 0x23b04, // form
|
0x1cd: 0x24302, // h2
|
||||||
0x1f5: 0x23209, // truespeed
|
0x1ce: 0x11e08, // manifest
|
||||||
0x1f6: 0x2f02, // rb
|
0x1d0: 0x706, // target
|
||||||
0x1fb: 0x20b09, // translate
|
0x1d1: 0x11605, // ismap
|
||||||
0x1fd: 0x2e002, // h1
|
0x1d3: 0xc405, // media
|
||||||
|
0x1d7: 0x13103, // big
|
||||||
|
0x1da: 0x37903, // del
|
||||||
|
0x1dc: 0x6f09, // scrolling
|
||||||
|
0x1de: 0x37505, // embed
|
||||||
|
0x1e0: 0x31d05, // muted
|
||||||
|
0x1e4: 0x2390a, // novalidate
|
||||||
|
0x1e6: 0x7202, // ol
|
||||||
|
0x1eb: 0x9f05, // tfoot
|
||||||
|
0x1ec: 0x18808, // codetype
|
||||||
|
0x1ee: 0x26307, // enabled
|
||||||
|
0x1f0: 0x2c206, // header
|
||||||
|
0x1f1: 0x1cf08, // noresize
|
||||||
|
0x1f6: 0x1d707, // compact
|
||||||
|
0x1f9: 0x12c03, // col
|
||||||
|
0x1fa: 0x38a03, // xmp
|
||||||
|
0x1fb: 0x1807, // charset
|
||||||
}
|
}
|
||||||
|
|
41
vendor/github.com/tdewolff/minify/v2/html/html.go
generated
vendored
41
vendor/github.com/tdewolff/minify/v2/html/html.go
generated
vendored
|
@ -129,7 +129,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
var params map[string]string
|
var params map[string]string
|
||||||
if rawTagHash == Iframe {
|
if rawTagHash == Iframe {
|
||||||
mimetype = htmlMimeBytes
|
mimetype = htmlMimeBytes
|
||||||
} else if len(rawTagMediatype) > 0 {
|
} else if 0 < len(rawTagMediatype) {
|
||||||
mimetype, params = parse.Mediatype(rawTagMediatype)
|
mimetype, params = parse.Mediatype(rawTagMediatype)
|
||||||
} else if rawTagHash == Script {
|
} else if rawTagHash == Script {
|
||||||
mimetype = jsMimeBytes
|
mimetype = jsMimeBytes
|
||||||
|
@ -169,20 +169,15 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
t.Data = t.Data[:len(t.Data)-1]
|
t.Data = t.Data[:len(t.Data)-1]
|
||||||
omitSpace = false
|
omitSpace = false
|
||||||
break
|
break
|
||||||
} else if next.TokenType == html.TextToken {
|
} else if next.TokenType == html.TextToken && !parse.IsAllWhitespace(next.Data) {
|
||||||
// this only happens when a comment, doctype or phrasing end tag (only for !o.KeepWhitespace) was in between
|
// stop looking when text encountered
|
||||||
// remove if the text token starts with a whitespace
|
|
||||||
if len(next.Data) > 0 && parse.IsWhitespace(next.Data[0]) {
|
|
||||||
t.Data = t.Data[:len(t.Data)-1]
|
|
||||||
omitSpace = false
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
} else if next.TokenType == html.StartTagToken || next.TokenType == html.EndTagToken {
|
} else if next.TokenType == html.StartTagToken || next.TokenType == html.EndTagToken {
|
||||||
if o.KeepWhitespace {
|
if o.KeepWhitespace {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// remove when followed up by a block tag
|
// remove when followed by a block tag
|
||||||
if next.Traits&nonPhrasingTag != 0 {
|
if next.Traits&blockTag != 0 {
|
||||||
t.Data = t.Data[:len(t.Data)-1]
|
t.Data = t.Data[:len(t.Data)-1]
|
||||||
omitSpace = false
|
omitSpace = false
|
||||||
break
|
break
|
||||||
|
@ -271,14 +266,14 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Traits&nonPhrasingTag != 0 {
|
|
||||||
omitSpace = true // omit spaces after block elements
|
|
||||||
} else if o.KeepWhitespace || t.Traits&objectTag != 0 {
|
|
||||||
omitSpace = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !omitEndTag {
|
if !omitEndTag {
|
||||||
if len(t.Data) > 3+len(t.Text) {
|
if o.KeepWhitespace || t.Traits&objectTag != 0 {
|
||||||
|
omitSpace = false
|
||||||
|
} else if t.Traits&blockTag != 0 {
|
||||||
|
omitSpace = true // omit spaces after block elements
|
||||||
|
}
|
||||||
|
|
||||||
|
if 3+len(t.Text) < len(t.Data) {
|
||||||
t.Data[2+len(t.Text)] = '>'
|
t.Data[2+len(t.Text)] = '>'
|
||||||
t.Data = t.Data[:3+len(t.Text)]
|
t.Data = t.Data[:3+len(t.Text)]
|
||||||
}
|
}
|
||||||
|
@ -296,7 +291,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
|
|
||||||
if o.KeepWhitespace || t.Traits&objectTag != 0 {
|
if o.KeepWhitespace || t.Traits&objectTag != 0 {
|
||||||
omitSpace = false
|
omitSpace = false
|
||||||
} else if t.Traits&nonPhrasingTag != 0 {
|
} else if t.Traits&blockTag != 0 {
|
||||||
omitSpace = true // omit spaces after block elements
|
omitSpace = true // omit spaces after block elements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +322,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
for i := 0; i < len(content.AttrVal); i++ {
|
for i := 0; i < len(content.AttrVal); i++ {
|
||||||
if content.AttrVal[i] == '=' && i+2 < len(content.AttrVal) {
|
if content.AttrVal[i] == '=' && i+2 < len(content.AttrVal) {
|
||||||
i++
|
i++
|
||||||
if n := parse.Number(content.AttrVal[i:]); n > 0 {
|
if n := parse.Number(content.AttrVal[i:]); 0 < n {
|
||||||
minNum := minify.Number(content.AttrVal[i:i+n], -1)
|
minNum := minify.Number(content.AttrVal[i:i+n], -1)
|
||||||
if len(minNum) < n {
|
if len(minNum) < n {
|
||||||
copy(content.AttrVal[i:i+len(minNum)], minNum)
|
copy(content.AttrVal[i:i+len(minNum)], minNum)
|
||||||
|
@ -434,10 +429,10 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
if len(val) == 0 {
|
if len(val) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if len(attr.Text) > 2 && attr.Text[0] == 'o' && attr.Text[1] == 'n' {
|
} else if 2 < len(attr.Text) && attr.Text[0] == 'o' && attr.Text[1] == 'n' {
|
||||||
// JS minifier for attribute inline code
|
// JS minifier for attribute inline code
|
||||||
val = parse.TrimWhitespace(val)
|
val = parse.TrimWhitespace(val)
|
||||||
if len(val) >= 11 && parse.EqualFold(val[:11], jsSchemeBytes) {
|
if 11 <= len(val) && parse.EqualFold(val[:11], jsSchemeBytes) {
|
||||||
val = val[11:]
|
val = val[11:]
|
||||||
}
|
}
|
||||||
attrMinifyBuffer.Reset()
|
attrMinifyBuffer.Reset()
|
||||||
|
@ -475,7 +470,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
|
|
||||||
w.Write(spaceBytes)
|
w.Write(spaceBytes)
|
||||||
w.Write(attr.Text)
|
w.Write(attr.Text)
|
||||||
if len(val) > 0 && attr.Traits&booleanAttr == 0 {
|
if 0 < len(val) && attr.Traits&booleanAttr == 0 {
|
||||||
w.Write(isBytes)
|
w.Write(isBytes)
|
||||||
|
|
||||||
// use double quotes for RDFa attributes
|
// use double quotes for RDFa attributes
|
||||||
|
@ -504,7 +499,7 @@ func (o *Minifier) Minify(m *minify.M, w io.Writer, r io.Reader, _ map[string]st
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep space after phrasing tags (<i>, <span>, ...) FontAwesome etc.
|
// keep space after phrasing tags (<i>, <span>, ...) FontAwesome etc.
|
||||||
if t.TokenType == html.StartTagToken && t.Traits&nonPhrasingTag == 0 {
|
if t.TokenType == html.StartTagToken && t.Traits == normalTag {
|
||||||
if next := tb.Peek(0); next.Hash == t.Hash && next.TokenType == html.EndTagToken {
|
if next := tb.Peek(0); next.Hash == t.Hash && next.TokenType == html.EndTagToken {
|
||||||
omitSpace = false
|
omitSpace = false
|
||||||
}
|
}
|
||||||
|
|
158
vendor/github.com/tdewolff/minify/v2/html/table.go
generated
vendored
158
vendor/github.com/tdewolff/minify/v2/html/table.go
generated
vendored
|
@ -3,12 +3,12 @@ package html
|
||||||
type traits uint16
|
type traits uint16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
normalTag traits = 1 << iota
|
normalTag traits = 1 << iota
|
||||||
rawTag // raw tags need special processing for their content
|
rawTag // raw tags need special processing for their content
|
||||||
nonPhrasingTag // non-phrasing elements are unaffected by whitespace, remove spaces around these tags
|
blockTag // remove spaces around these tags
|
||||||
objectTag // content tags with a few exclusions, keep spaces after these open/close tags
|
objectTag // keep spaces after these open/close tags
|
||||||
omitPTag // omit p end tag if it is followed by this start tag
|
omitPTag // omit p end tag if it is followed by this start tag
|
||||||
keepPTag // keep p end tag if it is followed by this end tag
|
keepPTag // keep p end tag if it is followed by this end tag
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -21,54 +21,54 @@ const (
|
||||||
var tagMap = map[Hash]traits{
|
var tagMap = map[Hash]traits{
|
||||||
A: keepPTag,
|
A: keepPTag,
|
||||||
Abbr: normalTag,
|
Abbr: normalTag,
|
||||||
Address: nonPhrasingTag | omitPTag,
|
Address: blockTag | omitPTag,
|
||||||
Area: normalTag,
|
Area: normalTag,
|
||||||
Article: nonPhrasingTag | omitPTag,
|
Article: blockTag | omitPTag,
|
||||||
Aside: nonPhrasingTag | omitPTag,
|
Aside: blockTag | omitPTag,
|
||||||
Audio: keepPTag,
|
Audio: keepPTag,
|
||||||
B: normalTag,
|
B: normalTag,
|
||||||
Base: normalTag,
|
Base: normalTag,
|
||||||
Bb: normalTag,
|
Bb: normalTag,
|
||||||
Bdi: normalTag,
|
Bdi: normalTag,
|
||||||
Bdo: normalTag,
|
Bdo: normalTag,
|
||||||
Blockquote: nonPhrasingTag | omitPTag,
|
Blockquote: blockTag | omitPTag,
|
||||||
Body: nonPhrasingTag,
|
Body: normalTag,
|
||||||
Br: nonPhrasingTag,
|
Br: blockTag,
|
||||||
Button: objectTag,
|
Button: objectTag,
|
||||||
Canvas: objectTag | keepPTag,
|
Canvas: objectTag | keepPTag,
|
||||||
Caption: nonPhrasingTag,
|
Caption: blockTag,
|
||||||
Cite: normalTag,
|
Cite: normalTag,
|
||||||
Code: normalTag,
|
Code: normalTag,
|
||||||
Col: nonPhrasingTag,
|
Col: blockTag,
|
||||||
Colgroup: nonPhrasingTag,
|
Colgroup: blockTag,
|
||||||
Data: normalTag,
|
Data: normalTag,
|
||||||
Datalist: normalTag,
|
Datalist: normalTag, // no text content
|
||||||
Dd: nonPhrasingTag,
|
Dd: blockTag,
|
||||||
Del: keepPTag,
|
Del: keepPTag,
|
||||||
Details: omitPTag,
|
Details: blockTag | omitPTag,
|
||||||
Dfn: normalTag,
|
Dfn: normalTag,
|
||||||
Dialog: normalTag,
|
Dialog: normalTag,
|
||||||
Div: nonPhrasingTag | omitPTag,
|
Div: blockTag | omitPTag,
|
||||||
Dl: nonPhrasingTag | omitPTag,
|
Dl: blockTag | omitPTag,
|
||||||
Dt: nonPhrasingTag,
|
Dt: blockTag,
|
||||||
Em: normalTag,
|
Em: normalTag,
|
||||||
Embed: nonPhrasingTag,
|
Embed: normalTag,
|
||||||
Fieldset: nonPhrasingTag | omitPTag,
|
Fieldset: blockTag | omitPTag,
|
||||||
Figcaption: nonPhrasingTag | omitPTag,
|
Figcaption: blockTag | omitPTag,
|
||||||
Figure: nonPhrasingTag | omitPTag,
|
Figure: blockTag | omitPTag,
|
||||||
Footer: nonPhrasingTag | omitPTag,
|
Footer: blockTag | omitPTag,
|
||||||
Form: nonPhrasingTag | omitPTag,
|
Form: blockTag | omitPTag,
|
||||||
H1: nonPhrasingTag | omitPTag,
|
H1: blockTag | omitPTag,
|
||||||
H2: nonPhrasingTag | omitPTag,
|
H2: blockTag | omitPTag,
|
||||||
H3: nonPhrasingTag | omitPTag,
|
H3: blockTag | omitPTag,
|
||||||
H4: nonPhrasingTag | omitPTag,
|
H4: blockTag | omitPTag,
|
||||||
H5: nonPhrasingTag | omitPTag,
|
H5: blockTag | omitPTag,
|
||||||
H6: nonPhrasingTag | omitPTag,
|
H6: blockTag | omitPTag,
|
||||||
Head: nonPhrasingTag,
|
Head: blockTag,
|
||||||
Header: nonPhrasingTag | omitPTag,
|
Header: blockTag | omitPTag,
|
||||||
Hgroup: nonPhrasingTag,
|
Hgroup: blockTag,
|
||||||
Hr: nonPhrasingTag | omitPTag,
|
Hr: blockTag | omitPTag,
|
||||||
Html: nonPhrasingTag,
|
Html: blockTag,
|
||||||
I: normalTag,
|
I: normalTag,
|
||||||
Iframe: rawTag | objectTag,
|
Iframe: rawTag | objectTag,
|
||||||
Img: objectTag,
|
Img: objectTag,
|
||||||
|
@ -76,64 +76,90 @@ var tagMap = map[Hash]traits{
|
||||||
Ins: keepPTag,
|
Ins: keepPTag,
|
||||||
Kbd: normalTag,
|
Kbd: normalTag,
|
||||||
Label: normalTag,
|
Label: normalTag,
|
||||||
Legend: normalTag,
|
Legend: blockTag,
|
||||||
Li: nonPhrasingTag,
|
Li: blockTag,
|
||||||
Link: normalTag,
|
Link: normalTag,
|
||||||
Main: nonPhrasingTag | omitPTag,
|
Main: blockTag | omitPTag,
|
||||||
Map: keepPTag,
|
Map: keepPTag,
|
||||||
Mark: normalTag,
|
Mark: normalTag,
|
||||||
Math: rawTag,
|
Math: rawTag,
|
||||||
Menu: omitPTag,
|
Menu: blockTag | omitPTag,
|
||||||
Meta: nonPhrasingTag,
|
Meta: normalTag,
|
||||||
Meter: objectTag,
|
Meter: objectTag,
|
||||||
Nav: nonPhrasingTag | omitPTag,
|
Nav: blockTag | omitPTag,
|
||||||
Noscript: nonPhrasingTag | keepPTag,
|
Noscript: blockTag | keepPTag,
|
||||||
Object: objectTag,
|
Object: objectTag,
|
||||||
Ol: nonPhrasingTag | omitPTag,
|
Ol: blockTag | omitPTag,
|
||||||
Optgroup: normalTag,
|
Optgroup: normalTag, // no text content
|
||||||
Option: normalTag,
|
Option: blockTag,
|
||||||
Output: nonPhrasingTag,
|
Output: normalTag,
|
||||||
P: nonPhrasingTag | omitPTag,
|
P: blockTag | omitPTag,
|
||||||
Param: normalTag,
|
Param: normalTag,
|
||||||
Picture: normalTag,
|
Picture: normalTag,
|
||||||
Pre: nonPhrasingTag | omitPTag,
|
Pre: blockTag | omitPTag,
|
||||||
Progress: objectTag,
|
Progress: objectTag,
|
||||||
Q: objectTag,
|
Q: objectTag,
|
||||||
Rp: normalTag,
|
Rp: normalTag,
|
||||||
Rt: normalTag,
|
Rt: objectTag,
|
||||||
Ruby: normalTag,
|
Ruby: normalTag,
|
||||||
S: normalTag,
|
S: normalTag,
|
||||||
Samp: normalTag,
|
Samp: normalTag,
|
||||||
Script: rawTag,
|
Script: rawTag,
|
||||||
Section: nonPhrasingTag | omitPTag,
|
Section: blockTag | omitPTag,
|
||||||
Select: objectTag,
|
Select: objectTag,
|
||||||
Slot: normalTag,
|
Slot: normalTag,
|
||||||
Small: normalTag,
|
Small: normalTag,
|
||||||
Source: normalTag,
|
Source: normalTag,
|
||||||
Span: normalTag,
|
Span: normalTag,
|
||||||
Strong: normalTag,
|
Strong: normalTag,
|
||||||
Style: rawTag | nonPhrasingTag,
|
Style: rawTag | blockTag,
|
||||||
Sub: normalTag,
|
Sub: normalTag,
|
||||||
Summary: normalTag,
|
Summary: blockTag,
|
||||||
Sup: normalTag,
|
Sup: normalTag,
|
||||||
Svg: rawTag | objectTag,
|
Svg: rawTag | objectTag,
|
||||||
Table: nonPhrasingTag | omitPTag,
|
Table: blockTag | omitPTag,
|
||||||
Tbody: nonPhrasingTag,
|
Tbody: blockTag,
|
||||||
Td: nonPhrasingTag,
|
Td: blockTag,
|
||||||
Template: normalTag,
|
Template: normalTag,
|
||||||
Textarea: rawTag | objectTag,
|
Textarea: rawTag | objectTag,
|
||||||
Tfoot: nonPhrasingTag,
|
Tfoot: blockTag,
|
||||||
Th: nonPhrasingTag,
|
Th: blockTag,
|
||||||
Thead: nonPhrasingTag,
|
Thead: blockTag,
|
||||||
Time: normalTag,
|
Time: normalTag,
|
||||||
Title: nonPhrasingTag,
|
Title: normalTag,
|
||||||
Tr: nonPhrasingTag,
|
Tr: blockTag,
|
||||||
Track: normalTag,
|
Track: normalTag,
|
||||||
U: normalTag,
|
U: normalTag,
|
||||||
Ul: nonPhrasingTag | omitPTag,
|
Ul: blockTag | omitPTag,
|
||||||
Var: normalTag,
|
Var: normalTag,
|
||||||
Video: objectTag | keepPTag,
|
Video: objectTag | keepPTag,
|
||||||
Wbr: normalTag,
|
Wbr: objectTag,
|
||||||
|
|
||||||
|
// removed tags
|
||||||
|
Acronym: normalTag,
|
||||||
|
Applet: normalTag,
|
||||||
|
Basefont: normalTag,
|
||||||
|
Big: normalTag,
|
||||||
|
Center: blockTag,
|
||||||
|
Dir: blockTag,
|
||||||
|
Font: normalTag,
|
||||||
|
Frame: normalTag,
|
||||||
|
Frameset: normalTag,
|
||||||
|
Image: objectTag,
|
||||||
|
Marquee: blockTag,
|
||||||
|
Menuitem: normalTag,
|
||||||
|
Nobr: normalTag,
|
||||||
|
Noembed: blockTag,
|
||||||
|
Noframes: blockTag,
|
||||||
|
Plaintext: normalTag,
|
||||||
|
Rtc: objectTag,
|
||||||
|
Rb: normalTag,
|
||||||
|
Strike: normalTag,
|
||||||
|
Tt: normalTag,
|
||||||
|
Xmp: blockTag,
|
||||||
|
|
||||||
|
// experimental tags
|
||||||
|
Portal: normalTag,
|
||||||
}
|
}
|
||||||
|
|
||||||
var attrMap = map[Hash]traits{
|
var attrMap = map[Hash]traits{
|
||||||
|
@ -574,7 +600,7 @@ var EntitiesMap = map[string][]byte{
|
||||||
"SupersetEqual": []byte("⊇"),
|
"SupersetEqual": []byte("⊇"),
|
||||||
"Supset": []byte("⋑"),
|
"Supset": []byte("⋑"),
|
||||||
"THORN": []byte("Þ"),
|
"THORN": []byte("Þ"),
|
||||||
"Tab": []byte(" "),
|
"Tab": []byte("\t"),
|
||||||
"Tcaron": []byte("Ť"),
|
"Tcaron": []byte("Ť"),
|
||||||
"Tcedil": []byte("Ţ"),
|
"Tcedil": []byte("Ţ"),
|
||||||
"Therefore": []byte("∴"),
|
"Therefore": []byte("∴"),
|
||||||
|
|
63
vendor/github.com/tdewolff/parse/v2/strconv/decimal.go
generated
vendored
Normal file
63
vendor/github.com/tdewolff/parse/v2/strconv/decimal.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package strconv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseDecimal(b []byte) (float64, int) {
|
||||||
|
i := 0
|
||||||
|
start := i
|
||||||
|
dot := -1
|
||||||
|
trunk := -1
|
||||||
|
n := uint64(0)
|
||||||
|
for ; i < len(b); i++ {
|
||||||
|
c := b[i]
|
||||||
|
if '0' <= c && c <= '9' {
|
||||||
|
if trunk == -1 {
|
||||||
|
if math.MaxUint64/10 < n {
|
||||||
|
trunk = i
|
||||||
|
} else {
|
||||||
|
n *= 10
|
||||||
|
n += uint64(c - '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if dot == -1 && c == '.' {
|
||||||
|
dot = i
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == start || i == start+1 && dot == start {
|
||||||
|
return 0.0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
f := float64(n)
|
||||||
|
mantExp := int64(0)
|
||||||
|
if dot != -1 {
|
||||||
|
if trunk == -1 {
|
||||||
|
trunk = i
|
||||||
|
}
|
||||||
|
mantExp = int64(trunk - dot - 1)
|
||||||
|
} else if trunk != -1 {
|
||||||
|
mantExp = int64(trunk - i)
|
||||||
|
}
|
||||||
|
exp := -mantExp
|
||||||
|
|
||||||
|
// copied from strconv/atof.go
|
||||||
|
if exp == 0 {
|
||||||
|
return f, i
|
||||||
|
} else if 0 < exp && exp <= 15+22 { // int * 10^k
|
||||||
|
// If exponent is big but number of digits is not,
|
||||||
|
// can move a few zeros into the integer part.
|
||||||
|
if 22 < exp {
|
||||||
|
f *= float64pow10[exp-22]
|
||||||
|
exp = 22
|
||||||
|
}
|
||||||
|
if -1e15 <= f && f <= 1e15 {
|
||||||
|
return f * float64pow10[exp], i
|
||||||
|
}
|
||||||
|
} else if exp < 0 && -22 <= exp { // int / 10^k
|
||||||
|
return f / float64pow10[-exp], i
|
||||||
|
}
|
||||||
|
return f * math.Pow10(int(-mantExp)), i
|
||||||
|
}
|
30
vendor/github.com/tdewolff/parse/v2/strconv/float.go
generated
vendored
30
vendor/github.com/tdewolff/parse/v2/strconv/float.go
generated
vendored
|
@ -25,9 +25,9 @@ func ParseFloat(b []byte) (float64, int) {
|
||||||
n := uint64(0)
|
n := uint64(0)
|
||||||
for ; i < len(b); i++ {
|
for ; i < len(b); i++ {
|
||||||
c := b[i]
|
c := b[i]
|
||||||
if c >= '0' && c <= '9' {
|
if '0' <= c && c <= '9' {
|
||||||
if trunk == -1 {
|
if trunk == -1 {
|
||||||
if n > math.MaxUint64/10 {
|
if math.MaxUint64/10 < n {
|
||||||
trunk = i
|
trunk = i
|
||||||
} else {
|
} else {
|
||||||
n *= 10
|
n *= 10
|
||||||
|
@ -62,7 +62,7 @@ func ParseFloat(b []byte) (float64, int) {
|
||||||
if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
|
if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
|
||||||
startExp := i
|
startExp := i
|
||||||
i++
|
i++
|
||||||
if e, expLen := ParseInt(b[i:]); expLen > 0 {
|
if e, expLen := ParseInt(b[i:]); 0 < expLen {
|
||||||
expExp = e
|
expExp = e
|
||||||
i += expLen
|
i += expLen
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,17 +74,17 @@ func ParseFloat(b []byte) (float64, int) {
|
||||||
// copied from strconv/atof.go
|
// copied from strconv/atof.go
|
||||||
if exp == 0 {
|
if exp == 0 {
|
||||||
return f, i
|
return f, i
|
||||||
} else if exp > 0 && exp <= 15+22 { // int * 10^k
|
} else if 0 < exp && exp <= 15+22 { // int * 10^k
|
||||||
// If exponent is big but number of digits is not,
|
// If exponent is big but number of digits is not,
|
||||||
// can move a few zeros into the integer part.
|
// can move a few zeros into the integer part.
|
||||||
if exp > 22 {
|
if 22 < exp {
|
||||||
f *= float64pow10[exp-22]
|
f *= float64pow10[exp-22]
|
||||||
exp = 22
|
exp = 22
|
||||||
}
|
}
|
||||||
if f <= 1e15 && f >= -1e15 {
|
if -1e15 <= f && f <= 1e15 {
|
||||||
return f * float64pow10[exp], i
|
return f * float64pow10[exp], i
|
||||||
}
|
}
|
||||||
} else if exp < 0 && exp >= -22 { // int / 10^k
|
} else if -22 <= exp && exp < 0 { // int / 10^k
|
||||||
return f / float64pow10[-exp], i
|
return f / float64pow10[-exp], i
|
||||||
}
|
}
|
||||||
f *= math.Pow10(int(-mantExp))
|
f *= math.Pow10(int(-mantExp))
|
||||||
|
@ -135,7 +135,7 @@ func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
|
||||||
// expLen is zero for positive exponents, because positive exponents are determined later on in the big conversion loop
|
// expLen is zero for positive exponents, because positive exponents are determined later on in the big conversion loop
|
||||||
exp := 0
|
exp := 0
|
||||||
expLen := 0
|
expLen := 0
|
||||||
if mantExp > 0 {
|
if 0 < mantExp {
|
||||||
// positive exponent is determined in the loop below
|
// positive exponent is determined in the loop below
|
||||||
// but if we initially decreased the exponent to fit in an integer, we can't set the new exponent in the loop alone,
|
// but if we initially decreased the exponent to fit in an integer, we can't set the new exponent in the loop alone,
|
||||||
// since the number of zeros at the end determines the positive exponent in the loop, and we just artificially lost zeros
|
// since the number of zeros at the end determines the positive exponent in the loop, and we just artificially lost zeros
|
||||||
|
@ -156,7 +156,7 @@ func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
|
||||||
if neg {
|
if neg {
|
||||||
maxLen++
|
maxLen++
|
||||||
}
|
}
|
||||||
if i+maxLen > cap(b) {
|
if cap(b) < i+maxLen {
|
||||||
b = append(b, make([]byte, maxLen)...)
|
b = append(b, make([]byte, maxLen)...)
|
||||||
} else {
|
} else {
|
||||||
b = b[:i+maxLen]
|
b = b[:i+maxLen]
|
||||||
|
@ -175,17 +175,17 @@ func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
|
||||||
last := i + mantLen // right-most position of digit that is non-zero + dot
|
last := i + mantLen // right-most position of digit that is non-zero + dot
|
||||||
dot := last - prec - exp // position of dot
|
dot := last - prec - exp // position of dot
|
||||||
j := last
|
j := last
|
||||||
for mant > 0 {
|
for 0 < mant {
|
||||||
if j == dot {
|
if j == dot {
|
||||||
b[j] = '.'
|
b[j] = '.'
|
||||||
j--
|
j--
|
||||||
}
|
}
|
||||||
newMant := mant / 10
|
newMant := mant / 10
|
||||||
digit := mant - 10*newMant
|
digit := mant - 10*newMant
|
||||||
if zero && digit > 0 {
|
if zero && 0 < digit {
|
||||||
// first non-zero digit, if we are still behind the dot we can trim the end to this position
|
// first non-zero digit, if we are still behind the dot we can trim the end to this position
|
||||||
// otherwise trim to the dot (including the dot)
|
// otherwise trim to the dot (including the dot)
|
||||||
if j > dot {
|
if dot < j {
|
||||||
i = j + 1
|
i = j + 1
|
||||||
// decrease negative exponent further to get rid of dot
|
// decrease negative exponent further to get rid of dot
|
||||||
if exp < 0 {
|
if exp < 0 {
|
||||||
|
@ -209,9 +209,9 @@ func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
|
||||||
mant = newMant
|
mant = newMant
|
||||||
}
|
}
|
||||||
|
|
||||||
if j > dot {
|
if dot < j {
|
||||||
// extra zeros behind the dot
|
// extra zeros behind the dot
|
||||||
for j > dot {
|
for dot < j {
|
||||||
b[j] = '0'
|
b[j] = '0'
|
||||||
j--
|
j--
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) {
|
||||||
}
|
}
|
||||||
i += LenInt(int64(exp))
|
i += LenInt(int64(exp))
|
||||||
j := i
|
j := i
|
||||||
for exp > 0 {
|
for 0 < exp {
|
||||||
newExp := exp / 10
|
newExp := exp / 10
|
||||||
digit := exp - 10*newExp
|
digit := exp - 10*newExp
|
||||||
j--
|
j--
|
||||||
|
|
8
vendor/github.com/tdewolff/parse/v2/strconv/price.go
generated
vendored
8
vendor/github.com/tdewolff/parse/v2/strconv/price.go
generated
vendored
|
@ -20,14 +20,14 @@ func AppendPrice(b []byte, price int64, dec bool, milSeparator byte, decSeparato
|
||||||
// rounding
|
// rounding
|
||||||
if !dec {
|
if !dec {
|
||||||
firstDec := (price / 10) % 10
|
firstDec := (price / 10) % 10
|
||||||
if firstDec >= 5 {
|
if 5 <= firstDec {
|
||||||
price += 100
|
price += 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate size
|
// calculate size
|
||||||
n := LenInt(price) - 2
|
n := LenInt(price) - 2
|
||||||
if n > 0 {
|
if 0 < n {
|
||||||
n += (n - 1) / 3 // mil separator
|
n += (n - 1) / 3 // mil separator
|
||||||
} else {
|
} else {
|
||||||
n = 1
|
n = 1
|
||||||
|
@ -38,7 +38,7 @@ func AppendPrice(b []byte, price int64, dec bool, milSeparator byte, decSeparato
|
||||||
|
|
||||||
// resize byte slice
|
// resize byte slice
|
||||||
i := len(b)
|
i := len(b)
|
||||||
if i+n > cap(b) {
|
if cap(b) < i+n {
|
||||||
b = append(b, make([]byte, n)...)
|
b = append(b, make([]byte, n)...)
|
||||||
} else {
|
} else {
|
||||||
b = b[:i+n]
|
b = b[:i+n]
|
||||||
|
@ -66,7 +66,7 @@ func AppendPrice(b []byte, price int64, dec bool, milSeparator byte, decSeparato
|
||||||
|
|
||||||
// print integer-part
|
// print integer-part
|
||||||
j := 0
|
j := 0
|
||||||
for price > 0 {
|
for 0 < price {
|
||||||
if j == 3 {
|
if j == 3 {
|
||||||
b[i] = milSeparator
|
b[i] = milSeparator
|
||||||
i--
|
i--
|
||||||
|
|
28
vendor/golang.org/x/net/html/render.go
generated
vendored
28
vendor/golang.org/x/net/html/render.go
generated
vendored
|
@ -194,9 +194,8 @@ func render1(w writer, n *Node) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render any child nodes.
|
// Render any child nodes
|
||||||
switch n.Data {
|
if childTextNodesAreLiteral(n) {
|
||||||
case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
|
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
if c.Type == TextNode {
|
if c.Type == TextNode {
|
||||||
if _, err := w.WriteString(c.Data); err != nil {
|
if _, err := w.WriteString(c.Data); err != nil {
|
||||||
|
@ -213,7 +212,7 @@ func render1(w writer, n *Node) error {
|
||||||
// last element in the file, with no closing tag.
|
// last element in the file, with no closing tag.
|
||||||
return plaintextAbort
|
return plaintextAbort
|
||||||
}
|
}
|
||||||
default:
|
} else {
|
||||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
if err := render1(w, c); err != nil {
|
if err := render1(w, c); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -231,6 +230,27 @@ func render1(w writer, n *Node) error {
|
||||||
return w.WriteByte('>')
|
return w.WriteByte('>')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func childTextNodesAreLiteral(n *Node) bool {
|
||||||
|
// Per WHATWG HTML 13.3, if the parent of the current node is a style,
|
||||||
|
// script, xmp, iframe, noembed, noframes, or plaintext element, and the
|
||||||
|
// current node is a text node, append the value of the node's data
|
||||||
|
// literally. The specification is not explicit about it, but we only
|
||||||
|
// enforce this if we are in the HTML namespace (i.e. when the namespace is
|
||||||
|
// "").
|
||||||
|
// NOTE: we also always include noscript elements, although the
|
||||||
|
// specification states that they should only be rendered as such if
|
||||||
|
// scripting is enabled for the node (which is not something we track).
|
||||||
|
if n.Namespace != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch n.Data {
|
||||||
|
case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// writeQuoted writes s to w surrounded by quotes. Normally it will use double
|
// writeQuoted writes s to w surrounded by quotes. Normally it will use double
|
||||||
// quotes, but if s contains a double quote, it will use single quotes.
|
// quotes, but if s contains a double quote, it will use single quotes.
|
||||||
// It is used for writing the identifiers in a doctype declaration.
|
// It is used for writing the identifiers in a doctype declaration.
|
||||||
|
|
35
vendor/golang.org/x/net/http2/transport.go
generated
vendored
35
vendor/golang.org/x/net/http2/transport.go
generated
vendored
|
@ -19,6 +19,7 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
"math/bits"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -518,11 +519,14 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
func authorityAddr(scheme string, authority string) (addr string) {
|
func authorityAddr(scheme string, authority string) (addr string) {
|
||||||
host, port, err := net.SplitHostPort(authority)
|
host, port, err := net.SplitHostPort(authority)
|
||||||
if err != nil { // authority didn't have a port
|
if err != nil { // authority didn't have a port
|
||||||
|
host = authority
|
||||||
|
port = ""
|
||||||
|
}
|
||||||
|
if port == "" { // authority's port was empty
|
||||||
port = "443"
|
port = "443"
|
||||||
if scheme == "http" {
|
if scheme == "http" {
|
||||||
port = "80"
|
port = "80"
|
||||||
}
|
}
|
||||||
host = authority
|
|
||||||
}
|
}
|
||||||
if a, err := idna.ToASCII(host); err == nil {
|
if a, err := idna.ToASCII(host); err == nil {
|
||||||
host = a
|
host = a
|
||||||
|
@ -1677,7 +1681,27 @@ func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int {
|
||||||
return int(n) // doesn't truncate; max is 512K
|
return int(n) // doesn't truncate; max is 512K
|
||||||
}
|
}
|
||||||
|
|
||||||
var bufPool sync.Pool // of *[]byte
|
// Seven bufPools manage different frame sizes. This helps to avoid scenarios where long-running
|
||||||
|
// streaming requests using small frame sizes occupy large buffers initially allocated for prior
|
||||||
|
// requests needing big buffers. The size ranges are as follows:
|
||||||
|
// {0 KB, 16 KB], {16 KB, 32 KB], {32 KB, 64 KB], {64 KB, 128 KB], {128 KB, 256 KB],
|
||||||
|
// {256 KB, 512 KB], {512 KB, infinity}
|
||||||
|
// In practice, the maximum scratch buffer size should not exceed 512 KB due to
|
||||||
|
// frameScratchBufferLen(maxFrameSize), thus the "infinity pool" should never be used.
|
||||||
|
// It exists mainly as a safety measure, for potential future increases in max buffer size.
|
||||||
|
var bufPools [7]sync.Pool // of *[]byte
|
||||||
|
func bufPoolIndex(size int) int {
|
||||||
|
if size <= 16384 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
size -= 1
|
||||||
|
bits := bits.Len(uint(size))
|
||||||
|
index := bits - 14
|
||||||
|
if index >= len(bufPools) {
|
||||||
|
return len(bufPools) - 1
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
|
func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
|
||||||
cc := cs.cc
|
cc := cs.cc
|
||||||
|
@ -1695,12 +1719,13 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
|
||||||
// Scratch buffer for reading into & writing from.
|
// Scratch buffer for reading into & writing from.
|
||||||
scratchLen := cs.frameScratchBufferLen(maxFrameSize)
|
scratchLen := cs.frameScratchBufferLen(maxFrameSize)
|
||||||
var buf []byte
|
var buf []byte
|
||||||
if bp, ok := bufPool.Get().(*[]byte); ok && len(*bp) >= scratchLen {
|
index := bufPoolIndex(scratchLen)
|
||||||
defer bufPool.Put(bp)
|
if bp, ok := bufPools[index].Get().(*[]byte); ok && len(*bp) >= scratchLen {
|
||||||
|
defer bufPools[index].Put(bp)
|
||||||
buf = *bp
|
buf = *bp
|
||||||
} else {
|
} else {
|
||||||
buf = make([]byte, scratchLen)
|
buf = make([]byte, scratchLen)
|
||||||
defer bufPool.Put(&buf)
|
defer bufPools[index].Put(&buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sawEOF bool
|
var sawEOF bool
|
||||||
|
|
1
vendor/golang.org/x/oauth2/internal/client_appengine.go
generated
vendored
1
vendor/golang.org/x/oauth2/internal/client_appengine.go
generated
vendored
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build appengine
|
//go:build appengine
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
|
|
12
vendor/modules.txt
vendored
12
vendor/modules.txt
vendored
|
@ -659,11 +659,11 @@ github.com/superseriousbusiness/oauth2/v4/generates
|
||||||
github.com/superseriousbusiness/oauth2/v4/manage
|
github.com/superseriousbusiness/oauth2/v4/manage
|
||||||
github.com/superseriousbusiness/oauth2/v4/models
|
github.com/superseriousbusiness/oauth2/v4/models
|
||||||
github.com/superseriousbusiness/oauth2/v4/server
|
github.com/superseriousbusiness/oauth2/v4/server
|
||||||
# github.com/tdewolff/minify/v2 v2.12.7
|
# github.com/tdewolff/minify/v2 v2.12.8
|
||||||
## explicit; go 1.13
|
## explicit; go 1.18
|
||||||
github.com/tdewolff/minify/v2
|
github.com/tdewolff/minify/v2
|
||||||
github.com/tdewolff/minify/v2/html
|
github.com/tdewolff/minify/v2/html
|
||||||
# github.com/tdewolff/parse/v2 v2.6.6
|
# github.com/tdewolff/parse/v2 v2.6.7
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/tdewolff/parse/v2
|
github.com/tdewolff/parse/v2
|
||||||
github.com/tdewolff/parse/v2/buffer
|
github.com/tdewolff/parse/v2/buffer
|
||||||
|
@ -852,7 +852,7 @@ golang.org/x/image/webp
|
||||||
# golang.org/x/mod v0.10.0
|
# golang.org/x/mod v0.10.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
golang.org/x/mod/semver
|
golang.org/x/mod/semver
|
||||||
# golang.org/x/net v0.12.0
|
# golang.org/x/net v0.14.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
golang.org/x/net/bpf
|
golang.org/x/net/bpf
|
||||||
golang.org/x/net/context
|
golang.org/x/net/context
|
||||||
|
@ -870,8 +870,8 @@ golang.org/x/net/ipv4
|
||||||
golang.org/x/net/ipv6
|
golang.org/x/net/ipv6
|
||||||
golang.org/x/net/publicsuffix
|
golang.org/x/net/publicsuffix
|
||||||
golang.org/x/net/trace
|
golang.org/x/net/trace
|
||||||
# golang.org/x/oauth2 v0.10.0
|
# golang.org/x/oauth2 v0.11.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.18
|
||||||
golang.org/x/oauth2
|
golang.org/x/oauth2
|
||||||
golang.org/x/oauth2/internal
|
golang.org/x/oauth2/internal
|
||||||
# golang.org/x/sys v0.11.0
|
# golang.org/x/sys v0.11.0
|
||||||
|
|
45
web/assets/feditext.svg
Normal file
45
web/assets/feditext.svg
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
width="580"
|
||||||
|
height="648.07129"
|
||||||
|
viewBox="0 0 580 648.07129"
|
||||||
|
sodipodi:docname="feditext.svg"
|
||||||
|
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#ffffff"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="0.60078125"
|
||||||
|
inkscape:cx="643.329"
|
||||||
|
inkscape:cy="640"
|
||||||
|
inkscape:window-width="2752"
|
||||||
|
inkscape:window-height="1083"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Image 1">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;stroke-width:1.06266"
|
||||||
|
d="m 178.31448,648.0457 c 0,-0.22287 -3.31804,-12.73571 -7.37341,-27.8064 C 166.88568,605.16868 147.31446,532.1071 127.44944,457.88029 85.527985,301.23825 87.197265,307.36035 86.112035,306.27513 c -0.46091,-0.46092 -14.21456,2.67442 -30.56365,6.96742 -16.349101,4.293 -31.208011,8.12989 -33.019811,8.52642 l -3.29419,0.72096 -6.51865,-24.22492 C 2.0285837,258.5489 -0.57989635,248.01817 0.10089365,247.33738 c 0.35549,-0.3555 14.89589035,-4.46095 32.31199035,-9.12325 17.416101,-4.66229 32.068961,-8.85856 32.561911,-9.32506 0.49294,-0.46651 -1.98796,-11.77814 -5.51312,-25.13696 -8.38344,-31.76958 -9.39714,-36.33345 -11.30815,-50.91138 -5.98718,-45.67279 5.20679,-81.973838 33.81088,-109.645688 21.331845,-20.63662 48.691955,-33.6522399 86.724635,-41.2562299 10.997,-2.19866004 13.45942,-2.40581004 14.63412,-1.23110004 1.13937,1.13937004 22.62048,78.49091794 22.62048,81.45430794 0,0.46185 -4.05603,1.64524 -9.0134,2.62977 -24.76037,4.91735 -40.46005,14.46453 -46.82825,28.47687 -3.90046,8.5824 -4.77238,23.27239 -2.19535,36.98665 1.75696,9.35007 12.75641,51.76056 13.65838,52.66253 0.4428,0.44282 121.76948,-31.65747 131.29224,-34.73694 2.50328,-0.8095 3.34893,-1.63052 2.9807,-2.8938 -4.05367,-13.9066 -28.24159,-106.808348 -27.89809,-107.151858 0.45121,-0.4512 89.39306,-24.41748 92.86632,-25.02376 1.37732,-0.24042 4.22212,9.07873 15.31855,50.181108 7.4949,27.76199 14.02122,51.91095 14.50292,53.66433 0.53811,1.9588 1.67104,3.29946 2.93832,3.4771 1.13437,0.15901 18.23012,-4.02521 37.99054,-9.29827 19.76042,-5.27307 36.58465,-9.5874 37.38716,-9.5874 0.80252,0 1.70596,0.83685 2.00766,1.85966 1.15593,3.91878 15.49189,57.51001 17.43187,65.16443 l 2.03934,8.04645 -38.34515,10.31829 c -21.08983,5.67507 -38.67242,10.66171 -39.07242,11.08144 -0.56974,0.59783 48.98078,187.79383 57.02149,215.42048 4.67507,16.06282 13.21057,28.01206 23.68276,33.15461 5.63954,2.76939 6.94454,2.97246 18.81187,2.92708 11.85639,-0.0453 13.49817,-0.31471 23.37852,-3.83585 5.8446,-2.0829 13.37624,-5.04366 16.73685,-6.57947 3.36068,-1.53581 6.11032,-2.55248 6.11032,-2.25928 0,0.29321 4.782,18.33159 10.6266,40.08529 C 575.21806,518.68519 580,537.03684 580,537.71293 c 0,1.63704 -22.9762,11.00659 -38.25578,15.60048 -25.7852,7.75246 -32.1521,8.71291 -57.38362,8.65622 -22.14919,-0.0498 -23.12316,-0.14288 -31.8798,-3.04791 -29.12764,-9.66317 -49.29263,-28.46977 -63.86201,-59.56002 -6.69221,-14.28079 -9.86436,-25.16989 -39.68012,-136.21013 -35.98923,-134.03153 -31.51618,-118.94286 -35.13039,-118.50324 -3.44623,0.41919 -124.18412,32.49994 -128.5205,34.14862 -2.5808,0.98123 -2.81004,1.46652 -2.14776,4.54676 0.75671,3.51942 6.27829,24.2355 54.87563,205.88505 34.86397,130.31626 35.4586,132.60367 34.67505,133.38728 -0.55047,0.55045 -26.01282,7.54339 -82.42129,22.6361 -6.57521,1.75925 -11.95493,3.01638 -11.95493,2.79356 z"
|
||||||
|
id="path2-3" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4 KiB |
|
@ -55,6 +55,14 @@
|
||||||
<a href="https://tusky.app" target="_blank" rel="noopener">Get Tusky</a>
|
<a href="https://tusky.app" target="_blank" rel="noopener">Get Tusky</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="entry">
|
||||||
|
<img class="logo" src="/assets/feditext.svg" alt="The Feditext logo, the characters ft at a slight angle">
|
||||||
|
<div>
|
||||||
|
<h2>Feditext</h2>
|
||||||
|
<p>Feditext (beta) is a beautiful client for iOS, iPadOS and macOS.</p>
|
||||||
|
<a href="https://fedi.software/@Feditext" target="_blank" rel="noopener">Get Feditext</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="entry">
|
<div class="entry">
|
||||||
<img class="logo" src="/assets/mastodon.svg" alt="The Mastodon logo, the character M in a speech bubble">
|
<img class="logo" src="/assets/mastodon.svg" alt="The Mastodon logo, the character M in a speech bubble">
|
||||||
<div>
|
<div>
|
||||||
|
|
Loading…
Reference in a new issue