diff --git a/.drone.yml b/.drone.yml index d16386c7b4..a8fa7eba36 100644 --- a/.drone.yml +++ b/.drone.yml @@ -555,7 +555,7 @@ steps: # TODO: We should probably build all dependencies into a test image - name: test-e2e - image: mcr.microsoft.com/playwright:v1.29.0-focal + image: mcr.microsoft.com/playwright:v1.29.2-focal commands: - curl -sLO https://go.dev/dl/go1.19.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz - groupadd --gid 1001 gitea && useradd -m --gid 1001 --uid 1001 gitea diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 32e7ea70c8..fdd86a4647 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -84,6 +84,7 @@ rules: id-length: [0] id-match: [0] implicit-arrow-linebreak: [0] + import/consistent-type-specifier-style: [0] import/default: [0] import/dynamic-import-chunkname: [0] import/export: [2] @@ -103,6 +104,7 @@ rules: import/no-default-export: [0] import/no-deprecated: [0] import/no-dynamic-require: [0] + import/no-empty-named-blocks: [2] import/no-extraneous-dependencies: [2] import/no-import-module-exports: [0] import/no-internal-modules: [0] diff --git a/.gitpod.yml b/.gitpod.yml index 0b6ad1f30e..a184e6376e 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -7,10 +7,6 @@ tasks: command: | gp sync-done setup exit 0 - - name: Run frontend - command: | - gp sync-await setup - make watch-frontend - name: Run backend command: | gp sync-await setup @@ -19,9 +15,15 @@ tasks: echo -e "\n[database]\nDB_TYPE = sqlite3\nPATH = $GITPOD_REPO_ROOT/data/gitea.db" >> custom/conf/app.ini export TAGS="sqlite sqlite_unlock_notify" make watch-backend + - name: Run frontend + command: | + gp sync-await setup + make watch-frontend + openMode: split-right - name: Run docs before: sudo bash -c "$(grep 'https://github.com/gohugoio/hugo/releases/download' Makefile | tr -d '\')" # install hugo command: cd docs && make clean update && hugo server -D -F --baseUrl $(gp url 1313) --liveReloadPort=443 --appendPort=false --bind=0.0.0.0 + openMode: split-right vscode: extensions: diff --git a/CHANGELOG.md b/CHANGELOG.md index e82430e2ce..18b52673a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,66 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.18.3](https://github.com/go-gitea/gitea/releases/tag/v1.18.3) - 2023-01-23 + +* SECURITY + * Prevent multiple `To` recipients (#22566) (#22569) +* BUGFIXES + * Truncate commit summary on repo files table. (#22551) (#22552) + * Mute all links in issue timeline (#22534) + +## [1.18.2](https://github.com/go-gitea/gitea/releases/tag/v1.18.2) - 2023-01-19 + +* BUGFIXES + * Fix issue not auto-closing when it includes a reference to a branch (#22514) (#22521) + * Fix invalid issue branch reference if not specified in template (#22513) (#22520) + * Fix 500 error viewing pull request when fork has pull requests disabled (#22512) (#22515) + * Reliable selection of admin user (#22509) (#22511) + * Set disable_gravatar/enable_federated_avatar when offline mode is true (#22479) (#22496) +* BUILD + * cgo cross-compile for freebsd (#22397) (#22519) + +## [1.18.1](https://github.com/go-gitea/gitea/releases/tag/v1.18.1) - 2023-01-17 + +* API + * Add `sync_on_commit` option for push mirrors api (#22271) (#22292) +* BUGFIXES + * Update `github.com/zeripath/zapx/v15` (#22485) + * Fix pull request API field `closed_at` always being `null` (#22482) (#22483) + * Fix container blob mount (#22226) (#22476) + * Fix error when calculating repository size (#22392) (#22474) + * Fix Operator does not exist bug on explore page with ONLY_SHOW_RELEVANT_REPOS (#22454) (#22472) + * Fix environments for KaTeX and error reporting (#22453) (#22473) + * Remove the netgo tag for Windows build (#22467) (#22468) + * Fix migration from GitBucket (#22477) (#22465) + * Prevent panic on looking at api "git" endpoints for empty repos (#22457) (#22458) + * Fix PR status layout on mobile (#21547) (#22441) + * Fix wechatwork webhook sends empty content in PR review (#21762) (#22440) + * Remove duplicate "Actions" label in mobile view (#21974) (#22439) + * Fix leaving organization bug on user settings -> orgs (#21983) (#22438) + * Fixed colour transparency regex matching in project board sorting (#22092) (#22437) + * Correctly handle select on multiple channels in Queues (#22146) (#22428) + * Prepend refs/heads/ to issue template refs (#20461) (#22427) + * Restore function to "Show more" buttons (#22399) (#22426) + * Continue GCing other repos on error in one repo (#22422) (#22425) + * Allow HOST has no port (#22280) (#22409) + * Fix omit avatar_url in discord payload when empty (#22393) (#22394) + * Don't display stop watch top bar icon when disabled and hidden when click other place (#22374) (#22387) + * Don't lookup mail server when using sendmail (#22300) (#22383) + * Fix gravatar disable bug (#22337) + * Fix update settings table on install (#22326) (#22327) + * Fix sitemap (#22272) (#22320) + * Fix code search title translation (#22285) (#22316) + * Fix due date rendering the wrong date in issue (#22302) (#22306) + * Fix get system setting bug when enabled redis cache (#22298) + * Fix bug of DisableGravatar default value (#22297) + * Fix key signature error page (#22229) (#22230) +* TESTING + * Remove test session cache to reduce possible concurrent problem (#22199) (#22429) +* MISC + * Restore previous official review when an official review is deleted (#22449) (#22460) + * Log STDERR of external renderer when it fails (#22442) (#22444) + ## [1.18.0](https://github.com/go-gitea/gitea/releases/tag/v1.18.0) - 2022-12-29 * SECURITY diff --git a/DCO b/DCO index 3aca339def..49b8cb0549 100644 --- a/DCO +++ b/DCO @@ -2,8 +2,6 @@ Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, -San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -33,4 +31,4 @@ By making a contribution to this project, I certify that: are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. \ No newline at end of file + this project or the open source license(s) involved. diff --git a/README.md b/README.md index 19703d85dd..29d5371a36 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ - + - - + + @@ -45,7 +45,7 @@ - +

diff --git a/README_ZH.md b/README_ZH.md index 0e58ad6d4a..285a814037 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -12,14 +12,14 @@ - + - - + + @@ -45,7 +45,7 @@ - +

diff --git a/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet index 3e2513c4cf..31b7d4f9b2 100644 --- a/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet +++ b/contrib/gitea-monitoring-mixin/dashboards/overview.libsonnet @@ -29,7 +29,7 @@ local addIssueLabelsOverrides(labels) = grafanaDashboards+:: { - local giteaSelector = 'job="$job", instance="$instance"', + local giteaSelector = 'job=~"$job", instance=~"$instance"', local giteaStatsPanel = grafana.statPanel.new( 'Gitea stats', @@ -399,25 +399,31 @@ local addIssueLabelsOverrides(labels) = .addTemplate( { hide: 0, - label: null, + label: 'job', name: 'job', options: [], + datasource: '$datasource', query: 'label_values(gitea_organizations, job)', refresh: 1, regex: '', type: 'query', + multi: true, + allValue: '.+' }, ) .addTemplate( { hide: 0, - label: null, + label: 'instance', name: 'instance', options: [], + datasource: '$datasource', query: 'label_values(gitea_organizations{job="$job"}, instance)', refresh: 1, regex: '', type: 'query', + multi: true, + allValue: '.+' }, ) .addTemplate( diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index d40e4cb95e..a1d7bf6ad2 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1721,7 +1721,7 @@ ROUTER = console ;INTERVAL = 60 ;; ;; For "redis" and "memcache", connection host address -;; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180 +;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` ;; memcache: `127.0.0.1:11211` ;; twoqueue: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000` ;HOST = @@ -1760,7 +1760,7 @@ ROUTER = console ;; Provider config options ;; memory: doesn't have any config yet ;; file: session file path, e.g. `data/sessions` -;; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180 +;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` ;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table` ;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_. ;; @@ -2380,8 +2380,8 @@ ROUTER = console ;QUEUE_LENGTH = 1000 ;; ;; Task queue connection string, available only when `QUEUE_TYPE` is `redis`. -;; If there is a password of redis, use `addrs=127.0.0.1:6379 password=123 db=0`. -;QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0" +;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s`. +;QUEUE_CONN_STR = "redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/config.yaml b/docs/config.yaml index fbd7d8d810..0b47c0ffd8 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -18,7 +18,7 @@ params: description: Git with a cup of tea author: The Gitea Authors website: https://docs.gitea.io - version: 1.18.0 + version: 1.18.1 minGoVersion: 1.18 goVersion: 1.19 minNodeVersion: 16 diff --git a/docs/content/doc/developers/oauth2-provider.en-us.md b/docs/content/doc/developers/oauth2-provider.en-us.md index c6765f19e7..17c12d22f2 100644 --- a/docs/content/doc/developers/oauth2-provider.en-us.md +++ b/docs/content/doc/developers/oauth2-provider.en-us.md @@ -42,7 +42,41 @@ To use the Authorization Code Grant as a third party application it is required ## Scopes -Currently Gitea does not support scopes (see [#4300](https://github.com/go-gitea/gitea/issues/4300)) and all third party applications will be granted access to all resources of the user and their organizations. +Gitea supports the following scopes for tokens: + +| Name | Description | +| ---- | ----------- | +| **(no scope)** | Grants read-only access to public user profile and public repositories. | +| **repo** | Full control over all repositories. | +|     **repo:status** | Grants read/write access to commit status in all repositories. | +|     **public_repo** | Grants read/write access to public repositories only. | +| **admin:repo_hook** | Grants access to repository hooks of all repositories. This is included in the `repo` scope. | +|     **write:repo_hook** | Grants read/write access to repository hooks | +|     **read:repo_hook** | Grants read-only access to repository hooks | +| **admin:org** | Grants full access to organization settings | +|     **write:org** | Grants read/write access to organization settings | +|     **read:org** | Grants read-only access to organization settings | +| **admin:public_key** | Grants full access for managing public keys | +|     **write:public_key** | Grant read/write access to public keys | +|     **read:public_key** | Grant read-only access to public keys | +| **admin:org_hook** | Grants full access to organizational-level hooks | +| **notification** | Grants full access to notifications | +| **user** | Grants full access to user profile info | +|     **read:user** | Grants read access to user's profile | +|     **user:email** | Grants read access to user's email addresses | +|     **user:follow** | Grants access to follow/un-follow a user | +| **delete_repo** | Grants access to delete repositories as an admin | +| **package** | Grants full access to hosted packages | +|     **write:package** | Grants read/write access to packages | +|     **read:package** | Grants read access to packages | +|     **delete:package** | Grants delete access to packages | +| **admin:gpg_key** | Grants full access for managing GPG keys | +|     **write:gpg_key** | Grants read/write access to GPG keys | +|     **read:gpg_key** | Grants read-only access to GPG keys | +| **admin:application** | Grants full access to manage applications | +|     **write:application** | Grants read/write access for managing applications | +|     **read:application** | Grants read access for managing applications | +| **sudo** | Allows to perform actions as the site admin. | ## Client types diff --git a/docs/content/doc/features/comparison.en-us.md b/docs/content/doc/features/comparison.en-us.md index 87b2f43de7..1ecbf1068b 100644 --- a/docs/content/doc/features/comparison.en-us.md +++ b/docs/content/doc/features/comparison.en-us.md @@ -74,7 +74,7 @@ _Symbols used in table:_ | Granular user roles (Code, Issues, Wiki, …) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ | | Verified Committer | ⁄ | ✘ | ? | ✓ | ✓ | ✓ | ✘ | | GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | -| SSH Signed Commits | ✓ | ✘ | ✓ | ✘ | ✘ | ? | ? | +| SSH Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ? | | Reject unsigned commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | | Migrating repos from other services | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | | Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ | diff --git a/docs/content/doc/help/faq.en-us.md b/docs/content/doc/help/faq.en-us.md index a92186334f..2fcf0be156 100644 --- a/docs/content/doc/help/faq.en-us.md +++ b/docs/content/doc/help/faq.en-us.md @@ -138,6 +138,8 @@ For more information, refer to Gitea's [API docs]({{< relref "doc/developers/api You can see the latest API (for example) on . +You can also see an example of the `swagger.json` file at . + ## Adjusting your server for public/private use ### Preventing spammers diff --git a/docs/content/doc/packages/pypi.en-us.md b/docs/content/doc/packages/pypi.en-us.md index 588df71d60..ec2475aea3 100644 --- a/docs/content/doc/packages/pypi.en-us.md +++ b/docs/content/doc/packages/pypi.en-us.md @@ -77,6 +77,8 @@ For example: pip install --index-url https://testuser:password123@gitea.example.com/api/packages/testuser/pypi/simple --no-deps test_package ``` +You can use `--extra-index-url` instead of `--index-url` but that makes you vulnerable to dependency confusion attacks because `pip` checks the official PyPi repository for the package before it checks the specified custom repository. Read the `pip` docs for more information. + ## Supported commands ``` diff --git a/main.go b/main.go index 070e9857d0..4b183a7686 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/setting" // register supported doc types + _ "code.gitea.io/gitea/modules/markup/asciicast" _ "code.gitea.io/gitea/modules/markup/console" _ "code.gitea.io/gitea/modules/markup/csv" _ "code.gitea.io/gitea/modules/markup/markdown" diff --git a/models/auth/token.go b/models/auth/token.go index 0dfcb7629b..3f9f117f73 100644 --- a/models/auth/token.go +++ b/models/auth/token.go @@ -65,6 +65,7 @@ type AccessToken struct { TokenHash string `xorm:"UNIQUE"` // sha256 of token TokenSalt string TokenLastEight string `xorm:"INDEX token_last_eight"` + Scope AccessTokenScope CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` diff --git a/models/auth/token_scope.go b/models/auth/token_scope.go new file mode 100644 index 0000000000..c61c306496 --- /dev/null +++ b/models/auth/token_scope.go @@ -0,0 +1,251 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "fmt" + "strings" +) + +// AccessTokenScope represents the scope for an access token. +type AccessTokenScope string + +const ( + AccessTokenScopeAll AccessTokenScope = "all" + + AccessTokenScopeRepo AccessTokenScope = "repo" + AccessTokenScopeRepoStatus AccessTokenScope = "repo:status" + AccessTokenScopePublicRepo AccessTokenScope = "public_repo" + + AccessTokenScopeAdminOrg AccessTokenScope = "admin:org" + AccessTokenScopeWriteOrg AccessTokenScope = "write:org" + AccessTokenScopeReadOrg AccessTokenScope = "read:org" + + AccessTokenScopeAdminPublicKey AccessTokenScope = "admin:public_key" + AccessTokenScopeWritePublicKey AccessTokenScope = "write:public_key" + AccessTokenScopeReadPublicKey AccessTokenScope = "read:public_key" + + AccessTokenScopeAdminRepoHook AccessTokenScope = "admin:repo_hook" + AccessTokenScopeWriteRepoHook AccessTokenScope = "write:repo_hook" + AccessTokenScopeReadRepoHook AccessTokenScope = "read:repo_hook" + + AccessTokenScopeAdminOrgHook AccessTokenScope = "admin:org_hook" + + AccessTokenScopeNotification AccessTokenScope = "notification" + + AccessTokenScopeUser AccessTokenScope = "user" + AccessTokenScopeReadUser AccessTokenScope = "read:user" + AccessTokenScopeUserEmail AccessTokenScope = "user:email" + AccessTokenScopeUserFollow AccessTokenScope = "user:follow" + + AccessTokenScopeDeleteRepo AccessTokenScope = "delete_repo" + + AccessTokenScopePackage AccessTokenScope = "package" + AccessTokenScopeWritePackage AccessTokenScope = "write:package" + AccessTokenScopeReadPackage AccessTokenScope = "read:package" + AccessTokenScopeDeletePackage AccessTokenScope = "delete:package" + + AccessTokenScopeAdminGPGKey AccessTokenScope = "admin:gpg_key" + AccessTokenScopeWriteGPGKey AccessTokenScope = "write:gpg_key" + AccessTokenScopeReadGPGKey AccessTokenScope = "read:gpg_key" + + AccessTokenScopeAdminApplication AccessTokenScope = "admin:application" + AccessTokenScopeWriteApplication AccessTokenScope = "write:application" + AccessTokenScopeReadApplication AccessTokenScope = "read:application" + + AccessTokenScopeSudo AccessTokenScope = "sudo" +) + +// AccessTokenScopeBitmap represents a bitmap of access token scopes. +type AccessTokenScopeBitmap uint64 + +// Bitmap of each scope, including the child scopes. +const ( + // AccessTokenScopeAllBits is the bitmap of all access token scopes, except `sudo`. + AccessTokenScopeAllBits AccessTokenScopeBitmap = AccessTokenScopeRepoBits | + AccessTokenScopeAdminOrgBits | AccessTokenScopeAdminPublicKeyBits | AccessTokenScopeAdminOrgHookBits | + AccessTokenScopeNotificationBits | AccessTokenScopeUserBits | AccessTokenScopeDeleteRepoBits | + AccessTokenScopePackageBits | AccessTokenScopeAdminGPGKeyBits | AccessTokenScopeAdminApplicationBits + + AccessTokenScopeRepoBits AccessTokenScopeBitmap = 1< 64 scopes, + // refactoring the whole implementation in this file (and only this file) is needed. +) + +// allAccessTokenScopes contains all access token scopes. +// The order is important: parent scope must precedes child scopes. +var allAccessTokenScopes = []AccessTokenScope{ + AccessTokenScopeRepo, AccessTokenScopeRepoStatus, AccessTokenScopePublicRepo, + AccessTokenScopeAdminOrg, AccessTokenScopeWriteOrg, AccessTokenScopeReadOrg, + AccessTokenScopeAdminPublicKey, AccessTokenScopeWritePublicKey, AccessTokenScopeReadPublicKey, + AccessTokenScopeAdminRepoHook, AccessTokenScopeWriteRepoHook, AccessTokenScopeReadRepoHook, + AccessTokenScopeAdminOrgHook, + AccessTokenScopeNotification, + AccessTokenScopeUser, AccessTokenScopeReadUser, AccessTokenScopeUserEmail, AccessTokenScopeUserFollow, + AccessTokenScopeDeleteRepo, + AccessTokenScopePackage, AccessTokenScopeWritePackage, AccessTokenScopeReadPackage, AccessTokenScopeDeletePackage, + AccessTokenScopeAdminGPGKey, AccessTokenScopeWriteGPGKey, AccessTokenScopeReadGPGKey, + AccessTokenScopeAdminApplication, AccessTokenScopeWriteApplication, AccessTokenScopeReadApplication, + AccessTokenScopeSudo, +} + +// allAccessTokenScopeBits contains all access token scopes. +var allAccessTokenScopeBits = map[AccessTokenScope]AccessTokenScopeBitmap{ + AccessTokenScopeRepo: AccessTokenScopeRepoBits, + AccessTokenScopeRepoStatus: AccessTokenScopeRepoStatusBits, + AccessTokenScopePublicRepo: AccessTokenScopePublicRepoBits, + AccessTokenScopeAdminOrg: AccessTokenScopeAdminOrgBits, + AccessTokenScopeWriteOrg: AccessTokenScopeWriteOrgBits, + AccessTokenScopeReadOrg: AccessTokenScopeReadOrgBits, + AccessTokenScopeAdminPublicKey: AccessTokenScopeAdminPublicKeyBits, + AccessTokenScopeWritePublicKey: AccessTokenScopeWritePublicKeyBits, + AccessTokenScopeReadPublicKey: AccessTokenScopeReadPublicKeyBits, + AccessTokenScopeAdminRepoHook: AccessTokenScopeAdminRepoHookBits, + AccessTokenScopeWriteRepoHook: AccessTokenScopeWriteRepoHookBits, + AccessTokenScopeReadRepoHook: AccessTokenScopeReadRepoHookBits, + AccessTokenScopeAdminOrgHook: AccessTokenScopeAdminOrgHookBits, + AccessTokenScopeNotification: AccessTokenScopeNotificationBits, + AccessTokenScopeUser: AccessTokenScopeUserBits, + AccessTokenScopeReadUser: AccessTokenScopeReadUserBits, + AccessTokenScopeUserEmail: AccessTokenScopeUserEmailBits, + AccessTokenScopeUserFollow: AccessTokenScopeUserFollowBits, + AccessTokenScopeDeleteRepo: AccessTokenScopeDeleteRepoBits, + AccessTokenScopePackage: AccessTokenScopePackageBits, + AccessTokenScopeWritePackage: AccessTokenScopeWritePackageBits, + AccessTokenScopeReadPackage: AccessTokenScopeReadPackageBits, + AccessTokenScopeDeletePackage: AccessTokenScopeDeletePackageBits, + AccessTokenScopeAdminGPGKey: AccessTokenScopeAdminGPGKeyBits, + AccessTokenScopeWriteGPGKey: AccessTokenScopeWriteGPGKeyBits, + AccessTokenScopeReadGPGKey: AccessTokenScopeReadGPGKeyBits, + AccessTokenScopeAdminApplication: AccessTokenScopeAdminApplicationBits, + AccessTokenScopeWriteApplication: AccessTokenScopeWriteApplicationBits, + AccessTokenScopeReadApplication: AccessTokenScopeReadApplicationBits, + AccessTokenScopeSudo: AccessTokenScopeSudoBits, +} + +// Parse parses the scope string into a bitmap, thus removing possible duplicates. +func (s AccessTokenScope) Parse() (AccessTokenScopeBitmap, error) { + list := strings.Split(string(s), ",") + + var bitmap AccessTokenScopeBitmap + for _, v := range list { + singleScope := AccessTokenScope(v) + if singleScope == "" { + continue + } + if singleScope == AccessTokenScopeAll { + bitmap |= AccessTokenScopeAllBits + continue + } + + bits, ok := allAccessTokenScopeBits[singleScope] + if !ok { + return 0, fmt.Errorf("invalid access token scope: %s", singleScope) + } + bitmap |= bits + } + return bitmap, nil +} + +// Normalize returns a normalized scope string without any duplicates. +func (s AccessTokenScope) Normalize() (AccessTokenScope, error) { + bitmap, err := s.Parse() + if err != nil { + return "", err + } + + return bitmap.ToScope(), nil +} + +// HasScope returns true if the string has the given scope +func (s AccessTokenScope) HasScope(scope AccessTokenScope) (bool, error) { + bitmap, err := s.Parse() + if err != nil { + return false, err + } + + return bitmap.HasScope(scope) +} + +// HasScope returns true if the string has the given scope +func (bitmap AccessTokenScopeBitmap) HasScope(scope AccessTokenScope) (bool, error) { + expectedBits, ok := allAccessTokenScopeBits[scope] + if !ok { + return false, fmt.Errorf("invalid access token scope: %s", scope) + } + + return bitmap&expectedBits == expectedBits, nil +} + +// ToScope returns a normalized scope string without any duplicates. +func (bitmap AccessTokenScopeBitmap) ToScope() AccessTokenScope { + var scopes []string + + // iterate over all scopes, and reconstruct the bitmap + // if the reconstructed bitmap doesn't change, then the scope is already included + var reconstruct AccessTokenScopeBitmap + + for _, singleScope := range allAccessTokenScopes { + // no need for error checking here, since we know the scope is valid + if ok, _ := bitmap.HasScope(singleScope); ok { + current := reconstruct | allAccessTokenScopeBits[singleScope] + if current == reconstruct { + continue + } + + reconstruct = current + scopes = append(scopes, string(singleScope)) + } + } + + scope := AccessTokenScope(strings.Join(scopes, ",")) + scope = AccessTokenScope(strings.ReplaceAll( + string(scope), + "repo,admin:org,admin:public_key,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", + "all", + )) + return scope +} diff --git a/models/auth/token_scope_test.go b/models/auth/token_scope_test.go new file mode 100644 index 0000000000..1d7f4794a4 --- /dev/null +++ b/models/auth/token_scope_test.go @@ -0,0 +1,84 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAccessTokenScope_Normalize(t *testing.T) { + tests := []struct { + in AccessTokenScope + out AccessTokenScope + err error + }{ + {"", "", nil}, + {"repo", "repo", nil}, + {"repo,repo:status", "repo", nil}, + {"repo,public_repo", "repo", nil}, + {"admin:public_key,write:public_key", "admin:public_key", nil}, + {"admin:public_key,read:public_key", "admin:public_key", nil}, + {"write:public_key,read:public_key", "write:public_key", nil}, // read is include in write + {"admin:repo_hook,write:repo_hook", "admin:repo_hook", nil}, + {"admin:repo_hook,read:repo_hook", "admin:repo_hook", nil}, + {"repo,admin:repo_hook,read:repo_hook", "repo", nil}, // admin:repo_hook is a child scope of repo + {"repo,read:repo_hook", "repo", nil}, // read:repo_hook is a child scope of repo + {"user", "user", nil}, + {"user,read:user", "user", nil}, + {"user,admin:org,write:org", "admin:org,user", nil}, + {"admin:org,write:org,user", "admin:org,user", nil}, + {"package", "package", nil}, + {"package,write:package", "package", nil}, + {"package,write:package,delete:package", "package", nil}, + {"write:package,read:package", "write:package", nil}, // read is include in write + {"write:package,delete:package", "write:package,delete:package", nil}, // write and delete are not include in each other + {"admin:gpg_key", "admin:gpg_key", nil}, + {"admin:gpg_key,write:gpg_key", "admin:gpg_key", nil}, + {"admin:gpg_key,write:gpg_key,user", "user,admin:gpg_key", nil}, + {"admin:application,write:application,user", "user,admin:application", nil}, + {"all", "all", nil}, + {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application", "all", nil}, + {"repo,admin:org,admin:public_key,admin:repo_hook,admin:org_hook,notification,user,delete_repo,package,admin:gpg_key,admin:application,sudo", "all,sudo", nil}, + } + + for _, test := range tests { + t.Run(string(test.in), func(t *testing.T) { + scope, err := test.in.Normalize() + assert.Equal(t, test.out, scope) + assert.Equal(t, test.err, err) + }) + } +} + +func TestAccessTokenScope_HasScope(t *testing.T) { + tests := []struct { + in AccessTokenScope + scope AccessTokenScope + out bool + err error + }{ + {"repo", "repo", true, nil}, + {"repo", "repo:status", true, nil}, + {"repo", "public_repo", true, nil}, + {"repo", "admin:org", false, nil}, + {"repo", "admin:public_key", false, nil}, + {"repo:status", "repo", false, nil}, + {"repo:status", "public_repo", false, nil}, + {"admin:org", "write:org", true, nil}, + {"admin:org", "read:org", true, nil}, + {"admin:org", "admin:org", true, nil}, + {"user", "read:user", true, nil}, + {"package", "write:package", true, nil}, + } + + for _, test := range tests { + t.Run(string(test.in), func(t *testing.T) { + scope, err := test.in.HasScope(test.scope) + assert.Equal(t, test.out, scope) + assert.Equal(t, test.err, err) + }) + } +} diff --git a/models/db/list_options.go b/models/db/list_options.go index b9ee360b1b..2456f90f3b 100644 --- a/models/db/list_options.go +++ b/models/db/list_options.go @@ -4,8 +4,11 @@ package db import ( + "context" + "code.gitea.io/gitea/modules/setting" + "xorm.io/builder" "xorm.io/xorm" ) @@ -18,6 +21,7 @@ const ( type Paginator interface { GetSkipTake() (skip, take int) GetStartEnd() (start, end int) + IsListAll() bool } // GetPaginatedSession creates a paginated database session @@ -44,9 +48,12 @@ func SetEnginePagination(e Engine, p Paginator) Engine { // ListOptions options to paginate results type ListOptions struct { PageSize int - Page int // start from 1 + Page int // start from 1 + ListAll bool // if true, then PageSize and Page will not be taken } +var _ Paginator = &ListOptions{} + // GetSkipTake returns the skip and take values func (opts *ListOptions) GetSkipTake() (skip, take int) { opts.SetDefaultValues() @@ -60,6 +67,11 @@ func (opts *ListOptions) GetStartEnd() (start, end int) { return start, end } +// IsListAll indicates PageSize and Page will be ignored +func (opts *ListOptions) IsListAll() bool { + return opts.ListAll +} + // SetDefaultValues sets default values func (opts *ListOptions) SetDefaultValues() { if opts.PageSize <= 0 { @@ -79,6 +91,8 @@ type AbsoluteListOptions struct { take int } +var _ Paginator = &AbsoluteListOptions{} + // NewAbsoluteListOptions creates a list option with applied limits func NewAbsoluteListOptions(skip, take int) *AbsoluteListOptions { if skip < 0 { @@ -93,6 +107,11 @@ func NewAbsoluteListOptions(skip, take int) *AbsoluteListOptions { return &AbsoluteListOptions{skip, take} } +// IsListAll will always return false +func (opts *AbsoluteListOptions) IsListAll() bool { + return false +} + // GetSkipTake returns the skip and take values func (opts *AbsoluteListOptions) GetSkipTake() (skip, take int) { return opts.skip, opts.take @@ -102,3 +121,32 @@ func (opts *AbsoluteListOptions) GetSkipTake() (skip, take int) { func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) { return opts.skip, opts.skip + opts.take } + +// FindOptions represents a find options +type FindOptions interface { + Paginator + ToConds() builder.Cond +} + +// Find represents a common find function which accept an options interface +func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error { + sess := GetEngine(ctx).Where(opts.ToConds()) + if !opts.IsListAll() { + sess.Limit(opts.GetSkipTake()) + } + return sess.Find(&objects) +} + +// Count represents a common count function which accept an options interface +func Count[T any](ctx context.Context, opts FindOptions, object T) (int64, error) { + return GetEngine(ctx).Where(opts.ToConds()).Count(object) +} + +// FindAndCount represents a common findandcount function which accept an options interface +func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (int64, error) { + sess := GetEngine(ctx).Where(opts.ToConds()) + if !opts.IsListAll() { + sess.Limit(opts.GetSkipTake()) + } + return sess.FindAndCount(&objects) +} diff --git a/models/fixtures/project.yml b/models/fixtures/project.yml index 3d42597c5e..f38b5344bb 100644 --- a/models/fixtures/project.yml +++ b/models/fixtures/project.yml @@ -24,3 +24,12 @@ creator_id: 5 board_type: 1 type: 2 + +- + id: 4 + title: project on user2 + owner_id: 2 + is_closed: false + creator_id: 2 + board_type: 1 + type: 2 diff --git a/models/fixtures/project_board.yml b/models/fixtures/project_board.yml index 9e06e8c239..dc4f9cf565 100644 --- a/models/fixtures/project_board.yml +++ b/models/fixtures/project_board.yml @@ -21,3 +21,11 @@ creator_id: 2 created_unix: 1588117528 updated_unix: 1588117528 + +- + id: 4 + project_id: 4 + title: Done + creator_id: 2 + created_unix: 1588117528 + updated_unix: 1588117528 diff --git a/models/issues/comment.go b/models/issues/comment.go index 4a819f51ae..677310086e 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -8,9 +8,7 @@ package issues import ( "context" "fmt" - "regexp" "strconv" - "strings" "unicode/utf8" "code.gitea.io/gitea/models/db" @@ -22,8 +20,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" @@ -180,6 +176,15 @@ func (t CommentType) String() string { return commentStrings[t] } +func AsCommentType(typeName string) CommentType { + for index, name := range commentStrings { + if typeName == name { + return CommentType(index) + } + } + return CommentTypeUnknown +} + // RoleDescriptor defines comment tag type type RoleDescriptor int @@ -688,31 +693,6 @@ func (c *Comment) LoadReview() error { return c.loadReview(db.DefaultContext) } -var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`) - -func (c *Comment) checkInvalidation(doer *user_model.User, repo *git.Repository, branch string) error { - // FIXME differentiate between previous and proposed line - commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine())) - if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { - c.Invalidated = true - return UpdateComment(c, doer) - } - if err != nil { - return err - } - if c.CommitSHA != "" && c.CommitSHA != commit.ID.String() { - c.Invalidated = true - return UpdateComment(c, doer) - } - return nil -} - -// CheckInvalidation checks if the line of code comment got changed by another commit. -// If the line got changed the comment is going to be invalidated. -func (c *Comment) CheckInvalidation(repo *git.Repository, doer *user_model.User, branch string) error { - return c.checkInvalidation(doer, repo, branch) -} - // DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes. func (c *Comment) DiffSide() string { if c.Line < 0 { @@ -1009,23 +989,28 @@ func GetCommentByID(ctx context.Context, id int64) (*Comment, error) { // FindCommentsOptions describes the conditions to Find comments type FindCommentsOptions struct { db.ListOptions - RepoID int64 - IssueID int64 - ReviewID int64 - Since int64 - Before int64 - Line int64 - TreePath string - Type CommentType + RepoID int64 + IssueID int64 + ReviewID int64 + Since int64 + Before int64 + Line int64 + TreePath string + Type CommentType + IssueIDs []int64 + Invalidated util.OptionalBool } -func (opts *FindCommentsOptions) toConds() builder.Cond { +// ToConds implements FindOptions interface +func (opts *FindCommentsOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID}) } if opts.IssueID > 0 { cond = cond.And(builder.Eq{"comment.issue_id": opts.IssueID}) + } else if len(opts.IssueIDs) > 0 { + cond = cond.And(builder.In("comment.issue_id", opts.IssueIDs)) } if opts.ReviewID > 0 { cond = cond.And(builder.Eq{"comment.review_id": opts.ReviewID}) @@ -1045,13 +1030,16 @@ func (opts *FindCommentsOptions) toConds() builder.Cond { if len(opts.TreePath) > 0 { cond = cond.And(builder.Eq{"comment.tree_path": opts.TreePath}) } + if !opts.Invalidated.IsNone() { + cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.IsTrue()}) + } return cond } // FindComments returns all comments according options func FindComments(ctx context.Context, opts *FindCommentsOptions) ([]*Comment, error) { comments := make([]*Comment, 0, 10) - sess := db.GetEngine(ctx).Where(opts.toConds()) + sess := db.GetEngine(ctx).Where(opts.ToConds()) if opts.RepoID > 0 { sess.Join("INNER", "issue", "issue.id = comment.issue_id") } @@ -1070,13 +1058,19 @@ func FindComments(ctx context.Context, opts *FindCommentsOptions) ([]*Comment, e // CountComments count all comments according options by ignoring pagination func CountComments(opts *FindCommentsOptions) (int64, error) { - sess := db.GetEngine(db.DefaultContext).Where(opts.toConds()) + sess := db.GetEngine(db.DefaultContext).Where(opts.ToConds()) if opts.RepoID > 0 { sess.Join("INNER", "issue", "issue.id = comment.issue_id") } return sess.Count(&Comment{}) } +// UpdateCommentInvalidate updates comment invalidated column +func UpdateCommentInvalidate(ctx context.Context, c *Comment) error { + _, err := db.GetEngine(ctx).ID(c.ID).Cols("invalidated").Update(c) + return err +} + // UpdateComment updates information of comment. func UpdateComment(c *Comment, doer *user_model.User) error { ctx, committer, err := db.TxContext(db.DefaultContext) @@ -1135,120 +1129,6 @@ func DeleteComment(ctx context.Context, comment *Comment) error { return DeleteReaction(ctx, &ReactionOptions{CommentID: comment.ID}) } -// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS -type CodeComments map[string]map[int64][]*Comment - -// FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line -func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User) (CodeComments, error) { - return fetchCodeCommentsByReview(ctx, issue, currentUser, nil) -} - -func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review) (CodeComments, error) { - pathToLineToComment := make(CodeComments) - if review == nil { - review = &Review{ID: 0} - } - opts := FindCommentsOptions{ - Type: CommentTypeCode, - IssueID: issue.ID, - ReviewID: review.ID, - } - - comments, err := findCodeComments(ctx, opts, issue, currentUser, review) - if err != nil { - return nil, err - } - - for _, comment := range comments { - if pathToLineToComment[comment.TreePath] == nil { - pathToLineToComment[comment.TreePath] = make(map[int64][]*Comment) - } - pathToLineToComment[comment.TreePath][comment.Line] = append(pathToLineToComment[comment.TreePath][comment.Line], comment) - } - return pathToLineToComment, nil -} - -func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review) ([]*Comment, error) { - var comments []*Comment - if review == nil { - review = &Review{ID: 0} - } - conds := opts.toConds() - if review.ID == 0 { - conds = conds.And(builder.Eq{"invalidated": false}) - } - e := db.GetEngine(ctx) - if err := e.Where(conds). - Asc("comment.created_unix"). - Asc("comment.id"). - Find(&comments); err != nil { - return nil, err - } - - if err := issue.LoadRepo(ctx); err != nil { - return nil, err - } - - if err := CommentList(comments).LoadPosters(ctx); err != nil { - return nil, err - } - - // Find all reviews by ReviewID - reviews := make(map[int64]*Review) - ids := make([]int64, 0, len(comments)) - for _, comment := range comments { - if comment.ReviewID != 0 { - ids = append(ids, comment.ReviewID) - } - } - if err := e.In("id", ids).Find(&reviews); err != nil { - return nil, err - } - - n := 0 - for _, comment := range comments { - if re, ok := reviews[comment.ReviewID]; ok && re != nil { - // If the review is pending only the author can see the comments (except if the review is set) - if review.ID == 0 && re.Type == ReviewTypePending && - (currentUser == nil || currentUser.ID != re.ReviewerID) { - continue - } - comment.Review = re - } - comments[n] = comment - n++ - - if err := comment.LoadResolveDoer(); err != nil { - return nil, err - } - - if err := comment.LoadReactions(issue.Repo); err != nil { - return nil, err - } - - var err error - if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - URLPrefix: issue.Repo.Link(), - Metas: issue.Repo.ComposeMetas(), - }, comment.Content); err != nil { - return nil, err - } - } - return comments[:n], nil -} - -// FetchCodeCommentsByLine fetches the code comments for a given treePath and line number -func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64) ([]*Comment, error) { - opts := FindCommentsOptions{ - Type: CommentTypeCode, - IssueID: issue.ID, - TreePath: treePath, - Line: line, - } - return findCodeComments(ctx, opts, issue, currentUser, nil) -} - // UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error { _, err := db.GetEngine(db.DefaultContext).Table("comment"). diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go new file mode 100644 index 0000000000..8475da1a8c --- /dev/null +++ b/models/issues/comment_code.go @@ -0,0 +1,129 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package issues + +import ( + "context" + + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" + + "xorm.io/builder" +) + +// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS +type CodeComments map[string]map[int64][]*Comment + +// FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line +func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User) (CodeComments, error) { + return fetchCodeCommentsByReview(ctx, issue, currentUser, nil) +} + +func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review) (CodeComments, error) { + pathToLineToComment := make(CodeComments) + if review == nil { + review = &Review{ID: 0} + } + opts := FindCommentsOptions{ + Type: CommentTypeCode, + IssueID: issue.ID, + ReviewID: review.ID, + } + + comments, err := findCodeComments(ctx, opts, issue, currentUser, review) + if err != nil { + return nil, err + } + + for _, comment := range comments { + if pathToLineToComment[comment.TreePath] == nil { + pathToLineToComment[comment.TreePath] = make(map[int64][]*Comment) + } + pathToLineToComment[comment.TreePath][comment.Line] = append(pathToLineToComment[comment.TreePath][comment.Line], comment) + } + return pathToLineToComment, nil +} + +func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review) ([]*Comment, error) { + var comments []*Comment + if review == nil { + review = &Review{ID: 0} + } + conds := opts.ToConds() + if review.ID == 0 { + conds = conds.And(builder.Eq{"invalidated": false}) + } + e := db.GetEngine(ctx) + if err := e.Where(conds). + Asc("comment.created_unix"). + Asc("comment.id"). + Find(&comments); err != nil { + return nil, err + } + + if err := issue.LoadRepo(ctx); err != nil { + return nil, err + } + + if err := CommentList(comments).LoadPosters(ctx); err != nil { + return nil, err + } + + // Find all reviews by ReviewID + reviews := make(map[int64]*Review) + ids := make([]int64, 0, len(comments)) + for _, comment := range comments { + if comment.ReviewID != 0 { + ids = append(ids, comment.ReviewID) + } + } + if err := e.In("id", ids).Find(&reviews); err != nil { + return nil, err + } + + n := 0 + for _, comment := range comments { + if re, ok := reviews[comment.ReviewID]; ok && re != nil { + // If the review is pending only the author can see the comments (except if the review is set) + if review.ID == 0 && re.Type == ReviewTypePending && + (currentUser == nil || currentUser.ID != re.ReviewerID) { + continue + } + comment.Review = re + } + comments[n] = comment + n++ + + if err := comment.LoadResolveDoer(); err != nil { + return nil, err + } + + if err := comment.LoadReactions(issue.Repo); err != nil { + return nil, err + } + + var err error + if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + URLPrefix: issue.Repo.Link(), + Metas: issue.Repo.ComposeMetas(), + }, comment.Content); err != nil { + return nil, err + } + } + return comments[:n], nil +} + +// FetchCodeCommentsByLine fetches the code comments for a given treePath and line number +func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64) ([]*Comment, error) { + opts := FindCommentsOptions{ + Type: CommentTypeCode, + IssueID: issue.ID, + TreePath: treePath, + Line: line, + } + return findCodeComments(ctx, opts, issue, currentUser, nil) +} diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go index 0d0570ae34..f1232729f1 100644 --- a/models/issues/comment_test.go +++ b/models/issues/comment_test.go @@ -62,3 +62,10 @@ func TestFetchCodeComments(t *testing.T) { assert.NoError(t, err) assert.Len(t, res, 1) } + +func TestAsCommentType(t *testing.T) { + assert.Equal(t, issues_model.CommentTypeUnknown, issues_model.AsCommentType("")) + assert.Equal(t, issues_model.CommentTypeUnknown, issues_model.AsCommentType("nonsense")) + assert.Equal(t, issues_model.CommentTypeComment, issues_model.AsCommentType("comment")) + assert.Equal(t, issues_model.CommentTypePRUnScheduledToAutoMerge, issues_model.AsCommentType("pull_cancel_scheduled_merge")) +} diff --git a/models/issues/issue.go b/models/issues/issue.go index 3cc893157b..d9e44857d8 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -1100,7 +1100,7 @@ func GetIssueWithAttrsByID(id int64) (*Issue, error) { } // GetIssuesByIDs return issues with the given IDs. -func GetIssuesByIDs(ctx context.Context, issueIDs []int64) ([]*Issue, error) { +func GetIssuesByIDs(ctx context.Context, issueIDs []int64) (IssueList, error) { issues := make([]*Issue, 0, 10) return issues, db.GetEngine(ctx).In("id", issueIDs).Find(&issues) } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 8e559f13c9..c9f4c9f533 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -125,13 +125,17 @@ func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64 func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { oldProjectID := issue.projectID(ctx) + if err := issue.LoadRepo(ctx); err != nil { + return err + } + // Only check if we add a new project and not remove it. if newProjectID > 0 { newProject, err := project_model.GetProjectByID(ctx, newProjectID) if err != nil { return err } - if newProject.RepoID != issue.RepoID { + if newProject.RepoID != issue.RepoID && newProject.OwnerID != issue.Repo.OwnerID { return fmt.Errorf("issue's repository is not the same as project's repository") } } @@ -140,10 +144,6 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U return err } - if err := issue.LoadRepo(ctx); err != nil { - return err - } - if oldProjectID > 0 || newProjectID > 0 { if _, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeProject, diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 432e848e97..12dbff107d 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "xorm.io/xorm" @@ -161,7 +160,7 @@ func (prs PullRequestList) loadAttributes(ctx context.Context) error { } // Load issues. - issueIDs := prs.getIssueIDs() + issueIDs := prs.GetIssueIDs() issues := make([]*Issue, 0, len(issueIDs)) if err := db.GetEngine(ctx). Where("id > 0"). @@ -180,7 +179,8 @@ func (prs PullRequestList) loadAttributes(ctx context.Context) error { return nil } -func (prs PullRequestList) getIssueIDs() []int64 { +// GetIssueIDs returns all issue ids +func (prs PullRequestList) GetIssueIDs() []int64 { issueIDs := make([]int64, 0, len(prs)) for i := range prs { issueIDs = append(issueIDs, prs[i].IssueID) @@ -192,24 +192,3 @@ func (prs PullRequestList) getIssueIDs() []int64 { func (prs PullRequestList) LoadAttributes() error { return prs.loadAttributes(db.DefaultContext) } - -// InvalidateCodeComments will lookup the prs for code comments which got invalidated by change -func (prs PullRequestList) InvalidateCodeComments(ctx context.Context, doer *user_model.User, repo *git.Repository, branch string) error { - if len(prs) == 0 { - return nil - } - issueIDs := prs.getIssueIDs() - var codeComments []*Comment - if err := db.GetEngine(ctx). - Where("type = ? and invalidated = ?", CommentTypeCode, false). - In("issue_id", issueIDs). - Find(&codeComments); err != nil { - return fmt.Errorf("find code comments: %w", err) - } - for _, comment := range codeComments { - if err := comment.CheckInvalidation(repo, doer, branch); err != nil { - return err - } - } - return nil -} diff --git a/models/issues/review.go b/models/issues/review.go index d8e517ad3c..7e1a39bb5b 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -972,7 +972,7 @@ func DeleteReview(r *Review) error { ReviewID: r.ID, } - if _, err := sess.Where(opts.toConds()).Delete(new(Comment)); err != nil { + if _, err := sess.Where(opts.ToConds()).Delete(new(Comment)); err != nil { return err } @@ -982,7 +982,7 @@ func DeleteReview(r *Review) error { ReviewID: r.ID, } - if _, err := sess.Where(opts.toConds()).Delete(new(Comment)); err != nil { + if _, err := sess.Where(opts.ToConds()).Delete(new(Comment)); err != nil { return err } @@ -1006,7 +1006,7 @@ func (r *Review) GetCodeCommentsCount() int { IssueID: r.IssueID, ReviewID: r.ID, } - conds := opts.toConds() + conds := opts.ToConds() if r.ID == 0 { conds = conds.And(builder.Eq{"invalidated": false}) } @@ -1026,7 +1026,7 @@ func (r *Review) HTMLURL() string { ReviewID: r.ID, } comment := new(Comment) - has, err := db.GetEngine(db.DefaultContext).Where(opts.toConds()).Get(comment) + has, err := db.GetEngine(db.DefaultContext).Where(opts.ToConds()).Get(comment) if err != nil || !has { return "" } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 4e211617c0..2058fcec0f 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -451,6 +451,8 @@ var migrations = []Migration{ NewMigration("Drop ForeignReference table", v1_19.DropForeignReferenceTable), // v238 -> v239 NewMigration("Add updated unix to LFSMetaObject", v1_19.AddUpdatedUnixToLFSMetaObject), + // v239 -> v240 + NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go new file mode 100644 index 0000000000..10076f2401 --- /dev/null +++ b/models/migrations/v1_19/v239.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_19 //nolint + +import ( + "xorm.io/xorm" +) + +func AddScopeForAccessTokens(x *xorm.Engine) error { + type AccessToken struct { + Scope string + } + + if err := x.Sync(new(AccessToken)); err != nil { + return err + } + + // all previous tokens have `all` and `sudo` scopes + _, err := x.Exec("UPDATE access_token SET scope = ? WHERE scope IS NULL OR scope = ''", "all,sudo") + return err +} diff --git a/models/organization/team.go b/models/organization/team.go index 55d3f17276..0c2577dab1 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -16,8 +16,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" - - "xorm.io/builder" ) // ___________ @@ -96,59 +94,6 @@ func init() { db.RegisterModel(new(TeamInvite)) } -// SearchTeamOptions holds the search options -type SearchTeamOptions struct { - db.ListOptions - UserID int64 - Keyword string - OrgID int64 - IncludeDesc bool -} - -func (opts *SearchTeamOptions) toCond() builder.Cond { - cond := builder.NewCond() - - if len(opts.Keyword) > 0 { - lowerKeyword := strings.ToLower(opts.Keyword) - var keywordCond builder.Cond = builder.Like{"lower_name", lowerKeyword} - if opts.IncludeDesc { - keywordCond = keywordCond.Or(builder.Like{"LOWER(description)", lowerKeyword}) - } - cond = cond.And(keywordCond) - } - - if opts.OrgID > 0 { - cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID}) - } - - if opts.UserID > 0 { - cond = cond.And(builder.Eq{"team_user.uid": opts.UserID}) - } - - return cond -} - -// SearchTeam search for teams. Caller is responsible to check permissions. -func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) { - sess := db.GetEngine(db.DefaultContext) - - opts.SetDefaultValues() - cond := opts.toCond() - - if opts.UserID > 0 { - sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id") - } - sess = db.SetSessionPagination(sess, opts) - - teams := make([]*Team, 0, opts.PageSize) - count, err := sess.Where(cond).OrderBy("lower_name").FindAndCount(&teams) - if err != nil { - return nil, 0, err - } - - return teams, count, nil -} - // ColorFormat provides a basic color format for a Team func (t *Team) ColorFormat(s fmt.State) { if t == nil { @@ -335,16 +280,6 @@ func GetTeamNamesByID(teamIDs []int64) ([]string, error) { return teamNames, err } -// GetRepoTeams gets the list of teams that has access to the repository -func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams []*Team, err error) { - return teams, db.GetEngine(ctx). - Join("INNER", "team_repo", "team_repo.team_id = team.id"). - Where("team.org_id = ?", repo.OwnerID). - And("team_repo.repo_id=?", repo.ID). - OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END"). - Find(&teams) -} - // IncrTeamRepoNum increases the number of repos for the given team by 1 func IncrTeamRepoNum(ctx context.Context, teamID int64) error { _, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team)) diff --git a/models/organization/team_list.go b/models/organization/team_list.go new file mode 100644 index 0000000000..5d3bd555cc --- /dev/null +++ b/models/organization/team_list.go @@ -0,0 +1,128 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package organization + +import ( + "context" + "strings" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/perm" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" + + "xorm.io/builder" +) + +type TeamList []*Team + +func (t TeamList) LoadUnits(ctx context.Context) error { + for _, team := range t { + if err := team.getUnits(ctx); err != nil { + return err + } + } + return nil +} + +func (t TeamList) UnitMaxAccess(tp unit.Type) perm.AccessMode { + maxAccess := perm.AccessModeNone + for _, team := range t { + if team.IsOwnerTeam() { + return perm.AccessModeOwner + } + for _, teamUnit := range team.Units { + if teamUnit.Type != tp { + continue + } + if teamUnit.AccessMode > maxAccess { + maxAccess = teamUnit.AccessMode + } + } + } + return maxAccess +} + +// SearchTeamOptions holds the search options +type SearchTeamOptions struct { + db.ListOptions + UserID int64 + Keyword string + OrgID int64 + IncludeDesc bool +} + +func (opts *SearchTeamOptions) toCond() builder.Cond { + cond := builder.NewCond() + + if len(opts.Keyword) > 0 { + lowerKeyword := strings.ToLower(opts.Keyword) + var keywordCond builder.Cond = builder.Like{"lower_name", lowerKeyword} + if opts.IncludeDesc { + keywordCond = keywordCond.Or(builder.Like{"LOWER(description)", lowerKeyword}) + } + cond = cond.And(keywordCond) + } + + if opts.OrgID > 0 { + cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID}) + } + + if opts.UserID > 0 { + cond = cond.And(builder.Eq{"team_user.uid": opts.UserID}) + } + + return cond +} + +// SearchTeam search for teams. Caller is responsible to check permissions. +func SearchTeam(opts *SearchTeamOptions) (TeamList, int64, error) { + sess := db.GetEngine(db.DefaultContext) + + opts.SetDefaultValues() + cond := opts.toCond() + + if opts.UserID > 0 { + sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id") + } + sess = db.SetSessionPagination(sess, opts) + + teams := make([]*Team, 0, opts.PageSize) + count, err := sess.Where(cond).OrderBy("lower_name").FindAndCount(&teams) + if err != nil { + return nil, 0, err + } + + return teams, count, nil +} + +// GetRepoTeams gets the list of teams that has access to the repository +func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams TeamList, err error) { + return teams, db.GetEngine(ctx). + Join("INNER", "team_repo", "team_repo.team_id = team.id"). + Where("team.org_id = ?", repo.OwnerID). + And("team_repo.repo_id=?", repo.ID). + OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END"). + Find(&teams) +} + +// GetUserOrgTeams returns all teams that user belongs to in given organization. +func GetUserOrgTeams(ctx context.Context, orgID, userID int64) (teams TeamList, err error) { + return teams, db.GetEngine(ctx). + Join("INNER", "team_user", "team_user.team_id = team.id"). + Where("team.org_id = ?", orgID). + And("team_user.uid=?", userID). + Find(&teams) +} + +// GetUserRepoTeams returns user repo's teams +func GetUserRepoTeams(ctx context.Context, orgID, userID, repoID int64) (teams TeamList, err error) { + return teams, db.GetEngine(ctx). + Join("INNER", "team_user", "team_user.team_id = team.id"). + Join("INNER", "team_repo", "team_repo.team_id = team.id"). + Where("team.org_id = ?", orgID). + And("team_user.uid=?", userID). + And("team_repo.repo_id=?", repoID). + Find(&teams) +} diff --git a/models/organization/team_user.go b/models/organization/team_user.go index 7a024f1c6d..816daf3d34 100644 --- a/models/organization/team_user.go +++ b/models/organization/team_user.go @@ -72,26 +72,6 @@ func GetTeamMembers(ctx context.Context, opts *SearchMembersOptions) ([]*user_mo return members, nil } -// GetUserOrgTeams returns all teams that user belongs to in given organization. -func GetUserOrgTeams(ctx context.Context, orgID, userID int64) (teams []*Team, err error) { - return teams, db.GetEngine(ctx). - Join("INNER", "team_user", "team_user.team_id = team.id"). - Where("team.org_id = ?", orgID). - And("team_user.uid=?", userID). - Find(&teams) -} - -// GetUserRepoTeams returns user repo's teams -func GetUserRepoTeams(ctx context.Context, orgID, userID, repoID int64) (teams []*Team, err error) { - return teams, db.GetEngine(ctx). - Join("INNER", "team_user", "team_user.team_id = team.id"). - Join("INNER", "team_repo", "team_repo.team_id = team.id"). - Where("team.org_id = ?", orgID). - And("team_user.uid=?", userID). - And("team_repo.repo_id=?", repoID). - Find(&teams) -} - // IsUserInTeams returns if a user in some teams func IsUserInTeams(ctx context.Context, userID int64, teamIDs []int64) (bool, error) { return db.GetEngine(ctx).Where("uid=?", userID).In("team_id", teamIDs).Exist(new(TeamUser)) diff --git a/models/packages/package_blob.go b/models/packages/package_blob.go index 3b4a1ecf18..a55109af96 100644 --- a/models/packages/package_blob.go +++ b/models/packages/package_blob.go @@ -85,7 +85,16 @@ func DeleteBlobByID(ctx context.Context, blobID int64) error { } // GetTotalBlobSize returns the total blobs size in bytes -func GetTotalBlobSize() (int64, error) { - return db.GetEngine(db.DefaultContext). +func GetTotalBlobSize(ctx context.Context) (int64, error) { + return db.GetEngine(ctx). + SumInt(&PackageBlob{}, "size") +} + +// GetTotalUnreferencedBlobSize returns the total size of all unreferenced blobs in bytes +func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) { + return db.GetEngine(ctx). + Table("package_blob"). + Join("LEFT", "package_file", "package_file.blob_id = package_blob.id"). + Where("package_file.id IS NULL"). SumInt(&PackageBlob{}, "size") } diff --git a/models/packages/package_file.go b/models/packages/package_file.go index 7f794836dc..97e7a0d407 100644 --- a/models/packages/package_file.go +++ b/models/packages/package_file.go @@ -199,9 +199,9 @@ func SearchFiles(ctx context.Context, opts *PackageFileSearchOptions) ([]*Packag return pfs, count, err } -// CalculateBlobSize sums up all blob sizes matching the search options. +// CalculateFileSize sums up all blob sizes matching the search options. // It does NOT respect the deduplication of blobs. -func CalculateBlobSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) { +func CalculateFileSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) { return db.GetEngine(ctx). Table("package_file"). Where(opts.toConds()). diff --git a/models/project/project.go b/models/project/project.go index f432d0bc4c..273823ac9d 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -8,6 +8,9 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -78,12 +81,15 @@ func (err ErrProjectBoardNotExist) Unwrap() error { // Project represents a project board type Project struct { - ID int64 `xorm:"pk autoincr"` - Title string `xorm:"INDEX NOT NULL"` - Description string `xorm:"TEXT"` - RepoID int64 `xorm:"INDEX"` - CreatorID int64 `xorm:"NOT NULL"` - IsClosed bool `xorm:"INDEX"` + ID int64 `xorm:"pk autoincr"` + Title string `xorm:"INDEX NOT NULL"` + Description string `xorm:"TEXT"` + OwnerID int64 `xorm:"INDEX"` + Owner *user_model.User `xorm:"-"` + RepoID int64 `xorm:"INDEX"` + Repo *repo_model.Repository `xorm:"-"` + CreatorID int64 `xorm:"NOT NULL"` + IsClosed bool `xorm:"INDEX"` BoardType BoardType Type Type @@ -94,6 +100,46 @@ type Project struct { ClosedDateUnix timeutil.TimeStamp } +func (p *Project) LoadOwner(ctx context.Context) (err error) { + if p.Owner != nil { + return nil + } + p.Owner, err = user_model.GetUserByID(ctx, p.OwnerID) + return err +} + +func (p *Project) LoadRepo(ctx context.Context) (err error) { + if p.RepoID == 0 || p.Repo != nil { + return nil + } + p.Repo, err = repo_model.GetRepositoryByID(ctx, p.RepoID) + return err +} + +func (p *Project) Link() string { + if p.OwnerID > 0 { + err := p.LoadOwner(db.DefaultContext) + if err != nil { + log.Error("LoadOwner: %v", err) + return "" + } + return fmt.Sprintf("%s/-/projects/%d", p.Owner.HomeLink(), p.ID) + } + if p.RepoID > 0 { + err := p.LoadRepo(db.DefaultContext) + if err != nil { + log.Error("LoadRepo: %v", err) + return "" + } + return fmt.Sprintf("%s/projects/%d", p.Repo.Link(), p.ID) + } + return "" +} + +func (p *Project) IsOrganizationProject() bool { + return p.Type == TypeOrganization +} + func init() { db.RegisterModel(new(Project)) } @@ -110,7 +156,7 @@ func GetProjectsConfig() []ProjectsConfig { // IsTypeValid checks if a project type is valid func IsTypeValid(p Type) bool { switch p { - case TypeRepository: + case TypeRepository, TypeOrganization: return true default: return false @@ -119,6 +165,7 @@ func IsTypeValid(p Type) bool { // SearchOptions are options for GetProjects type SearchOptions struct { + OwnerID int64 RepoID int64 Page int IsClosed util.OptionalBool @@ -126,12 +173,11 @@ type SearchOptions struct { Type Type } -// GetProjects returns a list of all projects that have been created in the repository -func GetProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { - e := db.GetEngine(ctx) - projects := make([]*Project, 0, setting.UI.IssuePagingNum) - - var cond builder.Cond = builder.Eq{"repo_id": opts.RepoID} +func (opts *SearchOptions) toConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } switch opts.IsClosed { case util.OptionalBoolTrue: cond = cond.And(builder.Eq{"is_closed": true}) @@ -142,6 +188,22 @@ func GetProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, er if opts.Type > 0 { cond = cond.And(builder.Eq{"type": opts.Type}) } + if opts.OwnerID > 0 { + cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) + } + return cond +} + +// CountProjects counts projects +func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { + return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Project)) +} + +// FindProjects returns a list of all projects that have been created in the repository +func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { + e := db.GetEngine(ctx) + projects := make([]*Project, 0, setting.UI.IssuePagingNum) + cond := opts.toConds() count, err := e.Where(cond).Count(new(Project)) if err != nil { @@ -188,8 +250,10 @@ func NewProject(p *Project) error { return err } - if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil { - return err + if p.RepoID > 0 { + if _, err := db.Exec(ctx, "UPDATE `repository` SET num_projects = num_projects + 1 WHERE id = ?", p.RepoID); err != nil { + return err + } } if err := createBoardsForProjectsType(ctx, p); err != nil { diff --git a/models/project/project_test.go b/models/project/project_test.go index 4fde0fc7ce..c2d9005c43 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -22,7 +22,7 @@ func TestIsProjectTypeValid(t *testing.T) { }{ {TypeIndividual, false}, {TypeRepository, true}, - {TypeOrganization, false}, + {TypeOrganization, true}, {UnknownType, false}, } @@ -34,13 +34,13 @@ func TestIsProjectTypeValid(t *testing.T) { func TestGetProjects(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - projects, _, err := GetProjects(db.DefaultContext, SearchOptions{RepoID: 1}) + projects, _, err := FindProjects(db.DefaultContext, SearchOptions{RepoID: 1}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures assert.Len(t, projects, 1) - projects, _, err = GetProjects(db.DefaultContext, SearchOptions{RepoID: 3}) + projects, _, err = FindProjects(db.DefaultContext, SearchOptions{RepoID: 3}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures diff --git a/models/system/setting.go b/models/system/setting.go index 0701c4bf48..50fe17498e 100644 --- a/models/system/setting.go +++ b/models/system/setting.go @@ -268,6 +268,16 @@ func Init() error { if setting_module.OfflineMode { disableGravatar = true enableFederatedAvatar = false + if !GetSettingBool(KeyPictureDisableGravatar) { + if err := SetSettingNoVersion(KeyPictureDisableGravatar, "true"); err != nil { + return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err) + } + } + if GetSettingBool(KeyPictureEnableFederatedAvatar) { + if err := SetSettingNoVersion(KeyPictureEnableFederatedAvatar, "false"); err != nil { + return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err) + } + } } if enableFederatedAvatar || !disableGravatar { diff --git a/models/user/setting.go b/models/user/setting.go index f5cfef5b33..aec79b756b 100644 --- a/models/user/setting.go +++ b/models/user/setting.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/cache" + setting_module "code.gitea.io/gitea/modules/setting" "xorm.io/builder" ) @@ -154,11 +155,16 @@ func SetUserSetting(userID int64, key, value string) error { return err } - _, err := cache.GetString(genSettingCacheKey(userID, key), func() (string, error) { - return value, upsertUserSettingValue(userID, key, value) - }) + if err := upsertUserSettingValue(userID, key, value); err != nil { + return err + } - return err + cc := cache.GetCache() + if cc != nil { + return cc.Put(genSettingCacheKey(userID, key), value, setting_module.CacheService.TTLSeconds()) + } + + return nil } func upsertUserSettingValue(userID int64, key, value string) error { diff --git a/models/user/user.go b/models/user/user.go index 040f471c15..2c76f1763b 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -1257,7 +1257,10 @@ func GetUserByOpenID(uri string) (*User, error) { // GetAdminUser returns the first administrator func GetAdminUser() (*User, error) { var admin User - has, err := db.GetEngine(db.DefaultContext).Where("is_admin=?", true).Get(&admin) + has, err := db.GetEngine(db.DefaultContext). + Where("is_admin=?", true). + Asc("id"). // Reliably get the admin with the lowest ID. + Get(&admin) if err != nil { return nil, err } else if !has { diff --git a/modules/context/org.go b/modules/context/org.go index 39df29a860..ff3a5ae7ec 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -9,7 +9,9 @@ import ( "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" ) @@ -28,6 +30,32 @@ type Organization struct { Teams []*organization.Team } +func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool { + if ctx.Doer == nil { + return false + } + return org.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeWrite +} + +func (org *Organization) UnitPermission(ctx *Context, doerID int64, unitType unit.Type) perm.AccessMode { + if doerID > 0 { + teams, err := organization.GetUserOrgTeams(ctx, org.Organization.ID, doerID) + if err != nil { + log.Error("GetUserOrgTeams: %v", err) + return perm.AccessModeNone + } + if len(teams) > 0 { + return teams.UnitMaxAccess(unitType) + } + } + + if org.Organization.Visibility == structs.VisibleTypePublic { + return perm.AccessModeRead + } + + return perm.AccessModeNone +} + // HandleOrgAssignment handles organization assignment func HandleOrgAssignment(ctx *Context, args ...bool) { var ( diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go index f8bce3f465..0f19d87e8d 100644 --- a/modules/issue/template/template.go +++ b/modules/issue/template/template.go @@ -259,7 +259,9 @@ func (f *valuedField) WriteTo(builder *strings.Builder) { } // write label - _, _ = fmt.Fprintf(builder, "### %s\n\n", f.Label()) + if !f.HideLabel() { + _, _ = fmt.Fprintf(builder, "### %s\n\n", f.Label()) + } blankPlaceholder := "_No response_\n" @@ -311,6 +313,13 @@ func (f *valuedField) Label() string { return "" } +func (f *valuedField) HideLabel() bool { + if label, ok := f.Attributes["hide_label"].(bool); ok { + return label + } + return false +} + func (f *valuedField) Render() string { if render, ok := f.Attributes["render"].(string); ok { return render diff --git a/modules/issue/template/template_test.go b/modules/issue/template/template_test.go index 0845642cd3..0cdddd0c85 100644 --- a/modules/issue/template/template_test.go +++ b/modules/issue/template/template_test.go @@ -640,6 +640,7 @@ body: description: Description of input placeholder: Placeholder of input value: Value of input + hide_label: true validations: required: true is_number: true @@ -681,8 +682,6 @@ body: ` + "```bash\nValue of id2\n```" + ` -### Label of input - Value of id3 ### Label of dropdown diff --git a/modules/markup/asciicast/asciicast.go b/modules/markup/asciicast/asciicast.go new file mode 100644 index 0000000000..0678062340 --- /dev/null +++ b/modules/markup/asciicast/asciicast.go @@ -0,0 +1,64 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package asciicast + +import ( + "fmt" + "io" + "net/url" + "regexp" + + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/setting" +) + +func init() { + markup.RegisterRenderer(Renderer{}) +} + +// Renderer implements markup.Renderer for asciicast files. +// See https://github.com/asciinema/asciinema/blob/develop/doc/asciicast-v2.md +type Renderer struct{} + +// Name implements markup.Renderer +func (Renderer) Name() string { + return "asciicast" +} + +// Extensions implements markup.Renderer +func (Renderer) Extensions() []string { + return []string{".cast"} +} + +const ( + playerClassName = "asciinema-player-container" + playerSrcAttr = "data-asciinema-player-src" +) + +// SanitizerRules implements markup.Renderer +func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule { + return []setting.MarkupSanitizerRule{ + {Element: "div", AllowAttr: "class", Regexp: regexp.MustCompile(playerClassName)}, + {Element: "div", AllowAttr: playerSrcAttr}, + } +} + +// Render implements markup.Renderer +func (Renderer) Render(ctx *markup.RenderContext, _ io.Reader, output io.Writer) error { + rawURL := fmt.Sprintf("%s/%s/%s/raw/%s/%s", + setting.AppSubURL, + url.PathEscape(ctx.Metas["user"]), + url.PathEscape(ctx.Metas["repo"]), + ctx.Metas["BranchNameSubURL"], + url.PathEscape(ctx.RelativePath), + ) + + _, err := io.WriteString(output, fmt.Sprintf( + `
`, + playerClassName, + playerSrcAttr, + rawURL, + )) + return err +} diff --git a/modules/migration/comment.go b/modules/migration/comment.go index 27ecaa830a..f994e972ed 100644 --- a/modules/migration/comment.go +++ b/modules/migration/comment.go @@ -17,6 +17,7 @@ type Commentable interface { type Comment struct { IssueIndex int64 `yaml:"issue_index"` Index int64 + CommentType string `yaml:"comment_type"` // see `commentStrings` in models/issues/comment.go PosterID int64 `yaml:"poster_id"` PosterName string `yaml:"poster_name"` PosterEmail string `yaml:"poster_email"` @@ -24,6 +25,7 @@ type Comment struct { Updated time.Time Content string Reactions []*Reaction + Meta map[string]interface{} `yaml:"meta,omitempty"` // see models/issues/comment.go for fields in Comment struct } // GetExternalName ExternalUserMigrated interface diff --git a/modules/migration/file_format.go b/modules/migration/file_format.go index f319f02ef1..04e5d76981 100644 --- a/modules/migration/file_format.go +++ b/modules/migration/file_format.go @@ -76,7 +76,7 @@ func validate(bs []byte, datatype interface{}, isJSON bool) error { } err = sch.Validate(v) if err != nil { - log.Error("migration validation with %s failed for\n%s", schemaFilename, string(bs)) + log.Error("migration validation with %s failed:\n%#v", schemaFilename, err) } return err } diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go index 2f882c2cb8..c043ef62d5 100644 --- a/modules/notification/action/action.go +++ b/modules/notification/action/action.go @@ -56,7 +56,7 @@ func (a *actionNotifier) NotifyNewIssue(ctx context.Context, issue *issues_model } // NotifyIssueChangeStatus notifies close or reopen issue to notifiers -func (a *actionNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) { +func (a *actionNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) { // Compose comment action, could be plain comment, close or reopen issue/pull request. // This object will be used to notify watchers in the end of function. act := &activities_model.Action{ diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go index dbed20ba3a..4021bbe141 100644 --- a/modules/notification/base/notifier.go +++ b/modules/notification/base/notifier.go @@ -23,7 +23,7 @@ type Notifier interface { NotifyRenameRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldRepoName string) NotifyTransferRepository(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) NotifyNewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) - NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) + NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) NotifyDeleteIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) NotifyIssueChangeMilestone(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) NotifyIssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index de5f072d24..161eadfbec 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -32,7 +32,7 @@ func (*NullNotifier) NotifyNewIssue(ctx context.Context, issue *issues_model.Iss } // NotifyIssueChangeStatus places a place holder function -func (*NullNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { +func (*NullNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { } // NotifyDeleteIssue notify when some issue deleted diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go index 18f7fa22ae..7e54df44c4 100644 --- a/modules/notification/mail/mail.go +++ b/modules/notification/mail/mail.go @@ -54,7 +54,7 @@ func (m *mailNotifier) NotifyNewIssue(ctx context.Context, issue *issues_model.I } } -func (m *mailNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { +func (m *mailNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { var actionType activities_model.ActionType if issue.IsPull { if isClosed { diff --git a/modules/notification/notification.go b/modules/notification/notification.go index 10581eb87f..2300b68f78 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -77,9 +77,9 @@ func NotifyNewIssue(ctx context.Context, issue *issues_model.Issue, mentions []* } // NotifyIssueChangeStatus notifies close or reopen issue to notifiers -func NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) { +func NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) { for _, notifier := range notifiers { - notifier.NotifyIssueChangeStatus(ctx, doer, issue, actionComment, closeOrReopen) + notifier.NotifyIssueChangeStatus(ctx, doer, commitID, issue, actionComment, closeOrReopen) } } diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index bc66c3d5a3..4b85f17b6c 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -93,7 +93,7 @@ func (ns *notificationService) NotifyNewIssue(ctx context.Context, issue *issues } } -func (ns *notificationService) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { +func (ns *notificationService) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { _ = ns.issueQueue.Push(issueNotificationOpts{ IssueID: issue.ID, NotificationAuthorID: doer.ID, diff --git a/modules/structs/hook.go b/modules/structs/hook.go index b722e32ca0..df5da6790f 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -352,6 +352,7 @@ type IssuePayload struct { Issue *Issue `json:"issue"` Repository *Repository `json:"repository"` Sender *User `json:"sender"` + CommitID string `json:"commit_id"` } // JSONPayload encodes the IssuePayload to JSON, with an indentation of two spaces. @@ -386,6 +387,7 @@ type PullRequestPayload struct { PullRequest *PullRequest `json:"pull_request"` Repository *Repository `json:"repository"` Sender *User `json:"sender"` + CommitID string `json:"commit_id"` Review *ReviewPayload `json:"review"` } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 1cd4e6a1d0..43a8aeb08e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -747,6 +747,7 @@ access_token_deletion_cancel_action = Cancel access_token_deletion_confirm_action = Delete access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. This cannot be undone. Continue? delete_token_success = The token has been deleted. Applications using it no longer have access to your account. +select_scopes = Select scopes manage_oauth2_applications = Manage OAuth2 Applications edit_oauth2_application = Edit OAuth2 Application @@ -2644,6 +2645,7 @@ repos.size = Size packages.package_manage_panel = Package Management packages.total_size = Total Size: %s +packages.unreferenced_size = Unreferenced Size: %s packages.owner = Owner packages.creator = Creator packages.name = Name diff --git a/package-lock.json b/package-lock.json index a301dcddb3..60f0a0f8e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,23 +7,24 @@ "name": "gitea", "license": "MIT", "dependencies": { - "@citation-js/core": "0.6.1", - "@citation-js/plugin-bibtex": "0.6.1", - "@citation-js/plugin-csl": "0.6.4", + "@citation-js/core": "0.6.5", + "@citation-js/plugin-bibtex": "0.6.5", + "@citation-js/plugin-csl": "0.6.5", "@citation-js/plugin-software-formats": "0.6.0", "@claviska/jquery-minicolors": "2.3.6", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", - "@primer/octicons": "17.10.0", + "@primer/octicons": "17.10.2", "@vue/compiler-sfc": "3.2.45", "add-asset-webpack-plugin": "2.0.1", + "asciinema-player": "3.0.1", "css-loader": "6.7.3", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "2.20.0", + "esbuild-loader": "2.21.0", "escape-goat": "4.0.0", "fast-glob": "3.2.12", "font-awesome": "4.7.0", - "jquery": "3.6.2", + "jquery": "3.6.3", "jquery.are-you-sure": "1.9.0", "katex": "0.16.4", "less": "4.1.3", @@ -51,24 +52,24 @@ "wrap-ansi": "8.0.1" }, "devDependencies": { - "@playwright/test": "1.29.0", + "@playwright/test": "1.29.2", "@rollup/pluginutils": "5.0.2", "@stoplight/spectral-cli": "6.6.0", - "eslint": "8.30.0", - "eslint-plugin-import": "2.26.0", + "eslint": "8.32.0", + "eslint-plugin-import": "2.27.5", "eslint-plugin-jquery": "1.5.1", - "eslint-plugin-sonarjs": "0.17.0", + "eslint-plugin-sonarjs": "0.18.0", "eslint-plugin-unicorn": "45.0.2", - "eslint-plugin-vue": "9.8.0", - "jsdom": "20.0.3", - "markdownlint-cli": "0.32.2", + "eslint-plugin-vue": "9.9.0", + "jsdom": "21.0.0", + "markdownlint-cli": "0.33.0", "postcss-less": "6.0.0", - "stylelint": "14.16.0", + "stylelint": "14.16.1", "stylelint-config-standard": "29.0.0", "stylelint-declaration-strict-value": "1.9.1", "svgo": "3.0.2", - "updates": "13.2.4", - "vitest": "0.26.1" + "updates": "13.2.7", + "vitest": "0.27.2" }, "engines": { "node": ">= 14.0.0" @@ -187,9 +188,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -197,15 +198,26 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@braintree/sanitize-url": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", "integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==" }, "node_modules/@citation-js/core": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.6.1.tgz", - "integrity": "sha512-zvVxsAP4ciVHiZ60TmKTfjui4m6xeISSp/rtIhOcvZxZ70bBfkt83+kGnuI4xRlhB/oUrZN2fC9BSRKdivSobQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.6.5.tgz", + "integrity": "sha512-YmfL3wby/oLgggs3hqRcllL0xYOUzTaABChTEEbcfXwrvIstgHzODG1tcPAVg/EVuVH151uMR9xttuzu+Lbxcg==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -233,9 +245,9 @@ } }, "node_modules/@citation-js/plugin-bibtex": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.6.1.tgz", - "integrity": "sha512-JMw9h9MUXH7YWvgN0j+A5xI4Fw3cHYcDMzpweeAcXBfjfnC6q30Dyvs2YxfUxNEKvWDgRQjAiNNIzgWXs9uK1Q==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.6.5.tgz", + "integrity": "sha512-J4zdfScylT6jiTdUxMiBndV1ERDvq87IE5lm9blIMesPAIdf4bN++n0tNczNnJy6TbdlNK9tB0HTwIJe6J/GnA==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -249,9 +261,9 @@ } }, "node_modules/@citation-js/plugin-csl": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.6.4.tgz", - "integrity": "sha512-RG4NrFIx0CZTfNeMCC8CL7UGFRiUv5/bNd/Nc6Q/NHx0cS/tYDQcKt0M24dpOI7PAZwVoddbDW4Iakn6nS4QsQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.6.5.tgz", + "integrity": "sha512-1RcKAJpAF+hWjTebAhO99EGB1z6aL/GMNohZblRrhxpUelQvXp192CAjpS/riXpRDX6acLV7Q/qtcdi3ujzwBw==", "dependencies": { "@citation-js/date": "^0.5.0", "citeproc": "^2.4.6" @@ -285,19 +297,19 @@ } }, "node_modules/@csstools/selector-specificity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.0.tgz", + "integrity": "sha512-zJ6hb3FDgBbO8d2e83vg6zq7tNvDqSq9RwdwfzJ8tdm9JHNvANq2fqwyRn6mlpUb7CwTs5ILdUrGwi9Gk4vY5w==", "dev": true, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2", + "postcss": "^8.4", "postcss-selector-parser": "^6.0.10" } }, @@ -310,9 +322,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz", + "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==", "cpu": [ "arm" ], @@ -325,13 +337,12 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.10.tgz", - "integrity": "sha512-47Y+NwVKTldTlDhSgJHZ/RpvBQMUDG7eKihqaF/u6g7s0ZPz4J1vy8A3rwnnUOF2CuDn7w7Gj/QcMoWz3U3SJw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz", + "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -341,13 +352,12 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.10.tgz", - "integrity": "sha512-C4PfnrBMcuAcOurQzpF1tTtZz94IXO5JmICJJ3NFJRHbXXsQUg9RFG45KvydKqtFfBaFLCHpduUkUfXwIvGnRg==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz", + "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -357,13 +367,12 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.10.tgz", - "integrity": "sha512-bH/bpFwldyOKdi9HSLCLhhKeVgRYr9KblchwXgY2NeUHBB/BzTUHtUSBgGBmpydB1/4E37m+ggXXfSrnD7/E7g==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz", + "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -373,13 +382,12 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.10.tgz", - "integrity": "sha512-OXt7ijoLuy+AjDSKQWu+KdDFMBbdeaL6wtgMKtDUXKWHiAMKHan5+R1QAG6HD4+K0nnOvEJXKHeA9QhXNAjOTQ==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz", + "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -389,13 +397,12 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.10.tgz", - "integrity": "sha512-shSQX/3GHuspE3Uxtq5kcFG/zqC+VuMnJkqV7LczO41cIe6CQaXHD3QdMLA4ziRq/m0vZo7JdterlgbmgNIAlQ==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz", + "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -405,13 +412,12 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.10.tgz", - "integrity": "sha512-5YVc1zdeaJGASijZmTzSO4h6uKzsQGG3pkjI6fuXvolhm3hVRhZwnHJkforaZLmzvNv5Tb7a3QL2FAVmrgySIA==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz", + "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -421,13 +427,12 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.10.tgz", - "integrity": "sha512-c360287ZWI2miBnvIj23bPyVctgzeMT2kQKR+x94pVqIN44h3GF8VMEs1SFPH1UgyDr3yBbx3vowDS1SVhyVhA==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz", + "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==", "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -437,13 +442,12 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.10.tgz", - "integrity": "sha512-2aqeNVxIaRfPcIaMZIFoblLh588sWyCbmj1HHCCs9WmeNWm+EIN0SmvsmPvTa/TsNZFKnxTcvkX2eszTcCqIrA==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz", + "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -453,13 +457,12 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.10.tgz", - "integrity": "sha512-sqMIEWeyrLGU7J5RB5fTkLRIFwsgsQ7ieWXlDLEmC2HblPYGb3AucD7inw2OrKFpRPKsec1l+lssiM3+NV5aOw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz", + "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==", "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -469,9 +472,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz", + "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==", "cpu": [ "loong64" ], @@ -484,13 +487,12 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.10.tgz", - "integrity": "sha512-FN8mZOH7531iPHM0kaFhAOqqNHoAb6r/YHW2ZIxNi0a85UBi2DO4Vuyn7t1p4UN8a4LoAnLOT1PqNgHkgBJgbA==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz", + "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==", "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -500,13 +502,12 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.10.tgz", - "integrity": "sha512-Dg9RiqdvHOAWnOKIOTsIx8dFX9EDlY2IbPEY7YFzchrCiTZmMkD7jWA9UdZbNUygPjdmQBVPRCrLydReFlX9yg==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz", + "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==", "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -516,13 +517,12 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.10.tgz", - "integrity": "sha512-XMqtpjwzbmlar0BJIxmzu/RZ7EWlfVfH68Vadrva0Wj5UKOdKvqskuev2jY2oPV3aoQUyXwnMbMrFmloO2GfAw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz", + "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==", "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -532,13 +532,12 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.10.tgz", - "integrity": "sha512-fu7XtnoeRNFMx8DjK3gPWpFBDM2u5ba+FYwg27SjMJwKvJr4bDyKz5c+FLXLUSSAkMAt/UL+cUbEbra+rYtUgw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz", + "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==", "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -548,13 +547,12 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.10.tgz", - "integrity": "sha512-61lcjVC/RldNNMUzQQdyCWjCxp9YLEQgIxErxU9XluX7juBdGKb0pvddS0vPNuCvotRbzijZ1pzII+26haWzbA==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", + "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -564,13 +562,12 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.10.tgz", - "integrity": "sha512-JeZXCX3viSA9j4HqSoygjssdqYdfHd6yCFWyfSekLbz4Ef+D2EjvsN02ZQPwYl5a5gg/ehdHgegHhlfOFP0HCA==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz", + "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -580,13 +577,12 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.10.tgz", - "integrity": "sha512-3qpxQKuEVIIg8SebpXsp82OBrqjPV/OwNWmG+TnZDr3VGyChNnGMHccC1xkbxCHDQNnnXjxhMQNyHmdFJbmbRA==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz", + "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -596,13 +592,12 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.10.tgz", - "integrity": "sha512-z+q0xZ+et/7etz7WoMyXTHZ1rB8PMSNp/FOqURLJLOPb3GWJ2aj4oCqFCjPwEbW1rsT7JPpxeH/DwGAWk/I1Bg==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz", + "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -612,13 +607,12 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.10.tgz", - "integrity": "sha512-+YYu5sbQ9npkNT9Dec+tn1F/kjg6SMgr6bfi/6FpXYZvCRfu2YFPZGb+3x8K30s8eRxFpoG4sGhiSUkr1xbHEw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz", + "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==", "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -628,13 +622,12 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.10.tgz", - "integrity": "sha512-Aw7Fupk7XNehR1ftHGYwUteyJ2q+em/aE+fVU3YMTBN2V5A7Z4aVCSV+SvCp9HIIHZavPFBpbdP3VfjQpdf6Xg==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz", + "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==", "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -644,13 +637,12 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.10.tgz", - "integrity": "sha512-qddWullt3sC1EIpfHvCRBq3H4g3L86DZpD6n8k2XFjFVyp01D++uNbN1hT/JRsHxTbyyemZcpwL5aRlJwc/zFw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz", + "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -675,9 +667,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", - "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -910,13 +902,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.0.tgz", - "integrity": "sha512-gp5PVBenxTJsm2bATWDNc2CCnrL5OaA/MXQdJwwkGQtqTjmY+ZOqAdLqo49O9MLTDh2vYh+tHWDnmFsILnWaeA==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz", + "integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==", "dev": true, "dependencies": { "@types/node": "*", - "playwright-core": "1.29.0" + "playwright-core": "1.29.2" }, "bin": { "playwright": "cli.js" @@ -935,9 +927,9 @@ } }, "node_modules/@primer/octicons": { - "version": "17.10.0", - "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.10.0.tgz", - "integrity": "sha512-rg+NfA4M/SFutVzsqGwGWoKgXpHpTAbnoGvyGbkswT7iLV0PBFGJRkV61MhC61wEEF4SErMiaH5tOQKlvgvV9A==", + "version": "17.10.2", + "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.10.2.tgz", + "integrity": "sha512-J/p2PcgT39Za4wpukbN6iUkEUvL5aE7Bs9kXBeEkrjEgc0Uu7J7B2ypwx9J0qM3m3lk2273RT5/4oGv8pfFLcg==", "dependencies": { "object-assign": "^4.1.1" } @@ -1463,9 +1455,9 @@ } }, "node_modules/@types/codemirror": { - "version": "5.60.5", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz", - "integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==", + "version": "5.60.6", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz", + "integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==", "dependencies": { "@types/tern": "*" } @@ -1525,9 +1517,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", - "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==" + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -1640,6 +1632,11 @@ "csstype": "^2.6.8" } }, + "node_modules/@vue/runtime-dom/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, "node_modules/@vue/server-renderer": { "version": "3.2.45", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz", @@ -1931,9 +1928,9 @@ } }, "node_modules/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -2076,6 +2073,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -2094,6 +2109,15 @@ "printable-characters": "^1.0.42" } }, + "node_modules/asciinema-player": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.0.1.tgz", + "integrity": "sha512-plm/C/MhOtZWysrfcT/rzxOuu8vxvvDSvF50pqZS6KpJUDmATedAhO54zktbE/g7RiaaYfzgX8xjRhlQdgISwA==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "solid-js": "^1.3.0" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2125,9 +2149,9 @@ } }, "node_modules/astring": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.3.tgz", - "integrity": "sha512-sRpyiNrx2dEYIMmUXprS8nlpRg2Drs8m9ElX9vVEXaCB4XEAJhKfs7IcX0IwShjuOAjLR6wzIrgoptz1n19i1A==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.4.tgz", + "integrity": "sha512-97a+l2LBU3Op3bBQEff79i/E4jMD2ZLFD8rHx9B6mXyB2uQwhJQYfiDqUwtfjF4QA1F2qs//N6Cw8LetMbQjcw==", "dev": true, "bin": { "astring": "bin/astring" @@ -2139,6 +2163,18 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2279,6 +2315,15 @@ "node": ">= 0.8" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -2328,9 +2373,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001439", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", - "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==", + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==", "funding": [ { "type": "opencollective", @@ -2393,10 +2438,16 @@ } }, "node_modules/ci-info": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { "node": ">=8" } @@ -2772,14 +2823,14 @@ "dev": true }, "node_modules/csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/d3": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.7.0.tgz", - "integrity": "sha512-VEwHCMgMjD2WBsxeRGUE18RmzxT9Bn7ghDpzvTEvkLSBAKgTMydJjouZTjspgQfRHpPt/PB3EHWBa6SSyFQq4g==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz", + "integrity": "sha512-WXty7qOGSHb7HR7CfOzwN1Gw04MUOzN8qh9ZUsvwycIMb4DYMpY9xczZ6jUorGtO6bR9BPMPaueIKwiDxu9uiQ==", "dependencies": { "d3-array": "3", "d3-axis": "3", @@ -2817,9 +2868,9 @@ } }, "node_modules/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", "dependencies": { "internmap": "1 - 2" }, @@ -2870,9 +2921,9 @@ } }, "node_modules/d3-contour": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", - "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", "dependencies": { "d3-array": "^3.2.0" }, @@ -2984,9 +3035,9 @@ } }, "node_modules/d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", "dependencies": { "d3-array": "2.5.0 - 3" }, @@ -3081,11 +3132,11 @@ } }, "node_modules/d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "dependencies": { - "d3-path": "1 - 3" + "d3-path": "^3.1.0" }, "engines": { "node": ">=12" @@ -3654,27 +3705,33 @@ } }, "node_modules/es-abstract": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", - "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "dependencies": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", "gopd": "^1.0.1", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", "object-inspect": "^1.12.2", "object-keys": "^1.1.1", @@ -3683,7 +3740,9 @@ "safe-regex-test": "^1.0.0", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", - "unbox-primitive": "^1.0.2" + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" @@ -3718,6 +3777,20 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -3745,9 +3818,9 @@ } }, "node_modules/esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz", + "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3756,251 +3829,41 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" - } - }, - "node_modules/esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "@esbuild/android-arm": "0.16.17", + "@esbuild/android-arm64": "0.16.17", + "@esbuild/android-x64": "0.16.17", + "@esbuild/darwin-arm64": "0.16.17", + "@esbuild/darwin-x64": "0.16.17", + "@esbuild/freebsd-arm64": "0.16.17", + "@esbuild/freebsd-x64": "0.16.17", + "@esbuild/linux-arm": "0.16.17", + "@esbuild/linux-arm64": "0.16.17", + "@esbuild/linux-ia32": "0.16.17", + "@esbuild/linux-loong64": "0.16.17", + "@esbuild/linux-mips64el": "0.16.17", + "@esbuild/linux-ppc64": "0.16.17", + "@esbuild/linux-riscv64": "0.16.17", + "@esbuild/linux-s390x": "0.16.17", + "@esbuild/linux-x64": "0.16.17", + "@esbuild/netbsd-x64": "0.16.17", + "@esbuild/openbsd-x64": "0.16.17", + "@esbuild/sunos-x64": "0.16.17", + "@esbuild/win32-arm64": "0.16.17", + "@esbuild/win32-ia32": "0.16.17", + "@esbuild/win32-x64": "0.16.17" } }, "node_modules/esbuild-loader": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz", - "integrity": "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.21.0.tgz", + "integrity": "sha512-k7ijTkCT43YBSZ6+fBCW1Gin7s46RrJ0VQaM8qA7lq7W+OLsGgtLyFV8470FzYi/4TeDexniTBTPTwZUnXXR5g==", "dependencies": { - "esbuild": "^0.15.6", + "esbuild": "^0.16.17", "joycon": "^3.0.1", "json5": "^2.2.0", "loader-utils": "^2.0.0", "tapable": "^2.2.0", - "webpack-sources": "^2.2.0" + "webpack-sources": "^1.4.3" }, "funding": { "url": "https://github.com/privatenumber/esbuild-loader?sponsor=1" @@ -4009,96 +3872,6 @@ "webpack": "^4.40.0 || ^5.0.0" } }, - "node_modules/esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -4204,12 +3977,12 @@ } }, "node_modules/eslint": { - "version": "8.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", - "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", + "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.0", + "@eslint/eslintrc": "^1.4.1", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4260,13 +4033,14 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", "dev": true, "dependencies": { "debug": "^3.2.7", - "resolve": "^1.20.0" + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { @@ -4305,23 +4079,25 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", "dev": true, "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", "has": "^1.0.3", - "is-core-module": "^2.8.1", + "is-core-module": "^2.11.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", "tsconfig-paths": "^3.14.1" }, "engines": { @@ -4332,12 +4108,12 @@ } }, "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import/node_modules/doctrine": { @@ -4352,11 +4128,14 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, "node_modules/eslint-plugin-jquery": { "version": "1.5.1", @@ -4368,9 +4147,9 @@ } }, "node_modules/eslint-plugin-sonarjs": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.17.0.tgz", - "integrity": "sha512-jtGtxI49UbJJeJj7CVRLI3+LLH+y+hkR3GOOwM7vBbci9DEFIRGCWvEd2BJScrzltZ6D6iubukTAfc9cyG7sdw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.18.0.tgz", + "integrity": "sha512-DJ3osLnt6KFdT5e9ZuIDOjT5A6wUGSLeiJJT03lPgpdD+7CVWlYAw9Goe3bt7SmbFO3Xh89NOCZAuB9XA7bAUQ==", "dev": true, "engines": { "node": ">=14" @@ -4413,9 +4192,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.8.0.tgz", - "integrity": "sha512-E/AXwcTzunyzM83C2QqDHxepMzvI2y6x+mmeYHbVDQlKFqmKYvRrhaVixEeeG27uI44p9oKDFiyCRw4XxgtfHA==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.9.0.tgz", + "integrity": "sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==", "dev": true, "dependencies": { "eslint-utils": "^3.0.0", @@ -4653,9 +4432,9 @@ } }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dependencies": { "reusify": "^1.0.4" } @@ -4743,6 +4522,15 @@ "node": ">=0.10.3" } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -5097,9 +4885,9 @@ "dev": true }, "node_modules/gsap": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.3.tgz", - "integrity": "sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA==" + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.4.tgz", + "integrity": "sha512-McHhEguHyExMMnjqKA8G+7TvxmlKQGMbjgwAilnFe1e4id7V/tFveRQ2YMZhTYu0oxHGWvbPltdVYQOu3z1SCA==" }, "node_modules/hard-rejection": { "version": "2.1.0", @@ -5150,6 +4938,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5318,9 +5118,9 @@ } }, "node_modules/immer": { - "version": "9.0.16", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", - "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", + "version": "9.0.18", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", + "integrity": "sha512-eAPNpsj7Ax1q6Y/3lm2PmlwRcFzpON7HSNQ3ru5WQH1/PSpnyed/HpNOELl2CxLKoj4r+bAHgdyKqW5gc2Se1A==", "dev": true, "funding": { "type": "opencollective", @@ -5447,6 +5247,20 @@ "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", "dev": true }, + "node_modules/is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5696,6 +5510,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -5768,9 +5601,9 @@ } }, "node_modules/jquery": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.2.tgz", - "integrity": "sha512-/e7ulNIEEYk1Z/l4X0vpxGt+B/dNsV8ghOPAWZaJs8pkGvsSC0tm33aMGylXcj/U7y4IcvwtMXPMyBFZn/gK9A==" + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz", + "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==" }, "node_modules/jquery.are-you-sure": { "version": "1.9.0", @@ -5811,9 +5644,9 @@ } }, "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.0.0.tgz", + "integrity": "sha512-AIw+3ZakSUtDYvhwPwWHiZsUi3zHugpMEKlNPaurviseYoBqo0zBd3zqoUi3LPCNtPFlEP8FiW9MqCZdjb2IYA==", "dev": true, "dependencies": { "abab": "^2.0.6", @@ -5970,9 +5803,9 @@ } }, "node_modules/klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "engines": { "node": ">= 8" } @@ -6076,15 +5909,6 @@ "semver": "bin/semver.js" } }, - "node_modules/license-checker-webpack-plugin/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, "node_modules/license-checker-webpack-plugin/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -6135,9 +5959,9 @@ } }, "node_modules/local-pkg": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz", - "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", "dev": true, "engines": { "node": ">=14" @@ -6289,32 +6113,31 @@ } }, "node_modules/markdownlint": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.26.2.tgz", - "integrity": "sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.27.0.tgz", + "integrity": "sha512-HtfVr/hzJJmE0C198F99JLaeada+646B5SaG2pVoEakLFI6iRGsvMqrnnrflq8hm1zQgwskEgqSnhDW11JBp0w==", "dev": true, "dependencies": { "markdown-it": "13.0.1" }, "engines": { - "node": ">=14" + "node": ">=14.18.0" } }, "node_modules/markdownlint-cli": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.32.2.tgz", - "integrity": "sha512-xmJT1rGueUgT4yGNwk6D0oqQr90UJ7nMyakXtqjgswAkEhYYqjHew9RY8wDbOmh2R270IWjuKSeZzHDEGPAUkQ==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.33.0.tgz", + "integrity": "sha512-zMK1oHpjYkhjO+94+ngARiBBrRDEUMzooDHBAHtmEIJ9oYddd9l3chCReY2mPlecwH7gflQp1ApilTo+o0zopQ==", "dev": true, "dependencies": { - "commander": "~9.4.0", + "commander": "~9.4.1", "get-stdin": "~9.0.0", "glob": "~8.0.3", - "ignore": "~5.2.0", + "ignore": "~5.2.4", "js-yaml": "^4.1.0", - "jsonc-parser": "~3.1.0", - "markdownlint": "~0.26.2", - "markdownlint-rule-helpers": "~0.17.2", - "minimatch": "~5.1.0", + "jsonc-parser": "~3.2.0", + "markdownlint": "~0.27.0", + "minimatch": "~5.1.2", "run-con": "~1.2.11" }, "bin": { @@ -6362,15 +6185,15 @@ } }, "node_modules/markdownlint-cli/node_modules/jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -6379,19 +6202,10 @@ "node": ">=10" } }, - "node_modules/markdownlint-rule-helpers": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.17.2.tgz", - "integrity": "sha512-XaeoW2NYSlWxMCZM2B3H7YTG6nlaLfkEZWMBhr4hSPlq9MuY2sy83+Xr89jXOqZMZYjvi5nBCGoFh7hHoPKZmA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/marked": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.4.tgz", - "integrity": "sha512-Wcc9ikX7Q5E4BYDPvh1C6QNSxrjC9tBgz+A/vAhp59KXUgachw++uMvMKiSW8oA85nopmPZcEvBoex/YLMsiyA==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "bin": { "marked": "bin/marked.js" }, @@ -6639,15 +6453,15 @@ } }, "node_modules/mlly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.0.0.tgz", - "integrity": "sha512-QL108Hwt+u9bXdWgOI0dhzZfACovn5Aen4Xvc8Jasd9ouRH4NjnrXEiyP3nVvJo91zPlYjVRckta0Nt2zfoR6g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.0.tgz", + "integrity": "sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==", "dev": true, "dependencies": { "acorn": "^8.8.1", "pathe": "^1.0.0", - "pkg-types": "^1.0.0", - "ufo": "^1.0.0" + "pkg-types": "^1.0.1", + "ufo": "^1.0.1" } }, "node_modules/mlly/node_modules/pathe": { @@ -6776,9 +6590,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -6880,9 +6694,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7286,9 +7100,9 @@ "dev": true }, "node_modules/playwright-core": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.0.tgz", - "integrity": "sha512-pboOm1m0RD6z1GtwAbEH60PYRfF87vKdzOSRw2RyO0Y0a7utrMyWN2Au1ojGvQr4umuBMODkKTv607YIRypDSQ==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz", + "integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==", "dev": true, "bin": { "playwright": "cli.js" @@ -7316,9 +7130,9 @@ } }, "node_modules/postcss": { - "version": "8.4.20", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", - "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "funding": [ { "type": "opencollective", @@ -7540,9 +7354,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "engines": { "node": ">=6" } @@ -7754,6 +7568,11 @@ "node": ">=8" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "node_modules/regexp-tree": { "version": "0.1.24", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", @@ -8095,9 +7914,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dependencies": { "randombytes": "^2.1.0" } @@ -8161,6 +7980,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -8249,6 +8074,14 @@ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", "dev": true }, + "node_modules/solid-js": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.6.9.tgz", + "integrity": "sha512-kV3fMmm+1C2J95c8eDOPKGfZHnuAkHUBLG4hX1Xu08bXeAIPqmxuz/QdH3B8SIdTp3EatBVIyA6RCes3hrGzpg==", + "dependencies": { + "csstype": "^3.1.0" + } + }, "node_modules/sortablejs": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", @@ -8352,6 +8185,12 @@ "spdx-ranges": "^2.0.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, "node_modules/stacktracey": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", @@ -8481,9 +8320,9 @@ "dev": true }, "node_modules/stylelint": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.0.tgz", - "integrity": "sha512-X6uTi9DcxjzLV8ZUAjit1vsRtSwcls0nl07c9rqOPzvpA8IvTX/xWEkBRowS0ffevRrqkHa/ThDEu86u73FQDg==", + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", + "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", "dev": true, "dependencies": { "@csstools/selector-specificity": "^2.0.2", @@ -8986,6 +8825,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typo-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.2.tgz", @@ -9067,9 +8920,9 @@ } }, "node_modules/updates": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/updates/-/updates-13.2.4.tgz", - "integrity": "sha512-aQcPi3/kRcMLqLZj6nvy0PZAX3ZhE78IdHoj02SujNqH3/55hgn2i/bHVzPUI86+qWITkt7PyjJHBLDSxDHEyA==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/updates/-/updates-13.2.7.tgz", + "integrity": "sha512-0RbLcGX1zweNWITsX4hd1OjUX+GHqE/bK9czDdmnmt7lVwae4VLMuou3/Uu7YD6oUSjkswU7KNFNa0watI566A==", "dev": true, "bin": { "updates": "bin/updates.js" @@ -9150,9 +9003,9 @@ } }, "node_modules/vite": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.0.2.tgz", - "integrity": "sha512-QJaY3R+tFlTagH0exVqbgkkw45B+/bXVBzF2ZD1KB5Z8RiAoiKo60vSUf6/r4c2Vh9jfGBKM4oBI9b4/1ZJYng==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz", + "integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==", "dev": true, "dependencies": { "esbuild": "^0.16.3", @@ -9199,14 +9052,16 @@ } }, "node_modules/vite-node": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.26.1.tgz", - "integrity": "sha512-5FJSKZZJz48zFRKHE55WyevZe61hLMQEsqGn+ungfd60kxEztFybZ3yG9ToGFtOWNCCy7Vn5EVuXD8bdeHOSdw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.27.2.tgz", + "integrity": "sha512-IDwuVhslF10qCnWOGJui7/2KksAOBHi+UbVo6Pqt4f5lgn+kS2sVvYDsETRG5PSuslisGB5CFGvb9I6FQgymBQ==", "dev": true, "dependencies": { + "cac": "^6.7.14", "debug": "^4.3.4", - "mlly": "^1.0.0", + "mlly": "^1.1.0", "pathe": "^0.2.0", + "picocolors": "^1.0.0", "source-map": "^0.6.1", "source-map-support": "^0.5.21", "vite": "^3.0.0 || ^4.0.0" @@ -9221,79 +9076,10 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.10.tgz", - "integrity": "sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.10.tgz", - "integrity": "sha512-O7Pd5hLEtTg37NC73pfhUOGTjx/+aXu5YoSq3ahCxcN7Bcr2F47mv+kG5t840thnsEzrv0oB70+LJu3gUgchvg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.10.tgz", - "integrity": "sha512-z5dIViHoVnw2l+NCJ3zj5behdXjYvXne9gL18OOivCadXDUhyDkeSvEtLcGVAJW2fNmh33TDUpsi704XYlDodw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.16.10", - "@esbuild/android-arm64": "0.16.10", - "@esbuild/android-x64": "0.16.10", - "@esbuild/darwin-arm64": "0.16.10", - "@esbuild/darwin-x64": "0.16.10", - "@esbuild/freebsd-arm64": "0.16.10", - "@esbuild/freebsd-x64": "0.16.10", - "@esbuild/linux-arm": "0.16.10", - "@esbuild/linux-arm64": "0.16.10", - "@esbuild/linux-ia32": "0.16.10", - "@esbuild/linux-loong64": "0.16.10", - "@esbuild/linux-mips64el": "0.16.10", - "@esbuild/linux-ppc64": "0.16.10", - "@esbuild/linux-riscv64": "0.16.10", - "@esbuild/linux-s390x": "0.16.10", - "@esbuild/linux-x64": "0.16.10", - "@esbuild/netbsd-x64": "0.16.10", - "@esbuild/openbsd-x64": "0.16.10", - "@esbuild/sunos-x64": "0.16.10", - "@esbuild/win32-arm64": "0.16.10", - "@esbuild/win32-ia32": "0.16.10", - "@esbuild/win32-x64": "0.16.10" - } - }, "node_modules/vite/node_modules/rollup": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.7.5.tgz", - "integrity": "sha512-z0ZbqHBtS/et2EEUKMrAl2CoSdwN7ZPzL17UMiKN9RjjqHShTlv7F9J6ZJZJNREYjBh3TvBrdfjkFDIXFNeuiQ==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.10.0.tgz", + "integrity": "sha512-JmRYz44NjC1MjVF2VKxc0M1a97vn+cDxeqWmnwyAF4FvpjK8YFdHpaqvQB+3IxCvX05vJxKZkoMDU8TShhmJVA==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -9307,9 +9093,9 @@ } }, "node_modules/vitest": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.26.1.tgz", - "integrity": "sha512-qTLRnjYmjmJpHlLUtErxtlRqGCe8WItFhGXKklpWivu7CLP9KXN9iTezROe+vf51Kb+BB/fzxK6fUG9DvFGL5Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.27.2.tgz", + "integrity": "sha512-y7tdsL2uaQy+KF18AlmNHZe29ukyFytlxrpSTwwmgLE2XHR/aPucJP9FLjWoqjgqFlXzRAjHlFJLU+HDyI/OsA==", "dev": true, "dependencies": { "@types/chai": "^4.3.4", @@ -9317,16 +9103,19 @@ "@types/node": "*", "acorn": "^8.8.1", "acorn-walk": "^8.2.0", + "cac": "^6.7.14", "chai": "^4.3.7", "debug": "^4.3.4", "local-pkg": "^0.4.2", + "picocolors": "^1.0.0", "source-map": "^0.6.1", "strip-literal": "^1.0.0", "tinybench": "^2.3.1", "tinypool": "^0.3.0", "tinyspy": "^1.0.2", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.26.1" + "vite-node": "0.27.2", + "why-is-node-running": "^2.2.2" }, "bin": { "vitest": "vitest.mjs" @@ -9582,9 +9371,9 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", - "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "engines": { "node": "^12.20.0 || >=14" } @@ -9602,15 +9391,12 @@ } }, "node_modules/webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10.13.0" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" } }, "node_modules/webpack/node_modules/@types/estree": { @@ -9755,6 +9541,42 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -9946,16 +9768,16 @@ } }, "node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -10155,9 +9977,17 @@ } }, "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==" + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", + "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==" + }, + "@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "requires": { + "regenerator-runtime": "^0.13.11" + } }, "@braintree/sanitize-url": { "version": "6.0.2", @@ -10165,9 +9995,9 @@ "integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==" }, "@citation-js/core": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.6.1.tgz", - "integrity": "sha512-zvVxsAP4ciVHiZ60TmKTfjui4m6xeISSp/rtIhOcvZxZ70bBfkt83+kGnuI4xRlhB/oUrZN2fC9BSRKdivSobQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.6.5.tgz", + "integrity": "sha512-YmfL3wby/oLgggs3hqRcllL0xYOUzTaABChTEEbcfXwrvIstgHzODG1tcPAVg/EVuVH151uMR9xttuzu+Lbxcg==", "requires": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -10186,9 +10016,9 @@ "integrity": "sha512-brSPsjs2fOVzSnARLKu0qncn6suWjHVQtrqSUrnqyaRH95r/Ad4wPF5EsoWr+Dx8HzkCGb/ogmoAzfCsqlTwTQ==" }, "@citation-js/plugin-bibtex": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.6.1.tgz", - "integrity": "sha512-JMw9h9MUXH7YWvgN0j+A5xI4Fw3cHYcDMzpweeAcXBfjfnC6q30Dyvs2YxfUxNEKvWDgRQjAiNNIzgWXs9uK1Q==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.6.5.tgz", + "integrity": "sha512-J4zdfScylT6jiTdUxMiBndV1ERDvq87IE5lm9blIMesPAIdf4bN++n0tNczNnJy6TbdlNK9tB0HTwIJe6J/GnA==", "requires": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -10196,9 +10026,9 @@ } }, "@citation-js/plugin-csl": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.6.4.tgz", - "integrity": "sha512-RG4NrFIx0CZTfNeMCC8CL7UGFRiUv5/bNd/Nc6Q/NHx0cS/tYDQcKt0M24dpOI7PAZwVoddbDW4Iakn6nS4QsQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.6.5.tgz", + "integrity": "sha512-1RcKAJpAF+hWjTebAhO99EGB1z6aL/GMNohZblRrhxpUelQvXp192CAjpS/riXpRDX6acLV7Q/qtcdi3ujzwBw==", "requires": { "@citation-js/date": "^0.5.0", "citeproc": "^2.4.6" @@ -10221,9 +10051,9 @@ "requires": {} }, "@csstools/selector-specificity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.0.tgz", + "integrity": "sha512-zJ6hb3FDgBbO8d2e83vg6zq7tNvDqSq9RwdwfzJ8tdm9JHNvANq2fqwyRn6mlpUb7CwTs5ILdUrGwi9Gk4vY5w==", "dev": true, "requires": {} }, @@ -10233,155 +10063,135 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==" }, "@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz", + "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==", "optional": true }, "@esbuild/android-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.10.tgz", - "integrity": "sha512-47Y+NwVKTldTlDhSgJHZ/RpvBQMUDG7eKihqaF/u6g7s0ZPz4J1vy8A3rwnnUOF2CuDn7w7Gj/QcMoWz3U3SJw==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz", + "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==", "optional": true }, "@esbuild/android-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.10.tgz", - "integrity": "sha512-C4PfnrBMcuAcOurQzpF1tTtZz94IXO5JmICJJ3NFJRHbXXsQUg9RFG45KvydKqtFfBaFLCHpduUkUfXwIvGnRg==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz", + "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==", "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.10.tgz", - "integrity": "sha512-bH/bpFwldyOKdi9HSLCLhhKeVgRYr9KblchwXgY2NeUHBB/BzTUHtUSBgGBmpydB1/4E37m+ggXXfSrnD7/E7g==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz", + "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==", "optional": true }, "@esbuild/darwin-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.10.tgz", - "integrity": "sha512-OXt7ijoLuy+AjDSKQWu+KdDFMBbdeaL6wtgMKtDUXKWHiAMKHan5+R1QAG6HD4+K0nnOvEJXKHeA9QhXNAjOTQ==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz", + "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==", "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.10.tgz", - "integrity": "sha512-shSQX/3GHuspE3Uxtq5kcFG/zqC+VuMnJkqV7LczO41cIe6CQaXHD3QdMLA4ziRq/m0vZo7JdterlgbmgNIAlQ==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz", + "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==", "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.10.tgz", - "integrity": "sha512-5YVc1zdeaJGASijZmTzSO4h6uKzsQGG3pkjI6fuXvolhm3hVRhZwnHJkforaZLmzvNv5Tb7a3QL2FAVmrgySIA==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz", + "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==", "optional": true }, "@esbuild/linux-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.10.tgz", - "integrity": "sha512-c360287ZWI2miBnvIj23bPyVctgzeMT2kQKR+x94pVqIN44h3GF8VMEs1SFPH1UgyDr3yBbx3vowDS1SVhyVhA==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz", + "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==", "optional": true }, "@esbuild/linux-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.10.tgz", - "integrity": "sha512-2aqeNVxIaRfPcIaMZIFoblLh588sWyCbmj1HHCCs9WmeNWm+EIN0SmvsmPvTa/TsNZFKnxTcvkX2eszTcCqIrA==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz", + "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==", "optional": true }, "@esbuild/linux-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.10.tgz", - "integrity": "sha512-sqMIEWeyrLGU7J5RB5fTkLRIFwsgsQ7ieWXlDLEmC2HblPYGb3AucD7inw2OrKFpRPKsec1l+lssiM3+NV5aOw==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz", + "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==", "optional": true }, "@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz", + "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==", "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.10.tgz", - "integrity": "sha512-FN8mZOH7531iPHM0kaFhAOqqNHoAb6r/YHW2ZIxNi0a85UBi2DO4Vuyn7t1p4UN8a4LoAnLOT1PqNgHkgBJgbA==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz", + "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==", "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.10.tgz", - "integrity": "sha512-Dg9RiqdvHOAWnOKIOTsIx8dFX9EDlY2IbPEY7YFzchrCiTZmMkD7jWA9UdZbNUygPjdmQBVPRCrLydReFlX9yg==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz", + "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==", "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.10.tgz", - "integrity": "sha512-XMqtpjwzbmlar0BJIxmzu/RZ7EWlfVfH68Vadrva0Wj5UKOdKvqskuev2jY2oPV3aoQUyXwnMbMrFmloO2GfAw==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz", + "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==", "optional": true }, "@esbuild/linux-s390x": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.10.tgz", - "integrity": "sha512-fu7XtnoeRNFMx8DjK3gPWpFBDM2u5ba+FYwg27SjMJwKvJr4bDyKz5c+FLXLUSSAkMAt/UL+cUbEbra+rYtUgw==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz", + "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==", "optional": true }, "@esbuild/linux-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.10.tgz", - "integrity": "sha512-61lcjVC/RldNNMUzQQdyCWjCxp9YLEQgIxErxU9XluX7juBdGKb0pvddS0vPNuCvotRbzijZ1pzII+26haWzbA==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", + "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==", "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.10.tgz", - "integrity": "sha512-JeZXCX3viSA9j4HqSoygjssdqYdfHd6yCFWyfSekLbz4Ef+D2EjvsN02ZQPwYl5a5gg/ehdHgegHhlfOFP0HCA==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz", + "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==", "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.10.tgz", - "integrity": "sha512-3qpxQKuEVIIg8SebpXsp82OBrqjPV/OwNWmG+TnZDr3VGyChNnGMHccC1xkbxCHDQNnnXjxhMQNyHmdFJbmbRA==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz", + "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==", "optional": true }, "@esbuild/sunos-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.10.tgz", - "integrity": "sha512-z+q0xZ+et/7etz7WoMyXTHZ1rB8PMSNp/FOqURLJLOPb3GWJ2aj4oCqFCjPwEbW1rsT7JPpxeH/DwGAWk/I1Bg==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz", + "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==", "optional": true }, "@esbuild/win32-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.10.tgz", - "integrity": "sha512-+YYu5sbQ9npkNT9Dec+tn1F/kjg6SMgr6bfi/6FpXYZvCRfu2YFPZGb+3x8K30s8eRxFpoG4sGhiSUkr1xbHEw==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz", + "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==", "optional": true }, "@esbuild/win32-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.10.tgz", - "integrity": "sha512-Aw7Fupk7XNehR1ftHGYwUteyJ2q+em/aE+fVU3YMTBN2V5A7Z4aVCSV+SvCp9HIIHZavPFBpbdP3VfjQpdf6Xg==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz", + "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==", "optional": true }, "@esbuild/win32-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.10.tgz", - "integrity": "sha512-qddWullt3sC1EIpfHvCRBq3H4g3L86DZpD6n8k2XFjFVyp01D++uNbN1hT/JRsHxTbyyemZcpwL5aRlJwc/zFw==", - "dev": true, + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz", + "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==", "optional": true }, "@eslint-community/eslint-utils": { @@ -10394,9 +10204,9 @@ } }, "@eslint/eslintrc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", - "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -10547,13 +10357,13 @@ } }, "@playwright/test": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.0.tgz", - "integrity": "sha512-gp5PVBenxTJsm2bATWDNc2CCnrL5OaA/MXQdJwwkGQtqTjmY+ZOqAdLqo49O9MLTDh2vYh+tHWDnmFsILnWaeA==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.29.2.tgz", + "integrity": "sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==", "dev": true, "requires": { "@types/node": "*", - "playwright-core": "1.29.0" + "playwright-core": "1.29.2" } }, "@popperjs/core": { @@ -10562,9 +10372,9 @@ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, "@primer/octicons": { - "version": "17.10.0", - "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.10.0.tgz", - "integrity": "sha512-rg+NfA4M/SFutVzsqGwGWoKgXpHpTAbnoGvyGbkswT7iLV0PBFGJRkV61MhC61wEEF4SErMiaH5tOQKlvgvV9A==", + "version": "17.10.2", + "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.10.2.tgz", + "integrity": "sha512-J/p2PcgT39Za4wpukbN6iUkEUvL5aE7Bs9kXBeEkrjEgc0Uu7J7B2ypwx9J0qM3m3lk2273RT5/4oGv8pfFLcg==", "requires": { "object-assign": "^4.1.1" } @@ -11001,9 +10811,9 @@ } }, "@types/codemirror": { - "version": "5.60.5", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz", - "integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==", + "version": "5.60.6", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.6.tgz", + "integrity": "sha512-JIDPSvkYRlcv/2F0erqD+de2ni/Mz6FJMEGb0vwF6ByQOcHIKfiEfwrO4d6dSRwYeHyNUMpGjev0PyjX2M0XWw==", "requires": { "@types/tern": "*" } @@ -11063,9 +10873,9 @@ "dev": true }, "@types/node": { - "version": "18.11.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", - "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==" + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, "@types/normalize-package-data": { "version": "2.4.1", @@ -11176,6 +10986,13 @@ "@vue/runtime-core": "3.2.45", "@vue/shared": "3.2.45", "csstype": "^2.6.8" + }, + "dependencies": { + "csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + } } }, "@vue/server-renderer": { @@ -11416,9 +11233,9 @@ } }, "ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -11510,6 +11327,18 @@ "es-shim-unscopables": "^1.0.0" } }, + "array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -11525,6 +11354,15 @@ "printable-characters": "^1.0.42" } }, + "asciinema-player": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.0.1.tgz", + "integrity": "sha512-plm/C/MhOtZWysrfcT/rzxOuu8vxvvDSvF50pqZS6KpJUDmATedAhO54zktbE/g7RiaaYfzgX8xjRhlQdgISwA==", + "requires": { + "@babel/runtime": "^7.15.4", + "solid-js": "^1.3.0" + } + }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -11547,9 +11385,9 @@ "dev": true }, "astring": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.3.tgz", - "integrity": "sha512-sRpyiNrx2dEYIMmUXprS8nlpRg2Drs8m9ElX9vVEXaCB4XEAJhKfs7IcX0IwShjuOAjLR6wzIrgoptz1n19i1A==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.4.tgz", + "integrity": "sha512-97a+l2LBU3Op3bBQEff79i/E4jMD2ZLFD8rHx9B6mXyB2uQwhJQYfiDqUwtfjF4QA1F2qs//N6Cw8LetMbQjcw==", "dev": true }, "asynckit": { @@ -11558,6 +11396,12 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -11639,6 +11483,12 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -11673,9 +11523,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001439", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", - "integrity": "sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==" + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==" }, "chai": { "version": "4.3.7", @@ -11713,9 +11563,9 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" }, "ci-info": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", "dev": true }, "citeproc": { @@ -12020,14 +11870,14 @@ } }, "csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "d3": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.7.0.tgz", - "integrity": "sha512-VEwHCMgMjD2WBsxeRGUE18RmzxT9Bn7ghDpzvTEvkLSBAKgTMydJjouZTjspgQfRHpPt/PB3EHWBa6SSyFQq4g==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz", + "integrity": "sha512-WXty7qOGSHb7HR7CfOzwN1Gw04MUOzN8qh9ZUsvwycIMb4DYMpY9xczZ6jUorGtO6bR9BPMPaueIKwiDxu9uiQ==", "requires": { "d3-array": "3", "d3-axis": "3", @@ -12062,9 +11912,9 @@ } }, "d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", "requires": { "internmap": "1 - 2" } @@ -12100,9 +11950,9 @@ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" }, "d3-contour": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", - "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", "requires": { "d3-array": "^3.2.0" } @@ -12175,9 +12025,9 @@ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" }, "d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", "requires": { "d3-array": "2.5.0 - 3" } @@ -12242,11 +12092,11 @@ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" }, "d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "requires": { - "d3-path": "1 - 3" + "d3-path": "^3.1.0" } }, "d3-time": { @@ -12666,27 +12516,33 @@ } }, "es-abstract": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", - "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "requires": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", "gopd": "^1.0.1", "has": "^1.0.3", "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", "object-inspect": "^1.12.2", "object-keys": "^1.1.1", @@ -12695,7 +12551,9 @@ "safe-regex-test": "^1.0.0", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", - "unbox-primitive": "^1.0.2" + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" } }, "es-aggregate-error": { @@ -12718,6 +12576,17 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + } + }, "es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -12739,167 +12608,47 @@ } }, "esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", + "version": "0.16.17", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz", + "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==", "requires": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" + "@esbuild/android-arm": "0.16.17", + "@esbuild/android-arm64": "0.16.17", + "@esbuild/android-x64": "0.16.17", + "@esbuild/darwin-arm64": "0.16.17", + "@esbuild/darwin-x64": "0.16.17", + "@esbuild/freebsd-arm64": "0.16.17", + "@esbuild/freebsd-x64": "0.16.17", + "@esbuild/linux-arm": "0.16.17", + "@esbuild/linux-arm64": "0.16.17", + "@esbuild/linux-ia32": "0.16.17", + "@esbuild/linux-loong64": "0.16.17", + "@esbuild/linux-mips64el": "0.16.17", + "@esbuild/linux-ppc64": "0.16.17", + "@esbuild/linux-riscv64": "0.16.17", + "@esbuild/linux-s390x": "0.16.17", + "@esbuild/linux-x64": "0.16.17", + "@esbuild/netbsd-x64": "0.16.17", + "@esbuild/openbsd-x64": "0.16.17", + "@esbuild/sunos-x64": "0.16.17", + "@esbuild/win32-arm64": "0.16.17", + "@esbuild/win32-ia32": "0.16.17", + "@esbuild/win32-x64": "0.16.17" } }, - "esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", - "optional": true - }, - "esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", - "optional": true - }, - "esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", - "optional": true - }, - "esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", - "optional": true - }, - "esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", - "optional": true - }, - "esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", - "optional": true - }, "esbuild-loader": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz", - "integrity": "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.21.0.tgz", + "integrity": "sha512-k7ijTkCT43YBSZ6+fBCW1Gin7s46RrJ0VQaM8qA7lq7W+OLsGgtLyFV8470FzYi/4TeDexniTBTPTwZUnXXR5g==", "requires": { - "esbuild": "^0.15.6", + "esbuild": "^0.16.17", "joycon": "^3.0.1", "json5": "^2.2.0", "loader-utils": "^2.0.0", "tapable": "^2.2.0", - "webpack-sources": "^2.2.0" + "webpack-sources": "^1.4.3" } }, - "esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", - "optional": true - }, - "esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", - "optional": true - }, - "esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", - "optional": true - }, - "esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", - "optional": true - }, - "esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", - "optional": true - }, - "esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", - "optional": true - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -12971,12 +12720,12 @@ } }, "eslint": { - "version": "8.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", - "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", + "version": "8.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", + "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.4.0", + "@eslint/eslintrc": "^1.4.1", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -13038,13 +12787,14 @@ } }, "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", "dev": true, "requires": { "debug": "^3.2.7", - "resolve": "^1.20.0" + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" }, "dependencies": { "debug": { @@ -13079,33 +12829,35 @@ } }, "eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", "dev": true, "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", "has": "^1.0.3", - "is-core-module": "^2.8.1", + "is-core-module": "^2.11.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", "tsconfig-paths": "^3.14.1" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "doctrine": { @@ -13117,10 +12869,10 @@ "esutils": "^2.0.2" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -13133,9 +12885,9 @@ "requires": {} }, "eslint-plugin-sonarjs": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.17.0.tgz", - "integrity": "sha512-jtGtxI49UbJJeJj7CVRLI3+LLH+y+hkR3GOOwM7vBbci9DEFIRGCWvEd2BJScrzltZ6D6iubukTAfc9cyG7sdw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.18.0.tgz", + "integrity": "sha512-DJ3osLnt6KFdT5e9ZuIDOjT5A6wUGSLeiJJT03lPgpdD+7CVWlYAw9Goe3bt7SmbFO3Xh89NOCZAuB9XA7bAUQ==", "dev": true, "requires": {} }, @@ -13164,9 +12916,9 @@ } }, "eslint-plugin-vue": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.8.0.tgz", - "integrity": "sha512-E/AXwcTzunyzM83C2QqDHxepMzvI2y6x+mmeYHbVDQlKFqmKYvRrhaVixEeeG27uI44p9oKDFiyCRw4XxgtfHA==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.9.0.tgz", + "integrity": "sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==", "dev": true, "requires": { "eslint-utils": "^3.0.0", @@ -13322,9 +13074,9 @@ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==" }, "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "requires": { "reusify": "^1.0.4" } @@ -13391,6 +13143,15 @@ "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==" }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -13660,9 +13421,9 @@ "dev": true }, "gsap": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.3.tgz", - "integrity": "sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA==" + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.4.tgz", + "integrity": "sha512-McHhEguHyExMMnjqKA8G+7TvxmlKQGMbjgwAilnFe1e4id7V/tFveRQ2YMZhTYu0oxHGWvbPltdVYQOu3z1SCA==" }, "hard-rejection": { "version": "2.1.0", @@ -13698,6 +13459,12 @@ "get-intrinsic": "^1.1.1" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -13805,9 +13572,9 @@ "optional": true }, "immer": { - "version": "9.0.16", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", - "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", + "version": "9.0.18", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.18.tgz", + "integrity": "sha512-eAPNpsj7Ax1q6Y/3lm2PmlwRcFzpON7HSNQ3ru5WQH1/PSpnyed/HpNOELl2CxLKoj4r+bAHgdyKqW5gc2Se1A==", "dev": true }, "import-fresh": { @@ -13894,6 +13661,17 @@ "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", "dev": true }, + "is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -14059,6 +13837,19 @@ "has-symbols": "^1.0.2" } }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -14115,9 +13906,9 @@ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==" }, "jquery": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.2.tgz", - "integrity": "sha512-/e7ulNIEEYk1Z/l4X0vpxGt+B/dNsV8ghOPAWZaJs8pkGvsSC0tm33aMGylXcj/U7y4IcvwtMXPMyBFZn/gK9A==" + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz", + "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==" }, "jquery.are-you-sure": { "version": "1.9.0", @@ -14148,9 +13939,9 @@ } }, "jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.0.0.tgz", + "integrity": "sha512-AIw+3ZakSUtDYvhwPwWHiZsUi3zHugpMEKlNPaurviseYoBqo0zBd3zqoUi3LPCNtPFlEP8FiW9MqCZdjb2IYA==", "dev": true, "requires": { "abab": "^2.0.6", @@ -14265,9 +14056,9 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==" }, "known-css-properties": { "version": "0.26.0", @@ -14337,15 +14128,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -14389,9 +14171,9 @@ } }, "local-pkg": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz", - "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", "dev": true }, "locate-path": { @@ -14518,29 +14300,28 @@ } }, "markdownlint": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.26.2.tgz", - "integrity": "sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.27.0.tgz", + "integrity": "sha512-HtfVr/hzJJmE0C198F99JLaeada+646B5SaG2pVoEakLFI6iRGsvMqrnnrflq8hm1zQgwskEgqSnhDW11JBp0w==", "dev": true, "requires": { "markdown-it": "13.0.1" } }, "markdownlint-cli": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.32.2.tgz", - "integrity": "sha512-xmJT1rGueUgT4yGNwk6D0oqQr90UJ7nMyakXtqjgswAkEhYYqjHew9RY8wDbOmh2R270IWjuKSeZzHDEGPAUkQ==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.33.0.tgz", + "integrity": "sha512-zMK1oHpjYkhjO+94+ngARiBBrRDEUMzooDHBAHtmEIJ9oYddd9l3chCReY2mPlecwH7gflQp1ApilTo+o0zopQ==", "dev": true, "requires": { - "commander": "~9.4.0", + "commander": "~9.4.1", "get-stdin": "~9.0.0", "glob": "~8.0.3", - "ignore": "~5.2.0", + "ignore": "~5.2.4", "js-yaml": "^4.1.0", - "jsonc-parser": "~3.1.0", - "markdownlint": "~0.26.2", - "markdownlint-rule-helpers": "~0.17.2", - "minimatch": "~5.1.0", + "jsonc-parser": "~3.2.0", + "markdownlint": "~0.27.0", + "minimatch": "~5.1.2", "run-con": "~1.2.11" }, "dependencies": { @@ -14573,15 +14354,15 @@ } }, "jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, "minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -14589,16 +14370,10 @@ } } }, - "markdownlint-rule-helpers": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.17.2.tgz", - "integrity": "sha512-XaeoW2NYSlWxMCZM2B3H7YTG6nlaLfkEZWMBhr4hSPlq9MuY2sy83+Xr89jXOqZMZYjvi5nBCGoFh7hHoPKZmA==", - "dev": true - }, "marked": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.4.tgz", - "integrity": "sha512-Wcc9ikX7Q5E4BYDPvh1C6QNSxrjC9tBgz+A/vAhp59KXUgachw++uMvMKiSW8oA85nopmPZcEvBoex/YLMsiyA==" + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==" }, "mathml-tag-names": { "version": "2.1.3", @@ -14777,15 +14552,15 @@ } }, "mlly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.0.0.tgz", - "integrity": "sha512-QL108Hwt+u9bXdWgOI0dhzZfACovn5Aen4Xvc8Jasd9ouRH4NjnrXEiyP3nVvJo91zPlYjVRckta0Nt2zfoR6g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.0.tgz", + "integrity": "sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==", "dev": true, "requires": { "acorn": "^8.8.1", "pathe": "^1.0.0", - "pkg-types": "^1.0.0", - "ufo": "^1.0.0" + "pkg-types": "^1.0.1", + "ufo": "^1.0.1" }, "dependencies": { "pathe": { @@ -14893,9 +14668,9 @@ } }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", "requires": { "whatwg-url": "^5.0.0" }, @@ -14978,9 +14753,9 @@ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, "object-keys": { @@ -15275,9 +15050,9 @@ } }, "playwright-core": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.0.tgz", - "integrity": "sha512-pboOm1m0RD6z1GtwAbEH60PYRfF87vKdzOSRw2RyO0Y0a7utrMyWN2Au1ojGvQr4umuBMODkKTv607YIRypDSQ==", + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.29.2.tgz", + "integrity": "sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==", "dev": true }, "pluralize": { @@ -15293,9 +15068,9 @@ "dev": true }, "postcss": { - "version": "8.4.20", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", - "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -15448,9 +15223,9 @@ "dev": true }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, "querystringify": { "version": "2.2.0", @@ -15606,6 +15381,11 @@ "strip-indent": "^3.0.0" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "regexp-tree": { "version": "0.1.24", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", @@ -15839,9 +15619,9 @@ } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "requires": { "randombytes": "^2.1.0" } @@ -15893,6 +15673,12 @@ "object-inspect": "^1.9.0" } }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -15960,6 +15746,14 @@ "socks": "^2.3.3" } }, + "solid-js": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.6.9.tgz", + "integrity": "sha512-kV3fMmm+1C2J95c8eDOPKGfZHnuAkHUBLG4hX1Xu08bXeAIPqmxuz/QdH3B8SIdTp3EatBVIyA6RCes3hrGzpg==", + "requires": { + "csstype": "^3.1.0" + } + }, "sortablejs": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", @@ -16056,6 +15850,12 @@ "spdx-ranges": "^2.0.0" } }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, "stacktracey": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", @@ -16155,9 +15955,9 @@ "dev": true }, "stylelint": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.0.tgz", - "integrity": "sha512-X6uTi9DcxjzLV8ZUAjit1vsRtSwcls0nl07c9rqOPzvpA8IvTX/xWEkBRowS0ffevRrqkHa/ThDEu86u73FQDg==", + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", + "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", "dev": true, "requires": { "@csstools/selector-specificity": "^2.0.2", @@ -16538,6 +16338,17 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typo-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.2.tgz", @@ -16594,9 +16405,9 @@ } }, "updates": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/updates/-/updates-13.2.4.tgz", - "integrity": "sha512-aQcPi3/kRcMLqLZj6nvy0PZAX3ZhE78IdHoj02SujNqH3/55hgn2i/bHVzPUI86+qWITkt7PyjJHBLDSxDHEyA==", + "version": "13.2.7", + "resolved": "https://registry.npmjs.org/updates/-/updates-13.2.7.tgz", + "integrity": "sha512-0RbLcGX1zweNWITsX4hd1OjUX+GHqE/bK9czDdmnmt7lVwae4VLMuou3/Uu7YD6oUSjkswU7KNFNa0watI566A==", "dev": true }, "uri-js": { @@ -16665,9 +16476,9 @@ } }, "vite": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.0.2.tgz", - "integrity": "sha512-QJaY3R+tFlTagH0exVqbgkkw45B+/bXVBzF2ZD1KB5Z8RiAoiKo60vSUf6/r4c2Vh9jfGBKM4oBI9b4/1ZJYng==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.0.4.tgz", + "integrity": "sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==", "dev": true, "requires": { "esbuild": "^0.16.3", @@ -16677,54 +16488,10 @@ "rollup": "^3.7.0" }, "dependencies": { - "@esbuild/android-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.10.tgz", - "integrity": "sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.10.tgz", - "integrity": "sha512-O7Pd5hLEtTg37NC73pfhUOGTjx/+aXu5YoSq3ahCxcN7Bcr2F47mv+kG5t840thnsEzrv0oB70+LJu3gUgchvg==", - "dev": true, - "optional": true - }, - "esbuild": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.10.tgz", - "integrity": "sha512-z5dIViHoVnw2l+NCJ3zj5behdXjYvXne9gL18OOivCadXDUhyDkeSvEtLcGVAJW2fNmh33TDUpsi704XYlDodw==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.16.10", - "@esbuild/android-arm64": "0.16.10", - "@esbuild/android-x64": "0.16.10", - "@esbuild/darwin-arm64": "0.16.10", - "@esbuild/darwin-x64": "0.16.10", - "@esbuild/freebsd-arm64": "0.16.10", - "@esbuild/freebsd-x64": "0.16.10", - "@esbuild/linux-arm": "0.16.10", - "@esbuild/linux-arm64": "0.16.10", - "@esbuild/linux-ia32": "0.16.10", - "@esbuild/linux-loong64": "0.16.10", - "@esbuild/linux-mips64el": "0.16.10", - "@esbuild/linux-ppc64": "0.16.10", - "@esbuild/linux-riscv64": "0.16.10", - "@esbuild/linux-s390x": "0.16.10", - "@esbuild/linux-x64": "0.16.10", - "@esbuild/netbsd-x64": "0.16.10", - "@esbuild/openbsd-x64": "0.16.10", - "@esbuild/sunos-x64": "0.16.10", - "@esbuild/win32-arm64": "0.16.10", - "@esbuild/win32-ia32": "0.16.10", - "@esbuild/win32-x64": "0.16.10" - } - }, "rollup": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.7.5.tgz", - "integrity": "sha512-z0ZbqHBtS/et2EEUKMrAl2CoSdwN7ZPzL17UMiKN9RjjqHShTlv7F9J6ZJZJNREYjBh3TvBrdfjkFDIXFNeuiQ==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.10.0.tgz", + "integrity": "sha512-JmRYz44NjC1MjVF2VKxc0M1a97vn+cDxeqWmnwyAF4FvpjK8YFdHpaqvQB+3IxCvX05vJxKZkoMDU8TShhmJVA==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -16733,23 +16500,25 @@ } }, "vite-node": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.26.1.tgz", - "integrity": "sha512-5FJSKZZJz48zFRKHE55WyevZe61hLMQEsqGn+ungfd60kxEztFybZ3yG9ToGFtOWNCCy7Vn5EVuXD8bdeHOSdw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.27.2.tgz", + "integrity": "sha512-IDwuVhslF10qCnWOGJui7/2KksAOBHi+UbVo6Pqt4f5lgn+kS2sVvYDsETRG5PSuslisGB5CFGvb9I6FQgymBQ==", "dev": true, "requires": { + "cac": "^6.7.14", "debug": "^4.3.4", - "mlly": "^1.0.0", + "mlly": "^1.1.0", "pathe": "^0.2.0", + "picocolors": "^1.0.0", "source-map": "^0.6.1", "source-map-support": "^0.5.21", "vite": "^3.0.0 || ^4.0.0" } }, "vitest": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.26.1.tgz", - "integrity": "sha512-qTLRnjYmjmJpHlLUtErxtlRqGCe8WItFhGXKklpWivu7CLP9KXN9iTezROe+vf51Kb+BB/fzxK6fUG9DvFGL5Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.27.2.tgz", + "integrity": "sha512-y7tdsL2uaQy+KF18AlmNHZe29ukyFytlxrpSTwwmgLE2XHR/aPucJP9FLjWoqjgqFlXzRAjHlFJLU+HDyI/OsA==", "dev": true, "requires": { "@types/chai": "^4.3.4", @@ -16757,16 +16526,19 @@ "@types/node": "*", "acorn": "^8.8.1", "acorn-walk": "^8.2.0", + "cac": "^6.7.14", "chai": "^4.3.7", "debug": "^4.3.4", "local-pkg": "^0.4.2", + "picocolors": "^1.0.0", "source-map": "^0.6.1", "strip-literal": "^1.0.0", "tinybench": "^2.3.1", "tinypool": "^0.3.0", "tinyspy": "^1.0.2", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.26.1" + "vite-node": "0.27.2", + "why-is-node-running": "^2.2.2" } }, "vm2": { @@ -16967,9 +16739,9 @@ }, "dependencies": { "commander": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", - "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==" + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" } } }, @@ -16983,12 +16755,12 @@ } }, "webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "requires": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" } }, "whatwg-encoding": { @@ -17037,6 +16809,30 @@ "is-symbol": "^1.0.3" } }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", @@ -17173,9 +16969,9 @@ } }, "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", + "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index 229d4f1aa9..842804211c 100644 --- a/package.json +++ b/package.json @@ -7,23 +7,24 @@ "node": ">= 14.0.0" }, "dependencies": { - "@citation-js/core": "0.6.1", - "@citation-js/plugin-bibtex": "0.6.1", - "@citation-js/plugin-csl": "0.6.4", + "@citation-js/core": "0.6.5", + "@citation-js/plugin-bibtex": "0.6.5", + "@citation-js/plugin-csl": "0.6.5", "@citation-js/plugin-software-formats": "0.6.0", "@claviska/jquery-minicolors": "2.3.6", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", - "@primer/octicons": "17.10.0", + "@primer/octicons": "17.10.2", "@vue/compiler-sfc": "3.2.45", "add-asset-webpack-plugin": "2.0.1", + "asciinema-player": "3.0.1", "css-loader": "6.7.3", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "2.20.0", + "esbuild-loader": "2.21.0", "escape-goat": "4.0.0", "fast-glob": "3.2.12", "font-awesome": "4.7.0", - "jquery": "3.6.2", + "jquery": "3.6.3", "jquery.are-you-sure": "1.9.0", "katex": "0.16.4", "less": "4.1.3", @@ -51,24 +52,24 @@ "wrap-ansi": "8.0.1" }, "devDependencies": { - "@playwright/test": "1.29.0", + "@playwright/test": "1.29.2", "@rollup/pluginutils": "5.0.2", "@stoplight/spectral-cli": "6.6.0", - "eslint": "8.30.0", - "eslint-plugin-import": "2.26.0", + "eslint": "8.32.0", + "eslint-plugin-import": "2.27.5", "eslint-plugin-jquery": "1.5.1", - "eslint-plugin-sonarjs": "0.17.0", + "eslint-plugin-sonarjs": "0.18.0", "eslint-plugin-unicorn": "45.0.2", - "eslint-plugin-vue": "9.8.0", - "jsdom": "20.0.3", - "markdownlint-cli": "0.32.2", + "eslint-plugin-vue": "9.9.0", + "jsdom": "21.0.0", + "markdownlint-cli": "0.33.0", "postcss-less": "6.0.0", - "stylelint": "14.16.0", + "stylelint": "14.16.1", "stylelint-config-standard": "29.0.0", "stylelint-declaration-strict-value": "1.9.1", "svgo": "3.0.2", - "updates": "13.2.4", - "vitest": "0.26.1" + "updates": "13.2.7", + "vitest": "0.27.2" }, "browserslist": [ "defaults", diff --git a/public/img/svg/octicon-accessibility-inset.svg b/public/img/svg/octicon-accessibility-inset.svg index ec303f9cb2..533cfa0fde 100644 --- a/public/img/svg/octicon-accessibility-inset.svg +++ b/public/img/svg/octicon-accessibility-inset.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-accessibility.svg b/public/img/svg/octicon-accessibility.svg index 2254b66641..2a708549ca 100644 --- a/public/img/svg/octicon-accessibility.svg +++ b/public/img/svg/octicon-accessibility.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-alert-fill.svg b/public/img/svg/octicon-alert-fill.svg index 34795cfbe4..4ea5649737 100644 --- a/public/img/svg/octicon-alert-fill.svg +++ b/public/img/svg/octicon-alert-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-alert.svg b/public/img/svg/octicon-alert.svg index c77ebb9cfc..e403314080 100644 --- a/public/img/svg/octicon-alert.svg +++ b/public/img/svg/octicon-alert.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-apps.svg b/public/img/svg/octicon-apps.svg index 56c005e115..b1a2dfad72 100644 --- a/public/img/svg/octicon-apps.svg +++ b/public/img/svg/octicon-apps.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-archive.svg b/public/img/svg/octicon-archive.svg index cbc47ebd6d..072053acec 100644 --- a/public/img/svg/octicon-archive.svg +++ b/public/img/svg/octicon-archive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-both.svg b/public/img/svg/octicon-arrow-both.svg index 673d5c0777..9fac0c6d44 100644 --- a/public/img/svg/octicon-arrow-both.svg +++ b/public/img/svg/octicon-arrow-both.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-down-left.svg b/public/img/svg/octicon-arrow-down-left.svg index f02bd330c9..64dbb3b799 100644 --- a/public/img/svg/octicon-arrow-down-left.svg +++ b/public/img/svg/octicon-arrow-down-left.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-down-right.svg b/public/img/svg/octicon-arrow-down-right.svg index dff9d36c1b..5d693457a8 100644 --- a/public/img/svg/octicon-arrow-down-right.svg +++ b/public/img/svg/octicon-arrow-down-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-down.svg b/public/img/svg/octicon-arrow-down.svg index 744c9ce00d..441723c988 100644 --- a/public/img/svg/octicon-arrow-down.svg +++ b/public/img/svg/octicon-arrow-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-left.svg b/public/img/svg/octicon-arrow-left.svg index 94eef67bf3..0eb1061361 100644 --- a/public/img/svg/octicon-arrow-left.svg +++ b/public/img/svg/octicon-arrow-left.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-right.svg b/public/img/svg/octicon-arrow-right.svg index 337c246a1e..a446fda056 100644 --- a/public/img/svg/octicon-arrow-right.svg +++ b/public/img/svg/octicon-arrow-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-switch.svg b/public/img/svg/octicon-arrow-switch.svg index cee8a3b2fb..c3cd4a1e50 100644 --- a/public/img/svg/octicon-arrow-switch.svg +++ b/public/img/svg/octicon-arrow-switch.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-up-left.svg b/public/img/svg/octicon-arrow-up-left.svg index 0f5a452b8a..8ad8a9febe 100644 --- a/public/img/svg/octicon-arrow-up-left.svg +++ b/public/img/svg/octicon-arrow-up-left.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-up-right.svg b/public/img/svg/octicon-arrow-up-right.svg index 896c9b60b4..4b0bb32f7f 100644 --- a/public/img/svg/octicon-arrow-up-right.svg +++ b/public/img/svg/octicon-arrow-up-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-arrow-up.svg b/public/img/svg/octicon-arrow-up.svg index 4ad5a38085..0dd87c7c27 100644 --- a/public/img/svg/octicon-arrow-up.svg +++ b/public/img/svg/octicon-arrow-up.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-beaker.svg b/public/img/svg/octicon-beaker.svg index 7ed628d327..6177ca3b01 100644 --- a/public/img/svg/octicon-beaker.svg +++ b/public/img/svg/octicon-beaker.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-bell-fill.svg b/public/img/svg/octicon-bell-fill.svg index 58aafca877..873aea69bd 100644 --- a/public/img/svg/octicon-bell-fill.svg +++ b/public/img/svg/octicon-bell-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-bell-slash.svg b/public/img/svg/octicon-bell-slash.svg index 909ebcbd49..298aa18671 100644 --- a/public/img/svg/octicon-bell-slash.svg +++ b/public/img/svg/octicon-bell-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-bell.svg b/public/img/svg/octicon-bell.svg index 400bd78822..cfb8a39951 100644 --- a/public/img/svg/octicon-bell.svg +++ b/public/img/svg/octicon-bell.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-blocked.svg b/public/img/svg/octicon-blocked.svg index a85f07618a..1a2a004b00 100644 --- a/public/img/svg/octicon-blocked.svg +++ b/public/img/svg/octicon-blocked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-bold.svg b/public/img/svg/octicon-bold.svg index 4e09a80658..484dc66dc7 100644 --- a/public/img/svg/octicon-bold.svg +++ b/public/img/svg/octicon-bold.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-book.svg b/public/img/svg/octicon-book.svg index 9790824e1c..a64db11791 100644 --- a/public/img/svg/octicon-book.svg +++ b/public/img/svg/octicon-book.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-bookmark-slash.svg b/public/img/svg/octicon-bookmark-slash.svg index 86c5117518..edf7e2d2e4 100644 --- a/public/img/svg/octicon-bookmark-slash.svg +++ b/public/img/svg/octicon-bookmark-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-bookmark.svg b/public/img/svg/octicon-bookmark.svg index 01e72b9bac..4544b79dd3 100644 --- a/public/img/svg/octicon-bookmark.svg +++ b/public/img/svg/octicon-bookmark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-briefcase.svg b/public/img/svg/octicon-briefcase.svg index 2fd3b4768f..379af2fd00 100644 --- a/public/img/svg/octicon-briefcase.svg +++ b/public/img/svg/octicon-briefcase.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-broadcast.svg b/public/img/svg/octicon-broadcast.svg index 8b3b054004..19d0ce7338 100644 --- a/public/img/svg/octicon-broadcast.svg +++ b/public/img/svg/octicon-broadcast.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-browser.svg b/public/img/svg/octicon-browser.svg index d8d7d6ebd1..cb01bfac14 100644 --- a/public/img/svg/octicon-browser.svg +++ b/public/img/svg/octicon-browser.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-bug.svg b/public/img/svg/octicon-bug.svg index ad99e32c3e..ec0fe60feb 100644 --- a/public/img/svg/octicon-bug.svg +++ b/public/img/svg/octicon-bug.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-cache.svg b/public/img/svg/octicon-cache.svg index 20b14138d9..064f576453 100644 --- a/public/img/svg/octicon-cache.svg +++ b/public/img/svg/octicon-cache.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-calendar.svg b/public/img/svg/octicon-calendar.svg index ff31292a56..7d77b86797 100644 --- a/public/img/svg/octicon-calendar.svg +++ b/public/img/svg/octicon-calendar.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-check-circle-fill.svg b/public/img/svg/octicon-check-circle-fill.svg index 931f38962a..225c006357 100644 --- a/public/img/svg/octicon-check-circle-fill.svg +++ b/public/img/svg/octicon-check-circle-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-check-circle.svg b/public/img/svg/octicon-check-circle.svg index 880acb92f4..6e2e8da048 100644 --- a/public/img/svg/octicon-check-circle.svg +++ b/public/img/svg/octicon-check-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-check.svg b/public/img/svg/octicon-check.svg index 029c94c09b..7171f388a6 100644 --- a/public/img/svg/octicon-check.svg +++ b/public/img/svg/octicon-check.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-checkbox.svg b/public/img/svg/octicon-checkbox.svg index f0313bc747..7b840f518d 100644 --- a/public/img/svg/octicon-checkbox.svg +++ b/public/img/svg/octicon-checkbox.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-checklist.svg b/public/img/svg/octicon-checklist.svg index 4b2adc163d..831938e5c1 100644 --- a/public/img/svg/octicon-checklist.svg +++ b/public/img/svg/octicon-checklist.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-chevron-down.svg b/public/img/svg/octicon-chevron-down.svg index 8685bdafbc..707639c87f 100644 --- a/public/img/svg/octicon-chevron-down.svg +++ b/public/img/svg/octicon-chevron-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-chevron-left.svg b/public/img/svg/octicon-chevron-left.svg index 70f327f2ee..00751dee72 100644 --- a/public/img/svg/octicon-chevron-left.svg +++ b/public/img/svg/octicon-chevron-left.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-chevron-right.svg b/public/img/svg/octicon-chevron-right.svg index cf4d1b3c70..b974356efd 100644 --- a/public/img/svg/octicon-chevron-right.svg +++ b/public/img/svg/octicon-chevron-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-chevron-up.svg b/public/img/svg/octicon-chevron-up.svg index 5fa573d353..9c48df9606 100644 --- a/public/img/svg/octicon-chevron-up.svg +++ b/public/img/svg/octicon-chevron-up.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-circle-slash.svg b/public/img/svg/octicon-circle-slash.svg index 31498e4816..29b5bbf68e 100644 --- a/public/img/svg/octicon-circle-slash.svg +++ b/public/img/svg/octicon-circle-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-circle.svg b/public/img/svg/octicon-circle.svg index 53ed3c0615..1642bdb181 100644 --- a/public/img/svg/octicon-circle.svg +++ b/public/img/svg/octicon-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-clock-fill.svg b/public/img/svg/octicon-clock-fill.svg index 5fcfb477c4..9ebf1d177f 100644 --- a/public/img/svg/octicon-clock-fill.svg +++ b/public/img/svg/octicon-clock-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-clock.svg b/public/img/svg/octicon-clock.svg index 82bb9ab022..58dcdce060 100644 --- a/public/img/svg/octicon-clock.svg +++ b/public/img/svg/octicon-clock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-cloud-offline.svg b/public/img/svg/octicon-cloud-offline.svg index 7d34b8dff6..ce696c32e6 100644 --- a/public/img/svg/octicon-cloud-offline.svg +++ b/public/img/svg/octicon-cloud-offline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-cloud.svg b/public/img/svg/octicon-cloud.svg index fddfd2e424..a23697dd1c 100644 --- a/public/img/svg/octicon-cloud.svg +++ b/public/img/svg/octicon-cloud.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-code-of-conduct.svg b/public/img/svg/octicon-code-of-conduct.svg index 538f7f0933..302b483ebb 100644 --- a/public/img/svg/octicon-code-of-conduct.svg +++ b/public/img/svg/octicon-code-of-conduct.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-code-review.svg b/public/img/svg/octicon-code-review.svg index 3b4a01af24..4a7a33eb31 100644 --- a/public/img/svg/octicon-code-review.svg +++ b/public/img/svg/octicon-code-review.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-code-square.svg b/public/img/svg/octicon-code-square.svg index 9afba6ff20..57cc48a5eb 100644 --- a/public/img/svg/octicon-code-square.svg +++ b/public/img/svg/octicon-code-square.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-code.svg b/public/img/svg/octicon-code.svg index b7f6ddc11e..b40aa2a1a8 100644 --- a/public/img/svg/octicon-code.svg +++ b/public/img/svg/octicon-code.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-codescan-checkmark.svg b/public/img/svg/octicon-codescan-checkmark.svg index 31d1ea6aaa..1aa88a5f45 100644 --- a/public/img/svg/octicon-codescan-checkmark.svg +++ b/public/img/svg/octicon-codescan-checkmark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-codescan.svg b/public/img/svg/octicon-codescan.svg index ad7400efa1..e2f7fa45bd 100644 --- a/public/img/svg/octicon-codescan.svg +++ b/public/img/svg/octicon-codescan.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-codespaces.svg b/public/img/svg/octicon-codespaces.svg index 627300c148..706ae12f6b 100644 --- a/public/img/svg/octicon-codespaces.svg +++ b/public/img/svg/octicon-codespaces.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-columns.svg b/public/img/svg/octicon-columns.svg index 5eefbae1e9..94b4eba8be 100644 --- a/public/img/svg/octicon-columns.svg +++ b/public/img/svg/octicon-columns.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-command-palette.svg b/public/img/svg/octicon-command-palette.svg index 92fcd63149..668cf25e19 100644 --- a/public/img/svg/octicon-command-palette.svg +++ b/public/img/svg/octicon-command-palette.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-comment-discussion.svg b/public/img/svg/octicon-comment-discussion.svg index e93ae4d4bd..bacec1829c 100644 --- a/public/img/svg/octicon-comment-discussion.svg +++ b/public/img/svg/octicon-comment-discussion.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-comment.svg b/public/img/svg/octicon-comment.svg index 45abf5e740..0fca1d5991 100644 --- a/public/img/svg/octicon-comment.svg +++ b/public/img/svg/octicon-comment.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-container.svg b/public/img/svg/octicon-container.svg index f2ed916577..5794644ec4 100644 --- a/public/img/svg/octicon-container.svg +++ b/public/img/svg/octicon-container.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-copilot-error.svg b/public/img/svg/octicon-copilot-error.svg index 97c88a12bf..32b2496fd1 100644 --- a/public/img/svg/octicon-copilot-error.svg +++ b/public/img/svg/octicon-copilot-error.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-copilot-warning.svg b/public/img/svg/octicon-copilot-warning.svg index 32bd7361ae..c1f6bb668b 100644 --- a/public/img/svg/octicon-copilot-warning.svg +++ b/public/img/svg/octicon-copilot-warning.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-copilot.svg b/public/img/svg/octicon-copilot.svg index 1a99f1f5f9..7ebf01134a 100644 --- a/public/img/svg/octicon-copilot.svg +++ b/public/img/svg/octicon-copilot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-copy.svg b/public/img/svg/octicon-copy.svg index 5ccaeeb33d..12275d058a 100644 --- a/public/img/svg/octicon-copy.svg +++ b/public/img/svg/octicon-copy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-cpu.svg b/public/img/svg/octicon-cpu.svg index 8e0dbdc8a2..d8e447884e 100644 --- a/public/img/svg/octicon-cpu.svg +++ b/public/img/svg/octicon-cpu.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-credit-card.svg b/public/img/svg/octicon-credit-card.svg index 87d703b062..3d5990b210 100644 --- a/public/img/svg/octicon-credit-card.svg +++ b/public/img/svg/octicon-credit-card.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-cross-reference.svg b/public/img/svg/octicon-cross-reference.svg index f7a17de8e3..665589b182 100644 --- a/public/img/svg/octicon-cross-reference.svg +++ b/public/img/svg/octicon-cross-reference.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-dash.svg b/public/img/svg/octicon-dash.svg index 929e2f9b8f..fe0eafcf38 100644 --- a/public/img/svg/octicon-dash.svg +++ b/public/img/svg/octicon-dash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-database.svg b/public/img/svg/octicon-database.svg index a4f6531ae4..28d99c0313 100644 --- a/public/img/svg/octicon-database.svg +++ b/public/img/svg/octicon-database.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-dependabot.svg b/public/img/svg/octicon-dependabot.svg index b64a166035..8d6ca5ce6f 100644 --- a/public/img/svg/octicon-dependabot.svg +++ b/public/img/svg/octicon-dependabot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-desktop-download.svg b/public/img/svg/octicon-desktop-download.svg index c0fbe8941d..f8b68d7f5e 100644 --- a/public/img/svg/octicon-desktop-download.svg +++ b/public/img/svg/octicon-desktop-download.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-device-camera-video.svg b/public/img/svg/octicon-device-camera-video.svg index 623937b859..42a68a0679 100644 --- a/public/img/svg/octicon-device-camera-video.svg +++ b/public/img/svg/octicon-device-camera-video.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-device-camera.svg b/public/img/svg/octicon-device-camera.svg index 66d510ee16..39e6dbef24 100644 --- a/public/img/svg/octicon-device-camera.svg +++ b/public/img/svg/octicon-device-camera.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-device-desktop.svg b/public/img/svg/octicon-device-desktop.svg index d540efe27b..49f4a636be 100644 --- a/public/img/svg/octicon-device-desktop.svg +++ b/public/img/svg/octicon-device-desktop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-device-mobile.svg b/public/img/svg/octicon-device-mobile.svg index 3a7c042401..e0c827cf94 100644 --- a/public/img/svg/octicon-device-mobile.svg +++ b/public/img/svg/octicon-device-mobile.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-diamond.svg b/public/img/svg/octicon-diamond.svg index d13a10b679..9a0bb6ebd9 100644 --- a/public/img/svg/octicon-diamond.svg +++ b/public/img/svg/octicon-diamond.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-diff-added.svg b/public/img/svg/octicon-diff-added.svg index 35ce1e2387..800db65b62 100644 --- a/public/img/svg/octicon-diff-added.svg +++ b/public/img/svg/octicon-diff-added.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-diff-ignored.svg b/public/img/svg/octicon-diff-ignored.svg index e0cd2db1c4..5960963fc2 100644 --- a/public/img/svg/octicon-diff-ignored.svg +++ b/public/img/svg/octicon-diff-ignored.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-diff-modified.svg b/public/img/svg/octicon-diff-modified.svg index 286533fecf..d0e01d04ab 100644 --- a/public/img/svg/octicon-diff-modified.svg +++ b/public/img/svg/octicon-diff-modified.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-diff-removed.svg b/public/img/svg/octicon-diff-removed.svg index feca991cff..c4fb21add6 100644 --- a/public/img/svg/octicon-diff-removed.svg +++ b/public/img/svg/octicon-diff-removed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-diff-renamed.svg b/public/img/svg/octicon-diff-renamed.svg index 018541b631..85cf98e3da 100644 --- a/public/img/svg/octicon-diff-renamed.svg +++ b/public/img/svg/octicon-diff-renamed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-diff.svg b/public/img/svg/octicon-diff.svg index 8171fbf40a..f3bea7d3a1 100644 --- a/public/img/svg/octicon-diff.svg +++ b/public/img/svg/octicon-diff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-dot-fill.svg b/public/img/svg/octicon-dot-fill.svg index bd4ad8fb51..ff91cf1ddc 100644 --- a/public/img/svg/octicon-dot-fill.svg +++ b/public/img/svg/octicon-dot-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-dot.svg b/public/img/svg/octicon-dot.svg index 752413e7a5..9f67ffa9fa 100644 --- a/public/img/svg/octicon-dot.svg +++ b/public/img/svg/octicon-dot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-download.svg b/public/img/svg/octicon-download.svg index c9f3981633..5cd5a82aa5 100644 --- a/public/img/svg/octicon-download.svg +++ b/public/img/svg/octicon-download.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-duplicate.svg b/public/img/svg/octicon-duplicate.svg index a9a0f1a746..5e4f717213 100644 --- a/public/img/svg/octicon-duplicate.svg +++ b/public/img/svg/octicon-duplicate.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-ellipsis.svg b/public/img/svg/octicon-ellipsis.svg index 2ab7750462..3ab6dffc11 100644 --- a/public/img/svg/octicon-ellipsis.svg +++ b/public/img/svg/octicon-ellipsis.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-eye-closed.svg b/public/img/svg/octicon-eye-closed.svg index d22b04b105..a1b5092223 100644 --- a/public/img/svg/octicon-eye-closed.svg +++ b/public/img/svg/octicon-eye-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-eye.svg b/public/img/svg/octicon-eye.svg index e7f3ad7dbc..349c56fc67 100644 --- a/public/img/svg/octicon-eye.svg +++ b/public/img/svg/octicon-eye.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-discussion.svg b/public/img/svg/octicon-feed-discussion.svg index 070441e89f..937fc44e81 100644 --- a/public/img/svg/octicon-feed-discussion.svg +++ b/public/img/svg/octicon-feed-discussion.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-forked.svg b/public/img/svg/octicon-feed-forked.svg index d93d48aaf7..4b0d6e1a02 100644 --- a/public/img/svg/octicon-feed-forked.svg +++ b/public/img/svg/octicon-feed-forked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-heart.svg b/public/img/svg/octicon-feed-heart.svg index 9303875a74..75f8622db7 100644 --- a/public/img/svg/octicon-feed-heart.svg +++ b/public/img/svg/octicon-feed-heart.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-merged.svg b/public/img/svg/octicon-feed-merged.svg index 9313931087..a3a249f493 100644 --- a/public/img/svg/octicon-feed-merged.svg +++ b/public/img/svg/octicon-feed-merged.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-person.svg b/public/img/svg/octicon-feed-person.svg index 55566f8021..5b4ee2d2cc 100644 --- a/public/img/svg/octicon-feed-person.svg +++ b/public/img/svg/octicon-feed-person.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-repo.svg b/public/img/svg/octicon-feed-repo.svg index 3ca36c513e..dcce09083f 100644 --- a/public/img/svg/octicon-feed-repo.svg +++ b/public/img/svg/octicon-feed-repo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-rocket.svg b/public/img/svg/octicon-feed-rocket.svg index a94461a4d0..5d0327d097 100644 --- a/public/img/svg/octicon-feed-rocket.svg +++ b/public/img/svg/octicon-feed-rocket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-star.svg b/public/img/svg/octicon-feed-star.svg index 45db15a48a..d6a2e632ec 100644 --- a/public/img/svg/octicon-feed-star.svg +++ b/public/img/svg/octicon-feed-star.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-tag.svg b/public/img/svg/octicon-feed-tag.svg index 69ac88c779..30dfc0d1ab 100644 --- a/public/img/svg/octicon-feed-tag.svg +++ b/public/img/svg/octicon-feed-tag.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-feed-trophy.svg b/public/img/svg/octicon-feed-trophy.svg index b19b85afe5..6d10691ad2 100644 --- a/public/img/svg/octicon-feed-trophy.svg +++ b/public/img/svg/octicon-feed-trophy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-added.svg b/public/img/svg/octicon-file-added.svg index c1ad23d296..1fd2f9da78 100644 --- a/public/img/svg/octicon-file-added.svg +++ b/public/img/svg/octicon-file-added.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-badge.svg b/public/img/svg/octicon-file-badge.svg index ef52524e29..0f41ca1d95 100644 --- a/public/img/svg/octicon-file-badge.svg +++ b/public/img/svg/octicon-file-badge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-binary.svg b/public/img/svg/octicon-file-binary.svg index 9834f83900..4048fe5c78 100644 --- a/public/img/svg/octicon-file-binary.svg +++ b/public/img/svg/octicon-file-binary.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-code.svg b/public/img/svg/octicon-file-code.svg index 8456343b3e..49ce3379ef 100644 --- a/public/img/svg/octicon-file-code.svg +++ b/public/img/svg/octicon-file-code.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-diff.svg b/public/img/svg/octicon-file-diff.svg index 0ffaf284b8..bbfc28ca23 100644 --- a/public/img/svg/octicon-file-diff.svg +++ b/public/img/svg/octicon-file-diff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-directory-fill.svg b/public/img/svg/octicon-file-directory-fill.svg index 7ec313489b..99777bdc86 100644 --- a/public/img/svg/octicon-file-directory-fill.svg +++ b/public/img/svg/octicon-file-directory-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-directory-open-fill.svg b/public/img/svg/octicon-file-directory-open-fill.svg index 50809936e2..04331093c4 100644 --- a/public/img/svg/octicon-file-directory-open-fill.svg +++ b/public/img/svg/octicon-file-directory-open-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-directory.svg b/public/img/svg/octicon-file-directory.svg index ca3345a4d3..248e166d1f 100644 --- a/public/img/svg/octicon-file-directory.svg +++ b/public/img/svg/octicon-file-directory.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-moved.svg b/public/img/svg/octicon-file-moved.svg index 2e26b4cfd2..9775ce8c2e 100644 --- a/public/img/svg/octicon-file-moved.svg +++ b/public/img/svg/octicon-file-moved.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-removed.svg b/public/img/svg/octicon-file-removed.svg index 43c07295dd..65f301be1a 100644 --- a/public/img/svg/octicon-file-removed.svg +++ b/public/img/svg/octicon-file-removed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-submodule.svg b/public/img/svg/octicon-file-submodule.svg index 994d50a29f..81faea9d87 100644 --- a/public/img/svg/octicon-file-submodule.svg +++ b/public/img/svg/octicon-file-submodule.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-symlink-file.svg b/public/img/svg/octicon-file-symlink-file.svg index cc6c628ce9..df29b91a99 100644 --- a/public/img/svg/octicon-file-symlink-file.svg +++ b/public/img/svg/octicon-file-symlink-file.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file-zip.svg b/public/img/svg/octicon-file-zip.svg index 3794b79648..75d3a38a5c 100644 --- a/public/img/svg/octicon-file-zip.svg +++ b/public/img/svg/octicon-file-zip.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-file.svg b/public/img/svg/octicon-file.svg index 4037afad5e..bdcc67e212 100644 --- a/public/img/svg/octicon-file.svg +++ b/public/img/svg/octicon-file.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-filter.svg b/public/img/svg/octicon-filter.svg index 69baf2ee37..58f9fb5b5b 100644 --- a/public/img/svg/octicon-filter.svg +++ b/public/img/svg/octicon-filter.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-flame.svg b/public/img/svg/octicon-flame.svg index d4cda7e305..759dd358d6 100644 --- a/public/img/svg/octicon-flame.svg +++ b/public/img/svg/octicon-flame.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-fold-down.svg b/public/img/svg/octicon-fold-down.svg index 15fb505f7e..586eacb240 100644 --- a/public/img/svg/octicon-fold-down.svg +++ b/public/img/svg/octicon-fold-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-fold-up.svg b/public/img/svg/octicon-fold-up.svg index a062740ef0..f95d157ddc 100644 --- a/public/img/svg/octicon-fold-up.svg +++ b/public/img/svg/octicon-fold-up.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-fold.svg b/public/img/svg/octicon-fold.svg index 809949ab1c..2167f72ad2 100644 --- a/public/img/svg/octicon-fold.svg +++ b/public/img/svg/octicon-fold.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-gear.svg b/public/img/svg/octicon-gear.svg index 7a833198ff..14a5426260 100644 --- a/public/img/svg/octicon-gear.svg +++ b/public/img/svg/octicon-gear.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-gift.svg b/public/img/svg/octicon-gift.svg index f8101891dd..4a5afae9fd 100644 --- a/public/img/svg/octicon-gift.svg +++ b/public/img/svg/octicon-gift.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-branch.svg b/public/img/svg/octicon-git-branch.svg index 796f7128a0..bbff4a8b66 100644 --- a/public/img/svg/octicon-git-branch.svg +++ b/public/img/svg/octicon-git-branch.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-commit.svg b/public/img/svg/octicon-git-commit.svg index 06be9837ad..0c627ddae6 100644 --- a/public/img/svg/octicon-git-commit.svg +++ b/public/img/svg/octicon-git-commit.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-compare.svg b/public/img/svg/octicon-git-compare.svg index 00be2721a9..174a93d7ea 100644 --- a/public/img/svg/octicon-git-compare.svg +++ b/public/img/svg/octicon-git-compare.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-merge-queue.svg b/public/img/svg/octicon-git-merge-queue.svg index 17d7767b05..c969158a1a 100644 --- a/public/img/svg/octicon-git-merge-queue.svg +++ b/public/img/svg/octicon-git-merge-queue.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-merge.svg b/public/img/svg/octicon-git-merge.svg index 4fd322f448..45c078cd86 100644 --- a/public/img/svg/octicon-git-merge.svg +++ b/public/img/svg/octicon-git-merge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-pull-request-closed.svg b/public/img/svg/octicon-git-pull-request-closed.svg index feacb3c776..2385c08619 100644 --- a/public/img/svg/octicon-git-pull-request-closed.svg +++ b/public/img/svg/octicon-git-pull-request-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-pull-request-draft.svg b/public/img/svg/octicon-git-pull-request-draft.svg index edd5ce7ddd..fb99e6b9ba 100644 --- a/public/img/svg/octicon-git-pull-request-draft.svg +++ b/public/img/svg/octicon-git-pull-request-draft.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-git-pull-request.svg b/public/img/svg/octicon-git-pull-request.svg index 35e6e36549..f9c6de9d49 100644 --- a/public/img/svg/octicon-git-pull-request.svg +++ b/public/img/svg/octicon-git-pull-request.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-globe.svg b/public/img/svg/octicon-globe.svg index b9b0d4e8c4..5ae492dba7 100644 --- a/public/img/svg/octicon-globe.svg +++ b/public/img/svg/octicon-globe.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-goal.svg b/public/img/svg/octicon-goal.svg index b715f4bbd9..54371c9227 100644 --- a/public/img/svg/octicon-goal.svg +++ b/public/img/svg/octicon-goal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-grabber.svg b/public/img/svg/octicon-grabber.svg index 2d20faf041..f882da3d2b 100644 --- a/public/img/svg/octicon-grabber.svg +++ b/public/img/svg/octicon-grabber.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-graph.svg b/public/img/svg/octicon-graph.svg index e562645aec..1eb6bd3c56 100644 --- a/public/img/svg/octicon-graph.svg +++ b/public/img/svg/octicon-graph.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-hash.svg b/public/img/svg/octicon-hash.svg index 277ac29a03..1a30f5a077 100644 --- a/public/img/svg/octicon-hash.svg +++ b/public/img/svg/octicon-hash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-heading.svg b/public/img/svg/octicon-heading.svg index dc85efc9ec..5b9162ac5b 100644 --- a/public/img/svg/octicon-heading.svg +++ b/public/img/svg/octicon-heading.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-heart-fill.svg b/public/img/svg/octicon-heart-fill.svg index f668e261d1..f1ab0f6132 100644 --- a/public/img/svg/octicon-heart-fill.svg +++ b/public/img/svg/octicon-heart-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-heart.svg b/public/img/svg/octicon-heart.svg index d3280ef89a..f90922b18e 100644 --- a/public/img/svg/octicon-heart.svg +++ b/public/img/svg/octicon-heart.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-history.svg b/public/img/svg/octicon-history.svg index c84b7e49e8..1569bc64aa 100644 --- a/public/img/svg/octicon-history.svg +++ b/public/img/svg/octicon-history.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-home.svg b/public/img/svg/octicon-home.svg index 78aef58def..0b8fd4d05a 100644 --- a/public/img/svg/octicon-home.svg +++ b/public/img/svg/octicon-home.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-horizontal-rule.svg b/public/img/svg/octicon-horizontal-rule.svg index 28e3b738e1..ccd44f2695 100644 --- a/public/img/svg/octicon-horizontal-rule.svg +++ b/public/img/svg/octicon-horizontal-rule.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-hourglass.svg b/public/img/svg/octicon-hourglass.svg index fc7f362568..bcc3d53e22 100644 --- a/public/img/svg/octicon-hourglass.svg +++ b/public/img/svg/octicon-hourglass.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-hubot.svg b/public/img/svg/octicon-hubot.svg index 706b4003fe..dd637e936a 100644 --- a/public/img/svg/octicon-hubot.svg +++ b/public/img/svg/octicon-hubot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-id-badge.svg b/public/img/svg/octicon-id-badge.svg index ddbf0fd6b1..f9f155151b 100644 --- a/public/img/svg/octicon-id-badge.svg +++ b/public/img/svg/octicon-id-badge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-image.svg b/public/img/svg/octicon-image.svg index e278a80f48..278ac33d3c 100644 --- a/public/img/svg/octicon-image.svg +++ b/public/img/svg/octicon-image.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-inbox.svg b/public/img/svg/octicon-inbox.svg index 21ca60a592..bea8281d37 100644 --- a/public/img/svg/octicon-inbox.svg +++ b/public/img/svg/octicon-inbox.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-infinity.svg b/public/img/svg/octicon-infinity.svg index 1c3d95e921..22409c528c 100644 --- a/public/img/svg/octicon-infinity.svg +++ b/public/img/svg/octicon-infinity.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-info.svg b/public/img/svg/octicon-info.svg index dc50b6facc..bf5f316d68 100644 --- a/public/img/svg/octicon-info.svg +++ b/public/img/svg/octicon-info.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-issue-closed.svg b/public/img/svg/octicon-issue-closed.svg index dba68ab071..bafa63aaf3 100644 --- a/public/img/svg/octicon-issue-closed.svg +++ b/public/img/svg/octicon-issue-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-issue-draft.svg b/public/img/svg/octicon-issue-draft.svg index 0efacc1b59..e853f2bcef 100644 --- a/public/img/svg/octicon-issue-draft.svg +++ b/public/img/svg/octicon-issue-draft.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-issue-opened.svg b/public/img/svg/octicon-issue-opened.svg index bd55f83940..d2fc7e53aa 100644 --- a/public/img/svg/octicon-issue-opened.svg +++ b/public/img/svg/octicon-issue-opened.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-issue-reopened.svg b/public/img/svg/octicon-issue-reopened.svg index c8367cfef8..73d889964f 100644 --- a/public/img/svg/octicon-issue-reopened.svg +++ b/public/img/svg/octicon-issue-reopened.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-issue-tracked-by.svg b/public/img/svg/octicon-issue-tracked-by.svg index d4352a9430..6fadd95ccd 100644 --- a/public/img/svg/octicon-issue-tracked-by.svg +++ b/public/img/svg/octicon-issue-tracked-by.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-issue-tracked-in.svg b/public/img/svg/octicon-issue-tracked-in.svg index 8910bd7a24..18ce933bed 100644 --- a/public/img/svg/octicon-issue-tracked-in.svg +++ b/public/img/svg/octicon-issue-tracked-in.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-italic.svg b/public/img/svg/octicon-italic.svg index 485bf091c0..c09cca3f2a 100644 --- a/public/img/svg/octicon-italic.svg +++ b/public/img/svg/octicon-italic.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-iterations.svg b/public/img/svg/octicon-iterations.svg index 02f7efe4f5..6eef9d62a3 100644 --- a/public/img/svg/octicon-iterations.svg +++ b/public/img/svg/octicon-iterations.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-kebab-horizontal.svg b/public/img/svg/octicon-kebab-horizontal.svg index 8852e0ca4a..c8b1c83813 100644 --- a/public/img/svg/octicon-kebab-horizontal.svg +++ b/public/img/svg/octicon-kebab-horizontal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-key-asterisk.svg b/public/img/svg/octicon-key-asterisk.svg index 7ed156b9be..8fe2415883 100644 --- a/public/img/svg/octicon-key-asterisk.svg +++ b/public/img/svg/octicon-key-asterisk.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-key.svg b/public/img/svg/octicon-key.svg index db8b3111d7..c531db86ff 100644 --- a/public/img/svg/octicon-key.svg +++ b/public/img/svg/octicon-key.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-law.svg b/public/img/svg/octicon-law.svg index 78417b3fff..4a9f7d64d8 100644 --- a/public/img/svg/octicon-law.svg +++ b/public/img/svg/octicon-law.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-light-bulb.svg b/public/img/svg/octicon-light-bulb.svg index 3428df5211..c12dbc0539 100644 --- a/public/img/svg/octicon-light-bulb.svg +++ b/public/img/svg/octicon-light-bulb.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-link-external.svg b/public/img/svg/octicon-link-external.svg index e0a6f8bc56..df16286ec2 100644 --- a/public/img/svg/octicon-link-external.svg +++ b/public/img/svg/octicon-link-external.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-link.svg b/public/img/svg/octicon-link.svg index 37f5738ab5..077629e897 100644 --- a/public/img/svg/octicon-link.svg +++ b/public/img/svg/octicon-link.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-list-ordered.svg b/public/img/svg/octicon-list-ordered.svg index d0238e5e82..039775ff67 100644 --- a/public/img/svg/octicon-list-ordered.svg +++ b/public/img/svg/octicon-list-ordered.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-list-unordered.svg b/public/img/svg/octicon-list-unordered.svg index 0f01110657..74a25fe5af 100644 --- a/public/img/svg/octicon-list-unordered.svg +++ b/public/img/svg/octicon-list-unordered.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-location.svg b/public/img/svg/octicon-location.svg index 4d74ff3b98..1fba46da19 100644 --- a/public/img/svg/octicon-location.svg +++ b/public/img/svg/octicon-location.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-lock.svg b/public/img/svg/octicon-lock.svg index 1d74068446..da213efa81 100644 --- a/public/img/svg/octicon-lock.svg +++ b/public/img/svg/octicon-lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-log.svg b/public/img/svg/octicon-log.svg index 04836934a0..7ab609dc1c 100644 --- a/public/img/svg/octicon-log.svg +++ b/public/img/svg/octicon-log.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-logo-gist.svg b/public/img/svg/octicon-logo-gist.svg index 24513be6e8..f060d2e0db 100644 --- a/public/img/svg/octicon-logo-gist.svg +++ b/public/img/svg/octicon-logo-gist.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-logo-github.svg b/public/img/svg/octicon-logo-github.svg index 7379366215..ca33522ca7 100644 --- a/public/img/svg/octicon-logo-github.svg +++ b/public/img/svg/octicon-logo-github.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-mail.svg b/public/img/svg/octicon-mail.svg index 44b22ff804..168198a26e 100644 --- a/public/img/svg/octicon-mail.svg +++ b/public/img/svg/octicon-mail.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-mark-github.svg b/public/img/svg/octicon-mark-github.svg index d00b2bcf12..3bf0a3f111 100644 --- a/public/img/svg/octicon-mark-github.svg +++ b/public/img/svg/octicon-mark-github.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-markdown.svg b/public/img/svg/octicon-markdown.svg index bce2e27b14..e77cfce561 100644 --- a/public/img/svg/octicon-markdown.svg +++ b/public/img/svg/octicon-markdown.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-megaphone.svg b/public/img/svg/octicon-megaphone.svg index b733aa552f..e892a46e51 100644 --- a/public/img/svg/octicon-megaphone.svg +++ b/public/img/svg/octicon-megaphone.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-mention.svg b/public/img/svg/octicon-mention.svg index 6847fed0f8..08c79c2a14 100644 --- a/public/img/svg/octicon-mention.svg +++ b/public/img/svg/octicon-mention.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-meter.svg b/public/img/svg/octicon-meter.svg index d4bf6e8759..e5c65c1fee 100644 --- a/public/img/svg/octicon-meter.svg +++ b/public/img/svg/octicon-meter.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-milestone.svg b/public/img/svg/octicon-milestone.svg index 37c431b54d..67581ce3bc 100644 --- a/public/img/svg/octicon-milestone.svg +++ b/public/img/svg/octicon-milestone.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-mirror.svg b/public/img/svg/octicon-mirror.svg index 42c9b45ae6..986e193692 100644 --- a/public/img/svg/octicon-mirror.svg +++ b/public/img/svg/octicon-mirror.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-moon.svg b/public/img/svg/octicon-moon.svg index fe9733e221..ada1acc4bb 100644 --- a/public/img/svg/octicon-moon.svg +++ b/public/img/svg/octicon-moon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-mortar-board.svg b/public/img/svg/octicon-mortar-board.svg index fa3941192d..58fd33f187 100644 --- a/public/img/svg/octicon-mortar-board.svg +++ b/public/img/svg/octicon-mortar-board.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-multi-select.svg b/public/img/svg/octicon-multi-select.svg index 0cde4d60d6..12dcc1130e 100644 --- a/public/img/svg/octicon-multi-select.svg +++ b/public/img/svg/octicon-multi-select.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-mute.svg b/public/img/svg/octicon-mute.svg index 088b756fce..6e4eb08f23 100644 --- a/public/img/svg/octicon-mute.svg +++ b/public/img/svg/octicon-mute.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-no-entry.svg b/public/img/svg/octicon-no-entry.svg index d51ceaeadb..31ccf5e383 100644 --- a/public/img/svg/octicon-no-entry.svg +++ b/public/img/svg/octicon-no-entry.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-north-star.svg b/public/img/svg/octicon-north-star.svg index ee501e4138..5bd7e614a8 100644 --- a/public/img/svg/octicon-north-star.svg +++ b/public/img/svg/octicon-north-star.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-note.svg b/public/img/svg/octicon-note.svg index 02ae3d5153..34a115d233 100644 --- a/public/img/svg/octicon-note.svg +++ b/public/img/svg/octicon-note.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-number.svg b/public/img/svg/octicon-number.svg index 0d60392de5..af8dcb5e57 100644 --- a/public/img/svg/octicon-number.svg +++ b/public/img/svg/octicon-number.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-organization.svg b/public/img/svg/octicon-organization.svg index 1a5ebf73d4..662116a2ed 100644 --- a/public/img/svg/octicon-organization.svg +++ b/public/img/svg/octicon-organization.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-package-dependencies.svg b/public/img/svg/octicon-package-dependencies.svg index cf6f8d3367..c29c1513f6 100644 --- a/public/img/svg/octicon-package-dependencies.svg +++ b/public/img/svg/octicon-package-dependencies.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-package-dependents.svg b/public/img/svg/octicon-package-dependents.svg index 4fa70763cb..79046fc2e7 100644 --- a/public/img/svg/octicon-package-dependents.svg +++ b/public/img/svg/octicon-package-dependents.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-package.svg b/public/img/svg/octicon-package.svg index 9614e0b7c4..262de9f221 100644 --- a/public/img/svg/octicon-package.svg +++ b/public/img/svg/octicon-package.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-paintbrush.svg b/public/img/svg/octicon-paintbrush.svg index aa8be69e0d..44b4ea38d5 100644 --- a/public/img/svg/octicon-paintbrush.svg +++ b/public/img/svg/octicon-paintbrush.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-paper-airplane.svg b/public/img/svg/octicon-paper-airplane.svg index 72f9bf7c0b..fd6aaa4846 100644 --- a/public/img/svg/octicon-paper-airplane.svg +++ b/public/img/svg/octicon-paper-airplane.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-paperclip.svg b/public/img/svg/octicon-paperclip.svg index ddae143818..40d3caddec 100644 --- a/public/img/svg/octicon-paperclip.svg +++ b/public/img/svg/octicon-paperclip.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-paste.svg b/public/img/svg/octicon-paste.svg index 3e5b1cb353..066cf54664 100644 --- a/public/img/svg/octicon-paste.svg +++ b/public/img/svg/octicon-paste.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-pencil.svg b/public/img/svg/octicon-pencil.svg index 19ead37d8a..dabd0ab5af 100644 --- a/public/img/svg/octicon-pencil.svg +++ b/public/img/svg/octicon-pencil.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-people.svg b/public/img/svg/octicon-people.svg index 8a00d3a42c..1c61c598a0 100644 --- a/public/img/svg/octicon-people.svg +++ b/public/img/svg/octicon-people.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-person-add.svg b/public/img/svg/octicon-person-add.svg index fbec6c90f9..45ff7908df 100644 --- a/public/img/svg/octicon-person-add.svg +++ b/public/img/svg/octicon-person-add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-person-fill.svg b/public/img/svg/octicon-person-fill.svg index b8cc69cfe9..966ff00119 100644 --- a/public/img/svg/octicon-person-fill.svg +++ b/public/img/svg/octicon-person-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-person.svg b/public/img/svg/octicon-person.svg index b81ba0e4d4..7f23ba0791 100644 --- a/public/img/svg/octicon-person.svg +++ b/public/img/svg/octicon-person.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-pin.svg b/public/img/svg/octicon-pin.svg index 152d379941..7df3a7f248 100644 --- a/public/img/svg/octicon-pin.svg +++ b/public/img/svg/octicon-pin.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-play.svg b/public/img/svg/octicon-play.svg index b06ad02f9d..6a53430be1 100644 --- a/public/img/svg/octicon-play.svg +++ b/public/img/svg/octicon-play.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-plug.svg b/public/img/svg/octicon-plug.svg index 4e7bf2dad3..569cf4d2ed 100644 --- a/public/img/svg/octicon-plug.svg +++ b/public/img/svg/octicon-plug.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-plus-circle.svg b/public/img/svg/octicon-plus-circle.svg index 4f0a9798b4..164233e8f4 100644 --- a/public/img/svg/octicon-plus-circle.svg +++ b/public/img/svg/octicon-plus-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-plus.svg b/public/img/svg/octicon-plus.svg index 0c392bea2d..8e727d01e2 100644 --- a/public/img/svg/octicon-plus.svg +++ b/public/img/svg/octicon-plus.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-project-roadmap.svg b/public/img/svg/octicon-project-roadmap.svg index afc3b223bd..ba8d11aaad 100644 --- a/public/img/svg/octicon-project-roadmap.svg +++ b/public/img/svg/octicon-project-roadmap.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-project-symlink.svg b/public/img/svg/octicon-project-symlink.svg index 2c889a090c..e0dd56b78b 100644 --- a/public/img/svg/octicon-project-symlink.svg +++ b/public/img/svg/octicon-project-symlink.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-project.svg b/public/img/svg/octicon-project.svg index f47b19cc75..4faff0697a 100644 --- a/public/img/svg/octicon-project.svg +++ b/public/img/svg/octicon-project.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-pulse.svg b/public/img/svg/octicon-pulse.svg index 1f1637b664..3dc1f0b5e3 100644 --- a/public/img/svg/octicon-pulse.svg +++ b/public/img/svg/octicon-pulse.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-question.svg b/public/img/svg/octicon-question.svg index 42a39fb51b..b4ec8e36ff 100644 --- a/public/img/svg/octicon-question.svg +++ b/public/img/svg/octicon-question.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-quote.svg b/public/img/svg/octicon-quote.svg index 819a0c902a..60887fd89e 100644 --- a/public/img/svg/octicon-quote.svg +++ b/public/img/svg/octicon-quote.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-read.svg b/public/img/svg/octicon-read.svg index bc275f50bf..d6a7cd8eef 100644 --- a/public/img/svg/octicon-read.svg +++ b/public/img/svg/octicon-read.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-rel-file-path.svg b/public/img/svg/octicon-rel-file-path.svg index e8c9899799..6b36861cf1 100644 --- a/public/img/svg/octicon-rel-file-path.svg +++ b/public/img/svg/octicon-rel-file-path.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-reply.svg b/public/img/svg/octicon-reply.svg index 8d5053f197..47feacc042 100644 --- a/public/img/svg/octicon-reply.svg +++ b/public/img/svg/octicon-reply.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo-clone.svg b/public/img/svg/octicon-repo-clone.svg index 2e551ffdd3..fa248b0f59 100644 --- a/public/img/svg/octicon-repo-clone.svg +++ b/public/img/svg/octicon-repo-clone.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo-deleted.svg b/public/img/svg/octicon-repo-deleted.svg index 0fcb2c1cea..8ff6cba32a 100644 --- a/public/img/svg/octicon-repo-deleted.svg +++ b/public/img/svg/octicon-repo-deleted.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo-forked.svg b/public/img/svg/octicon-repo-forked.svg index 42a565db1f..f91f7c859c 100644 --- a/public/img/svg/octicon-repo-forked.svg +++ b/public/img/svg/octicon-repo-forked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo-locked.svg b/public/img/svg/octicon-repo-locked.svg index 1da51110b2..3674b8ceac 100644 --- a/public/img/svg/octicon-repo-locked.svg +++ b/public/img/svg/octicon-repo-locked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo-pull.svg b/public/img/svg/octicon-repo-pull.svg index e396a0a4c7..b68d6d289d 100644 --- a/public/img/svg/octicon-repo-pull.svg +++ b/public/img/svg/octicon-repo-pull.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo-push.svg b/public/img/svg/octicon-repo-push.svg index c7b16a8372..cd24d5e28f 100644 --- a/public/img/svg/octicon-repo-push.svg +++ b/public/img/svg/octicon-repo-push.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo-template.svg b/public/img/svg/octicon-repo-template.svg index b18c540f87..9823f16a60 100644 --- a/public/img/svg/octicon-repo-template.svg +++ b/public/img/svg/octicon-repo-template.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-repo.svg b/public/img/svg/octicon-repo.svg index 5ac69c2498..8c0b2b6f82 100644 --- a/public/img/svg/octicon-repo.svg +++ b/public/img/svg/octicon-repo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-report.svg b/public/img/svg/octicon-report.svg index a17354fb8a..9c727259d0 100644 --- a/public/img/svg/octicon-report.svg +++ b/public/img/svg/octicon-report.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-rocket.svg b/public/img/svg/octicon-rocket.svg index 1b1607546a..1f0640ec55 100644 --- a/public/img/svg/octicon-rocket.svg +++ b/public/img/svg/octicon-rocket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-rows.svg b/public/img/svg/octicon-rows.svg index 7f522994ba..ff7a4dd74f 100644 --- a/public/img/svg/octicon-rows.svg +++ b/public/img/svg/octicon-rows.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-rss.svg b/public/img/svg/octicon-rss.svg index 18d4fc05f8..377d64de36 100644 --- a/public/img/svg/octicon-rss.svg +++ b/public/img/svg/octicon-rss.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-ruby.svg b/public/img/svg/octicon-ruby.svg index 41025699e4..f2df66a4c7 100644 --- a/public/img/svg/octicon-ruby.svg +++ b/public/img/svg/octicon-ruby.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-screen-full.svg b/public/img/svg/octicon-screen-full.svg index e76cd9f9cb..55e9157248 100644 --- a/public/img/svg/octicon-screen-full.svg +++ b/public/img/svg/octicon-screen-full.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-screen-normal.svg b/public/img/svg/octicon-screen-normal.svg index 5fcefec3a4..307005e71a 100644 --- a/public/img/svg/octicon-screen-normal.svg +++ b/public/img/svg/octicon-screen-normal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-search.svg b/public/img/svg/octicon-search.svg index a8d717d8d4..a5d91ba833 100644 --- a/public/img/svg/octicon-search.svg +++ b/public/img/svg/octicon-search.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-server.svg b/public/img/svg/octicon-server.svg index 140b56bd69..728e26764c 100644 --- a/public/img/svg/octicon-server.svg +++ b/public/img/svg/octicon-server.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-share-android.svg b/public/img/svg/octicon-share-android.svg index a84adb98bd..71dc8a6340 100644 --- a/public/img/svg/octicon-share-android.svg +++ b/public/img/svg/octicon-share-android.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-share.svg b/public/img/svg/octicon-share.svg index 2171a82f3e..f672b5a98d 100644 --- a/public/img/svg/octicon-share.svg +++ b/public/img/svg/octicon-share.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-shield-check.svg b/public/img/svg/octicon-shield-check.svg index 3d633205e7..d91190af59 100644 --- a/public/img/svg/octicon-shield-check.svg +++ b/public/img/svg/octicon-shield-check.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-shield-lock.svg b/public/img/svg/octicon-shield-lock.svg index 3d6f3fb9c6..2a728cb43a 100644 --- a/public/img/svg/octicon-shield-lock.svg +++ b/public/img/svg/octicon-shield-lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-shield-slash.svg b/public/img/svg/octicon-shield-slash.svg index ba4db6776b..45070e2c52 100644 --- a/public/img/svg/octicon-shield-slash.svg +++ b/public/img/svg/octicon-shield-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-shield-x.svg b/public/img/svg/octicon-shield-x.svg index 9b5e4eb287..2a3a2e23e7 100644 --- a/public/img/svg/octicon-shield-x.svg +++ b/public/img/svg/octicon-shield-x.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-shield.svg b/public/img/svg/octicon-shield.svg index 22b8f039ee..72c267feda 100644 --- a/public/img/svg/octicon-shield.svg +++ b/public/img/svg/octicon-shield.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sidebar-collapse.svg b/public/img/svg/octicon-sidebar-collapse.svg index 29f23e87b6..d8eb04b0f8 100644 --- a/public/img/svg/octicon-sidebar-collapse.svg +++ b/public/img/svg/octicon-sidebar-collapse.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sidebar-expand.svg b/public/img/svg/octicon-sidebar-expand.svg index 9b432e28f2..993c880577 100644 --- a/public/img/svg/octicon-sidebar-expand.svg +++ b/public/img/svg/octicon-sidebar-expand.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sign-in.svg b/public/img/svg/octicon-sign-in.svg index 014d78f6d7..9ed9b01f15 100644 --- a/public/img/svg/octicon-sign-in.svg +++ b/public/img/svg/octicon-sign-in.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sign-out.svg b/public/img/svg/octicon-sign-out.svg index ea39f78d42..85fec36afb 100644 --- a/public/img/svg/octicon-sign-out.svg +++ b/public/img/svg/octicon-sign-out.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-single-select.svg b/public/img/svg/octicon-single-select.svg index f4bfc07cd9..f9c981e4d5 100644 --- a/public/img/svg/octicon-single-select.svg +++ b/public/img/svg/octicon-single-select.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-skip-fill.svg b/public/img/svg/octicon-skip-fill.svg index 6606a56df9..5a18f708e7 100644 --- a/public/img/svg/octicon-skip-fill.svg +++ b/public/img/svg/octicon-skip-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-skip.svg b/public/img/svg/octicon-skip.svg index f889a7e23a..d8ceb454db 100644 --- a/public/img/svg/octicon-skip.svg +++ b/public/img/svg/octicon-skip.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sliders.svg b/public/img/svg/octicon-sliders.svg index 4ad22ab84b..9933b72c39 100644 --- a/public/img/svg/octicon-sliders.svg +++ b/public/img/svg/octicon-sliders.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-smiley.svg b/public/img/svg/octicon-smiley.svg index e68c2280fe..fe5a6715dd 100644 --- a/public/img/svg/octicon-smiley.svg +++ b/public/img/svg/octicon-smiley.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sort-asc.svg b/public/img/svg/octicon-sort-asc.svg index 4588fc6f1a..f58561b5a8 100644 --- a/public/img/svg/octicon-sort-asc.svg +++ b/public/img/svg/octicon-sort-asc.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sort-desc.svg b/public/img/svg/octicon-sort-desc.svg index ed018f31ce..3135d144f9 100644 --- a/public/img/svg/octicon-sort-desc.svg +++ b/public/img/svg/octicon-sort-desc.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sponsor-tiers.svg b/public/img/svg/octicon-sponsor-tiers.svg index dc200285f7..c80b925711 100644 --- a/public/img/svg/octicon-sponsor-tiers.svg +++ b/public/img/svg/octicon-sponsor-tiers.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-square-fill.svg b/public/img/svg/octicon-square-fill.svg index 8b8182b6c3..8976749e7f 100644 --- a/public/img/svg/octicon-square-fill.svg +++ b/public/img/svg/octicon-square-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-square.svg b/public/img/svg/octicon-square.svg index 3243253cf7..c895c22c06 100644 --- a/public/img/svg/octicon-square.svg +++ b/public/img/svg/octicon-square.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-squirrel.svg b/public/img/svg/octicon-squirrel.svg index e3b6f9b151..98f89460db 100644 --- a/public/img/svg/octicon-squirrel.svg +++ b/public/img/svg/octicon-squirrel.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-stack.svg b/public/img/svg/octicon-stack.svg index 42b0f1f952..df1ee4bb57 100644 --- a/public/img/svg/octicon-stack.svg +++ b/public/img/svg/octicon-stack.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-star-fill.svg b/public/img/svg/octicon-star-fill.svg index fd0b38d9e8..3497781fd3 100644 --- a/public/img/svg/octicon-star-fill.svg +++ b/public/img/svg/octicon-star-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-star.svg b/public/img/svg/octicon-star.svg index 5d48dfc17e..4e0f439926 100644 --- a/public/img/svg/octicon-star.svg +++ b/public/img/svg/octicon-star.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-stop.svg b/public/img/svg/octicon-stop.svg index 367686e344..e94f50d0ac 100644 --- a/public/img/svg/octicon-stop.svg +++ b/public/img/svg/octicon-stop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-stopwatch.svg b/public/img/svg/octicon-stopwatch.svg index 2ef2d8b122..c4a2ff74e9 100644 --- a/public/img/svg/octicon-stopwatch.svg +++ b/public/img/svg/octicon-stopwatch.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-strikethrough.svg b/public/img/svg/octicon-strikethrough.svg index 8797d6b697..6d5cfea81f 100644 --- a/public/img/svg/octicon-strikethrough.svg +++ b/public/img/svg/octicon-strikethrough.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sun.svg b/public/img/svg/octicon-sun.svg index 9d6c27c8e2..344ae94810 100644 --- a/public/img/svg/octicon-sun.svg +++ b/public/img/svg/octicon-sun.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-sync.svg b/public/img/svg/octicon-sync.svg index d8fc527464..5a642a85bc 100644 --- a/public/img/svg/octicon-sync.svg +++ b/public/img/svg/octicon-sync.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-tab-external.svg b/public/img/svg/octicon-tab-external.svg index d6b1dba150..30a89215d0 100644 --- a/public/img/svg/octicon-tab-external.svg +++ b/public/img/svg/octicon-tab-external.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-table.svg b/public/img/svg/octicon-table.svg index 5b80f78357..6aca7bf678 100644 --- a/public/img/svg/octicon-table.svg +++ b/public/img/svg/octicon-table.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-tag.svg b/public/img/svg/octicon-tag.svg index a9c9be3ce2..6d8c454845 100644 --- a/public/img/svg/octicon-tag.svg +++ b/public/img/svg/octicon-tag.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-tasklist.svg b/public/img/svg/octicon-tasklist.svg index 41b7c90f6d..c8b4654fb5 100644 --- a/public/img/svg/octicon-tasklist.svg +++ b/public/img/svg/octicon-tasklist.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-telescope-fill.svg b/public/img/svg/octicon-telescope-fill.svg index 6d154d4536..1856614304 100644 --- a/public/img/svg/octicon-telescope-fill.svg +++ b/public/img/svg/octicon-telescope-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-telescope.svg b/public/img/svg/octicon-telescope.svg index 4f98179147..e0f995e983 100644 --- a/public/img/svg/octicon-telescope.svg +++ b/public/img/svg/octicon-telescope.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-terminal.svg b/public/img/svg/octicon-terminal.svg index 27caf24875..bbd1a76114 100644 --- a/public/img/svg/octicon-terminal.svg +++ b/public/img/svg/octicon-terminal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-three-bars.svg b/public/img/svg/octicon-three-bars.svg index 1d741f76bd..806af37bf7 100644 --- a/public/img/svg/octicon-three-bars.svg +++ b/public/img/svg/octicon-three-bars.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-thumbsdown.svg b/public/img/svg/octicon-thumbsdown.svg index 368d98b4d6..3f392394b1 100644 --- a/public/img/svg/octicon-thumbsdown.svg +++ b/public/img/svg/octicon-thumbsdown.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-thumbsup.svg b/public/img/svg/octicon-thumbsup.svg index 69e409768a..d505448bfb 100644 --- a/public/img/svg/octicon-thumbsup.svg +++ b/public/img/svg/octicon-thumbsup.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-tools.svg b/public/img/svg/octicon-tools.svg index eb5201fd96..5727deeea4 100644 --- a/public/img/svg/octicon-tools.svg +++ b/public/img/svg/octicon-tools.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-trash.svg b/public/img/svg/octicon-trash.svg index 8902dcc609..7b287b658a 100644 --- a/public/img/svg/octicon-trash.svg +++ b/public/img/svg/octicon-trash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-triangle-down.svg b/public/img/svg/octicon-triangle-down.svg index 3a0054d834..efbcc3f67e 100644 --- a/public/img/svg/octicon-triangle-down.svg +++ b/public/img/svg/octicon-triangle-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-triangle-left.svg b/public/img/svg/octicon-triangle-left.svg index f2bdfd220c..9769beea9b 100644 --- a/public/img/svg/octicon-triangle-left.svg +++ b/public/img/svg/octicon-triangle-left.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-triangle-right.svg b/public/img/svg/octicon-triangle-right.svg index c73ac10f02..e127d1d50c 100644 --- a/public/img/svg/octicon-triangle-right.svg +++ b/public/img/svg/octicon-triangle-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-triangle-up.svg b/public/img/svg/octicon-triangle-up.svg index 9da17678cc..43b7b49518 100644 --- a/public/img/svg/octicon-triangle-up.svg +++ b/public/img/svg/octicon-triangle-up.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-trophy.svg b/public/img/svg/octicon-trophy.svg index 57cf90ccb4..ee4aaab2cd 100644 --- a/public/img/svg/octicon-trophy.svg +++ b/public/img/svg/octicon-trophy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-typography.svg b/public/img/svg/octicon-typography.svg index dffb43d6dc..3df3688a83 100644 --- a/public/img/svg/octicon-typography.svg +++ b/public/img/svg/octicon-typography.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-unfold.svg b/public/img/svg/octicon-unfold.svg index 8bbd21aa30..e55592b4f9 100644 --- a/public/img/svg/octicon-unfold.svg +++ b/public/img/svg/octicon-unfold.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-unlink.svg b/public/img/svg/octicon-unlink.svg index 7d72ef3a26..95e876a774 100644 --- a/public/img/svg/octicon-unlink.svg +++ b/public/img/svg/octicon-unlink.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-unlock.svg b/public/img/svg/octicon-unlock.svg index 42c000dc86..8267be89aa 100644 --- a/public/img/svg/octicon-unlock.svg +++ b/public/img/svg/octicon-unlock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-unmute.svg b/public/img/svg/octicon-unmute.svg index 6e007d68db..6cb4575826 100644 --- a/public/img/svg/octicon-unmute.svg +++ b/public/img/svg/octicon-unmute.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-unread.svg b/public/img/svg/octicon-unread.svg index bb3c1fc6ed..e3940c6ca3 100644 --- a/public/img/svg/octicon-unread.svg +++ b/public/img/svg/octicon-unread.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-unverified.svg b/public/img/svg/octicon-unverified.svg index af5292662b..60f2c09302 100644 --- a/public/img/svg/octicon-unverified.svg +++ b/public/img/svg/octicon-unverified.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-upload.svg b/public/img/svg/octicon-upload.svg index 184f5d595b..e96db10cde 100644 --- a/public/img/svg/octicon-upload.svg +++ b/public/img/svg/octicon-upload.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-verified.svg b/public/img/svg/octicon-verified.svg index f03be80643..b4e2a83045 100644 --- a/public/img/svg/octicon-verified.svg +++ b/public/img/svg/octicon-verified.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-versions.svg b/public/img/svg/octicon-versions.svg index a52370c952..b44a25786c 100644 --- a/public/img/svg/octicon-versions.svg +++ b/public/img/svg/octicon-versions.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-video.svg b/public/img/svg/octicon-video.svg index a584f75dc7..0b13a8612c 100644 --- a/public/img/svg/octicon-video.svg +++ b/public/img/svg/octicon-video.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-webhook.svg b/public/img/svg/octicon-webhook.svg index e2666a7307..809fef91c8 100644 --- a/public/img/svg/octicon-webhook.svg +++ b/public/img/svg/octicon-webhook.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-workflow.svg b/public/img/svg/octicon-workflow.svg index 276a4980fe..af89aeb88e 100644 --- a/public/img/svg/octicon-workflow.svg +++ b/public/img/svg/octicon-workflow.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-x-circle-fill.svg b/public/img/svg/octicon-x-circle-fill.svg index 8eaecda156..39a0d5e905 100644 --- a/public/img/svg/octicon-x-circle-fill.svg +++ b/public/img/svg/octicon-x-circle-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-x-circle.svg b/public/img/svg/octicon-x-circle.svg index 0fb14679cb..53780ff84a 100644 --- a/public/img/svg/octicon-x-circle.svg +++ b/public/img/svg/octicon-x-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-x.svg b/public/img/svg/octicon-x.svg index eafe159063..4653156318 100644 --- a/public/img/svg/octicon-x.svg +++ b/public/img/svg/octicon-x.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/img/svg/octicon-zap.svg b/public/img/svg/octicon-zap.svg index 1b9c78a489..3db1d82a7d 100644 --- a/public/img/svg/octicon-zap.svg +++ b/public/img/svg/octicon-zap.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index bd8705f127..bbddbe4288 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -69,6 +69,7 @@ import ( "net/http" "strings" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -206,9 +207,36 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) } // Contexter middleware already checks token for user sign in process. -func reqToken() func(ctx *context.APIContext) { +func reqToken(requiredScope auth_model.AccessTokenScope) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { - if true == ctx.Data["IsApiToken"] { + // If OAuth2 token is present + if _, ok := ctx.Data["ApiTokenScope"]; ctx.Data["IsApiToken"] == true && ok { + // no scope required + if requiredScope == "" { + return + } + + // check scope + scope := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) + allow, err := scope.HasScope(requiredScope) + if err != nil { + ctx.Error(http.StatusForbidden, "reqToken", "parsing token failed: "+err.Error()) + return + } + if allow { + return + } + + // if requires 'repo' scope, but only has 'public_repo' scope, allow it only if the repo is public + if requiredScope == auth_model.AccessTokenScopeRepo { + if allowPublicRepo, err := scope.HasScope(auth_model.AccessTokenScopePublicRepo); err == nil && allowPublicRepo { + if ctx.Repo.Repository != nil && !ctx.Repo.Repository.IsPrivate { + return + } + } + } + + ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope) return } if ctx.Context.IsBasicAuth { @@ -631,7 +659,7 @@ func Routes(ctx gocontext.Context) *web.Route { })) m.Group("", func() { - // Miscellaneous + // Miscellaneous (no scope required) if setting.API.EnableSwagger { m.Get("/swagger", func(ctx *context.APIContext) { ctx.Redirect(setting.AppSubURL + "/api/swagger") @@ -670,7 +698,7 @@ func Routes(ctx gocontext.Context) *web.Route { m.Get("/repository", settings.GetGeneralRepoSettings) }) - // Notifications + // Notifications (requires 'notification' scope) m.Group("/notifications", func() { m.Combo(""). Get(notify.ListNotifications). @@ -679,9 +707,9 @@ func Routes(ctx gocontext.Context) *web.Route { m.Combo("/threads/{id}"). Get(notify.GetThread). Patch(notify.ReadThread) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeNotification)) - // Users + // Users (no scope required) m.Group("/users", func() { m.Get("/search", reqExploreSignIn(), user.Search) @@ -701,6 +729,7 @@ func Routes(ctx gocontext.Context) *web.Route { }, context_service.UserAssignmentAPI()) }) + // (no scope required) m.Group("/users", func() { m.Group("/{username}", func() { m.Get("/keys", user.ListPublicKeys) @@ -716,57 +745,62 @@ func Routes(ctx gocontext.Context) *web.Route { m.Get("/subscriptions", user.GetWatchedRepos) }, context_service.UserAssignmentAPI()) - }, reqToken()) + }, reqToken("")) m.Group("/user", func() { m.Get("", user.GetAuthenticatedUser) m.Group("/settings", func() { - m.Get("", user.GetUserSettings) - m.Patch("", bind(api.UserSettingsOptions{}), user.UpdateUserSettings) - }, reqToken()) - m.Combo("/emails").Get(user.ListEmails). - Post(bind(api.CreateEmailOption{}), user.AddEmail). - Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail) + m.Get("", reqToken(auth_model.AccessTokenScopeReadUser), user.GetUserSettings) + m.Patch("", reqToken(auth_model.AccessTokenScopeUser), bind(api.UserSettingsOptions{}), user.UpdateUserSettings) + }) + m.Combo("/emails").Get(reqToken(auth_model.AccessTokenScopeReadUser), user.ListEmails). + Post(reqToken(auth_model.AccessTokenScopeUser), bind(api.CreateEmailOption{}), user.AddEmail). + Delete(reqToken(auth_model.AccessTokenScopeUser), bind(api.DeleteEmailOption{}), user.DeleteEmail) m.Get("/followers", user.ListMyFollowers) m.Group("/following", func() { m.Get("", user.ListMyFollowing) m.Group("/{username}", func() { m.Get("", user.CheckMyFollowing) - m.Put("", user.Follow) - m.Delete("", user.Unfollow) + m.Put("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Follow) // requires 'user:follow' scope + m.Delete("", reqToken(auth_model.AccessTokenScopeUserFollow), user.Unfollow) // requires 'user:follow' scope }, context_service.UserAssignmentAPI()) }) + // (admin:public_key scope) m.Group("/keys", func() { - m.Combo("").Get(user.ListMyPublicKeys). - Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) - m.Combo("/{id}").Get(user.GetPublicKey). - Delete(user.DeletePublicKey) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.ListMyPublicKeys). + Post(reqToken(auth_model.AccessTokenScopeWritePublicKey), bind(api.CreateKeyOption{}), user.CreatePublicKey) + m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadPublicKey), user.GetPublicKey). + Delete(reqToken(auth_model.AccessTokenScopeWritePublicKey), user.DeletePublicKey) }) + + // (admin:application scope) m.Group("/applications", func() { m.Combo("/oauth2"). - Get(user.ListOauth2Applications). - Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) + Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.ListOauth2Applications). + Post(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) m.Combo("/oauth2/{id}"). - Delete(user.DeleteOauth2Application). - Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). - Get(user.GetOauth2Application) - }, reqToken()) - - m.Group("/gpg_keys", func() { - m.Combo("").Get(user.ListMyGPGKeys). - Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) - m.Combo("/{id}").Get(user.GetGPGKey). - Delete(user.DeleteGPGKey) + Delete(reqToken(auth_model.AccessTokenScopeWriteApplication), user.DeleteOauth2Application). + Patch(reqToken(auth_model.AccessTokenScopeWriteApplication), bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). + Get(reqToken(auth_model.AccessTokenScopeReadApplication), user.GetOauth2Application) }) - m.Get("/gpg_key_token", user.GetVerificationToken) - m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) + // (admin:gpg_key scope) + m.Group("/gpg_keys", func() { + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadGPGKey), user.ListMyGPGKeys). + Post(reqToken(auth_model.AccessTokenScopeWriteGPGKey), bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) + m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadGPGKey), user.GetGPGKey). + Delete(reqToken(auth_model.AccessTokenScopeWriteGPGKey), user.DeleteGPGKey) + }) + m.Get("/gpg_key_token", reqToken(auth_model.AccessTokenScopeReadGPGKey), user.GetVerificationToken) + m.Post("/gpg_key_verify", reqToken(auth_model.AccessTokenScopeReadGPGKey), bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) - m.Combo("/repos").Get(user.ListMyRepos). + // (repo scope) + m.Combo("/repos", reqToken(auth_model.AccessTokenScopeRepo)).Get(user.ListMyRepos). Post(bind(api.CreateRepoOption{}), repo.Create) + // (repo scope) m.Group("/starred", func() { m.Get("", user.GetMyStarredRepos) m.Group("/{username}/{reponame}", func() { @@ -774,57 +808,57 @@ func Routes(ctx gocontext.Context) *web.Route { m.Put("", user.Star) m.Delete("", user.Unstar) }, repoAssignment()) - }) - m.Get("/times", repo.ListMyTrackedTimes) - - m.Get("/stopwatches", repo.GetStopwatches) - - m.Get("/subscriptions", user.GetMyWatchedRepos) - - m.Get("/teams", org.ListUserTeams) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Get("/times", reqToken(auth_model.AccessTokenScopeRepo), repo.ListMyTrackedTimes) + m.Get("/stopwatches", reqToken(auth_model.AccessTokenScopeRepo), repo.GetStopwatches) + m.Get("/subscriptions", reqToken(auth_model.AccessTokenScopeRepo), user.GetMyWatchedRepos) + m.Get("/teams", reqToken(auth_model.AccessTokenScopeRepo), org.ListUserTeams) + }, reqToken("")) // Repositories - m.Post("/org/{org}/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) + m.Post("/org/{org}/repos", reqToken(auth_model.AccessTokenScopeAdminOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) - m.Combo("/repositories/{id}", reqToken()).Get(repo.GetByID) + m.Combo("/repositories/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Get(repo.GetByID) m.Group("/repos", func() { m.Get("/search", repo.Search) m.Get("/issues/search", repo.SearchIssues) - m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) + // (repo scope) + m.Post("/migrate", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MigrateRepoOptions{}), repo.Migrate) m.Group("/{username}/{reponame}", func() { m.Combo("").Get(reqAnyRepoReader(), repo.Get). - Delete(reqToken(), reqOwner(), repo.Delete). - Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) - m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) - m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) - m.Post("/transfer/accept", reqToken(), repo.AcceptTransfer) - m.Post("/transfer/reject", reqToken(), repo.RejectTransfer) - m.Combo("/notifications"). - Get(reqToken(), notify.ListRepoNotifications). - Put(reqToken(), notify.ReadRepoNotifications) + Delete(reqToken(auth_model.AccessTokenScopeDeleteRepo), reqOwner(), repo.Delete). + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) + m.Post("/generate", reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) + m.Group("/transfer", func() { + m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) + m.Post("/accept", repo.AcceptTransfer) + m.Post("/reject", repo.RejectTransfer) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Combo("/notifications", reqToken(auth_model.AccessTokenScopeNotification)). + Get(notify.ListRepoNotifications). + Put(notify.ReadRepoNotifications) m.Group("/hooks/git", func() { - m.Combo("").Get(repo.ListGitHooks) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListGitHooks) m.Group("/{id}", func() { - m.Combo("").Get(repo.GetGitHook). - Patch(bind(api.EditGitHookOption{}), repo.EditGitHook). - Delete(repo.DeleteGitHook) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetGitHook). + Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditGitHookOption{}), repo.EditGitHook). + Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteGitHook) }) - }, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) + }, reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true)) m.Group("/hooks", func() { - m.Combo("").Get(repo.ListHooks). - Post(bind(api.CreateHookOption{}), repo.CreateHook) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.ListHooks). + Post(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.CreateHookOption{}), repo.CreateHook) m.Group("/{id}", func() { - m.Combo("").Get(repo.GetHook). - Patch(bind(api.EditHookOption{}), repo.EditHook). - Delete(repo.DeleteHook) - m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadRepoHook), repo.GetHook). + Patch(reqToken(auth_model.AccessTokenScopeWriteRepoHook), bind(api.EditHookOption{}), repo.EditHook). + Delete(reqToken(auth_model.AccessTokenScopeWriteRepoHook), repo.DeleteHook) + m.Post("/tests", reqToken(auth_model.AccessTokenScopeReadRepoHook), context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) }) - }, reqToken(), reqAdmin(), reqWebhooksEnabled()) + }, reqAdmin(), reqWebhooksEnabled()) m.Group("/collaborators", func() { m.Get("", reqAnyRepoReader(), repo.ListCollaborators) m.Group("/{collaborator}", func() { @@ -832,26 +866,26 @@ func Routes(ctx gocontext.Context) *web.Route { Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(reqAdmin(), repo.DeleteCollaborator) m.Get("/permission", repo.GetRepoPermissions) - }, reqToken()) - }, reqToken()) - m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees) - m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers) + }) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Get("/assignees", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetAssignees) + m.Get("/reviewers", reqToken(auth_model.AccessTokenScopeRepo), reqAnyRepoReader(), repo.GetReviewers) m.Group("/teams", func() { m.Get("", reqAnyRepoReader(), repo.ListTeams) m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). Put(reqAdmin(), repo.AddTeam). Delete(reqAdmin(), repo.DeleteTeam) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeRepo)) m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). - Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) m.Group("/branches", func() { m.Get("", repo.ListBranches) m.Get("/*", repo.GetBranch) - m.Delete("/*", reqRepoWriter(unit.TypeCode), repo.DeleteBranch) - m.Post("", reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) + m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.DeleteBranch) + m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) @@ -861,74 +895,74 @@ func Routes(ctx gocontext.Context) *web.Route { m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection) m.Delete("", repo.DeleteBranchProtection) }) - }, reqToken(), reqAdmin()) + }, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) m.Group("/tags", func() { m.Get("", repo.ListTags) m.Get("/*", repo.GetTag) - m.Post("", reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) - m.Delete("/*", repo.DeleteTag) + m.Post("", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), bind(api.CreateTagOption{}), repo.CreateTag) + m.Delete("/*", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTag) }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) m.Combo("/{id}").Get(repo.GetDeployKey). Delete(repo.DeleteDeploykey) - }, reqToken(), reqAdmin()) + }, reqToken(auth_model.AccessTokenScopeRepo), reqAdmin()) m.Group("/times", func() { m.Combo("").Get(repo.ListTrackedTimesByRepository) m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) - }, mustEnableIssues, reqToken()) + }, mustEnableIssues, reqToken(auth_model.AccessTokenScopeRepo)) m.Group("/wiki", func() { m.Combo("/page/{pageName}"). Get(repo.GetWikiPage). - Patch(mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). - Delete(mustNotBeArchived, reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) + Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). + Delete(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) m.Get("/revisions/{pageName}", repo.ListPageRevisions) - m.Post("/new", mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) + m.Post("/new", mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) m.Get("/pages", repo.ListWikiPages) }, mustEnableWiki) m.Group("/issues", func() { m.Combo("").Get(repo.ListIssues). - Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) m.Group("/comments", func() { m.Get("", repo.ListRepoIssueComments) m.Group("/{id}", func() { m.Combo(""). Get(repo.GetIssueComment). - Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). - Delete(reqToken(), repo.DeleteIssueComment) + Patch(mustNotBeArchived, reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueComment) m.Combo("/reactions"). Get(repo.GetIssueCommentReactions). - Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). - Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). + Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) m.Group("/assets", func() { m.Combo(""). Get(repo.ListIssueCommentAttachments). - Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueCommentAttachment) m.Combo("/{asset}"). Get(repo.GetIssueCommentAttachment). - Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). - Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment) + Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueCommentAttachment) }, mustEnableAttachments) }) }) m.Group("/{index}", func() { m.Combo("").Get(repo.GetIssue). - Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue). - Delete(reqToken(), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) + Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditIssueOption{}), repo.EditIssue). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). - Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) - m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) + m.Combo("/{id}", reqToken(auth_model.AccessTokenScopeRepo)).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Get("/timeline", repo.ListIssueCommentsAndTimeline) m.Group("/labels", func() { m.Combo("").Get(repo.ListIssueLabels). - Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). - Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). - Delete(reqToken(), repo.ClearIssueLabels) - m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). + Put(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.ClearIssueLabels) + m.Delete("/{id}", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueLabel) }) m.Group("/times", func() { m.Combo(""). @@ -936,125 +970,126 @@ func Routes(ctx gocontext.Context) *web.Route { Post(bind(api.AddTimeOption{}), repo.AddTime). Delete(repo.ResetIssueTime) m.Delete("/{id}", repo.DeleteTime) - }, reqToken()) - m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) + }, reqToken(auth_model.AccessTokenScopeRepo)) + m.Combo("/deadline").Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Group("/stopwatch", func() { - m.Post("/start", reqToken(), repo.StartIssueStopwatch) - m.Post("/stop", reqToken(), repo.StopIssueStopwatch) - m.Delete("/delete", reqToken(), repo.DeleteIssueStopwatch) + m.Post("/start", reqToken(auth_model.AccessTokenScopeRepo), repo.StartIssueStopwatch) + m.Post("/stop", reqToken(auth_model.AccessTokenScopeRepo), repo.StopIssueStopwatch) + m.Delete("/delete", reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteIssueStopwatch) }) m.Group("/subscriptions", func() { m.Get("", repo.GetIssueSubscribers) - m.Get("/check", reqToken(), repo.CheckIssueSubscription) - m.Put("/{user}", reqToken(), repo.AddIssueSubscription) - m.Delete("/{user}", reqToken(), repo.DelIssueSubscription) + m.Get("/check", reqToken(auth_model.AccessTokenScopeRepo), repo.CheckIssueSubscription) + m.Put("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.AddIssueSubscription) + m.Delete("/{user}", reqToken(auth_model.AccessTokenScopeRepo), repo.DelIssueSubscription) }) m.Combo("/reactions"). Get(repo.GetIssueReactions). - Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueReaction). - Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.PostIssueReaction). + Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) m.Group("/assets", func() { m.Combo(""). Get(repo.ListIssueAttachments). - Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CreateIssueAttachment) m.Combo("/{asset}"). Get(repo.GetIssueAttachment). - Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). - Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment) + Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueAttachment) }, mustEnableAttachments) }) }, mustEnableIssuesOrPulls) m.Group("/labels", func() { m.Combo("").Get(repo.ListLabels). - Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) m.Combo("/{id}").Get(repo.GetLabel). - Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). - Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteLabel) }) - m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown) - m.Post("/markdown/raw", misc.MarkdownRaw) + m.Post("/markdown", reqToken(auth_model.AccessTokenScopeRepo), bind(api.MarkdownOption{}), misc.Markdown) + m.Post("/markdown/raw", reqToken(auth_model.AccessTokenScopeRepo), misc.MarkdownRaw) m.Group("/milestones", func() { m.Combo("").Get(repo.ListMilestones). - Post(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) m.Combo("/{id}").Get(repo.GetMilestone). - Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). - Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) }) m.Get("/stargazers", repo.ListStargazers) m.Get("/subscribers", repo.ListSubscribers) m.Group("/subscription", func() { m.Get("", user.IsWatching) - m.Put("", reqToken(), user.Watch) - m.Delete("", reqToken(), user.Unwatch) + m.Put("", reqToken(auth_model.AccessTokenScopeRepo), user.Watch) + m.Delete("", reqToken(auth_model.AccessTokenScopeRepo), user.Unwatch) }) m.Group("/releases", func() { m.Combo("").Get(repo.ListReleases). - Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) + m.Combo("/latest").Get(repo.GetLatestRelease) m.Group("/{id}", func() { m.Combo("").Get(repo.GetRelease). - Patch(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). - Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) m.Group("/assets", func() { m.Combo("").Get(repo.ListReleaseAttachments). - Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) + Post(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) m.Combo("/{asset}").Get(repo.GetReleaseAttachment). - Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). - Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) + Patch(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) }) }) m.Group("/tags", func() { m.Combo("/{tag}"). Get(repo.GetReleaseByTag). - Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) + Delete(reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseByTag) }) }, reqRepoReader(unit.TypeReleases)) - m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync) - m.Post("/push_mirrors-sync", reqAdmin(), repo.PushMirrorSync) + m.Post("/mirror-sync", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeCode), repo.MirrorSync) + m.Post("/push_mirrors-sync", reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo), repo.PushMirrorSync) m.Group("/push_mirrors", func() { m.Combo("").Get(repo.ListPushMirrors). Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror) m.Combo("/{name}"). Delete(repo.DeletePushMirrorByRemoteName). Get(repo.GetPushMirrorByName) - }, reqAdmin()) + }, reqAdmin(), reqToken(auth_model.AccessTokenScopeRepo)) m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { m.Combo("").Get(repo.ListPullRequests). - Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) m.Group("/{index}", func() { m.Combo("").Get(repo.GetPullRequest). - Patch(reqToken(), bind(api.EditPullRequestOption{}), repo.EditPullRequest) + Patch(reqToken(auth_model.AccessTokenScopeRepo), bind(api.EditPullRequestOption{}), repo.EditPullRequest) m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch) - m.Post("/update", reqToken(), repo.UpdatePullRequest) + m.Post("/update", reqToken(auth_model.AccessTokenScopeRepo), repo.UpdatePullRequest) m.Get("/commits", repo.GetPullRequestCommits) m.Get("/files", repo.GetPullRequestFiles) m.Combo("/merge").Get(repo.IsPullRequestMerged). - Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). - Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge) + Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest). + Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.CancelScheduledAutoMerge) m.Group("/reviews", func() { m.Combo(""). Get(repo.ListPullReviews). - Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) m.Group("/{id}", func() { m.Combo(""). Get(repo.GetPullReview). - Delete(reqToken(), repo.DeletePullReview). - Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeletePullReview). + Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) m.Combo("/comments"). Get(repo.GetPullReviewComments) - m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) - m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) + m.Post("/dismissals", reqToken(auth_model.AccessTokenScopeRepo), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) + m.Post("/undismissals", reqToken(auth_model.AccessTokenScopeRepo), repo.UnDismissPullReview) }) }) - m.Combo("/requested_reviewers"). - Delete(reqToken(), bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). - Post(reqToken(), bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) + m.Combo("/requested_reviewers", reqToken(auth_model.AccessTokenScopeRepo)). + Delete(bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). + Post(bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) }) }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) m.Group("/statuses", func() { m.Combo("/{sha}").Get(repo.GetCommitStatuses). - Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) + Post(reqToken(auth_model.AccessTokenScopeRepoStatus), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) }, reqRepoReader(unit.TypeCode)) m.Group("/commits", func() { m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) @@ -1075,7 +1110,7 @@ func Routes(ctx gocontext.Context) *web.Route { m.Get("/tags/{sha}", repo.GetAnnotatedTag) m.Get("/notes/{sha}", repo.GetNote) }, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) - m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) + m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(auth_model.AccessTokenScopeRepo), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) m.Group("/contents", func() { m.Get("", repo.GetContentsList) m.Get("/*", repo.GetContents) @@ -1083,15 +1118,15 @@ func Routes(ctx gocontext.Context) *web.Route { m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, repo.CreateFile) m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, repo.UpdateFile) m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, repo.DeleteFile) - }, reqToken()) + }, reqToken(auth_model.AccessTokenScopeRepo)) }, reqRepoReader(unit.TypeCode)) m.Get("/signing-key.gpg", misc.SigningKey) m.Group("/topics", func() { m.Combo("").Get(repo.ListTopics). - Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) + Put(reqToken(auth_model.AccessTokenScopeRepo), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) m.Group("/{topic}", func() { - m.Combo("").Put(reqToken(), repo.AddTopic). - Delete(reqToken(), repo.DeleteTopic) + m.Combo("").Put(reqToken(auth_model.AccessTokenScopeRepo), repo.AddTopic). + Delete(reqToken(auth_model.AccessTokenScopeRepo), repo.DeleteTopic) }, reqAdmin()) }, reqAnyRepoReader()) m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) @@ -1102,49 +1137,49 @@ func Routes(ctx gocontext.Context) *web.Route { // NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs m.Group("/packages/{username}", func() { m.Group("/{type}/{name}/{version}", func() { - m.Get("", packages.GetPackage) - m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) - m.Get("/files", packages.ListPackageFiles) + m.Get("", reqToken(auth_model.AccessTokenScopeReadPackage), packages.GetPackage) + m.Delete("", reqToken(auth_model.AccessTokenScopeDeletePackage), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) + m.Get("/files", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackageFiles) }) - m.Get("/", packages.ListPackages) + m.Get("/", reqToken(auth_model.AccessTokenScopeReadPackage), packages.ListPackages) }, context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) // Organizations - m.Get("/user/orgs", reqToken(), org.ListMyOrgs) + m.Get("/user/orgs", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMyOrgs) m.Group("/users/{username}/orgs", func() { - m.Get("", org.ListUserOrgs) - m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListUserOrgs) + m.Get("/{org}/permissions", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetUserOrgsPermissions) }, context_service.UserAssignmentAPI()) - m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create) - m.Get("/orgs", org.GetAll) + m.Post("/orgs", reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateOrgOption{}), org.Create) + m.Get("/orgs", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetAll) m.Group("/orgs/{org}", func() { - m.Combo("").Get(org.Get). - Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). - Delete(reqToken(), reqOrgOwnership(), org.Delete) - m.Combo("/repos").Get(user.ListOrgRepos). - Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.Get). + Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.Delete) + m.Combo("/repos").Get(reqToken(auth_model.AccessTokenScopeReadOrg), user.ListOrgRepos). + Post(reqToken(auth_model.AccessTokenScopeWriteOrg), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) m.Group("/members", func() { - m.Get("", org.ListMembers) - m.Combo("/{username}").Get(org.IsMember). - Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListMembers) + m.Combo("/{username}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.IsMember). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteMember) }) m.Group("/public_members", func() { - m.Get("", org.ListPublicMembers) - m.Combo("/{username}").Get(org.IsPublicMember). - Put(reqToken(), reqOrgMembership(), org.PublicizeMember). - Delete(reqToken(), reqOrgMembership(), org.ConcealMember) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListPublicMembers) + m.Combo("/{username}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.IsPublicMember). + Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.PublicizeMember). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgMembership(), org.ConcealMember) }) m.Group("/teams", func() { - m.Get("", org.ListTeams) - m.Post("", reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) - m.Get("/search", org.SearchTeam) - }, reqToken(), reqOrgMembership()) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListTeams) + m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam) + m.Get("/search", reqToken(auth_model.AccessTokenScopeReadOrg), org.SearchTeam) + }, reqOrgMembership()) m.Group("/labels", func() { - m.Get("", org.ListLabels) - m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) - m.Combo("/{id}").Get(org.GetLabel). - Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). - Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.ListLabels) + m.Post("", reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) + m.Combo("/{id}").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetLabel). + Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteLabel) }) m.Group("/hooks", func() { m.Combo("").Get(org.ListHooks). @@ -1152,27 +1187,27 @@ func Routes(ctx gocontext.Context) *web.Route { m.Combo("/{id}").Get(org.GetHook). Patch(bind(api.EditHookOption{}), org.EditHook). Delete(org.DeleteHook) - }, reqToken(), reqOrgOwnership(), reqWebhooksEnabled()) + }, reqToken(auth_model.AccessTokenScopeAdminOrgHook), reqOrgOwnership(), reqWebhooksEnabled()) }, orgAssignment(true)) m.Group("/teams/{teamid}", func() { - m.Combo("").Get(org.GetTeam). - Patch(reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). - Delete(reqOrgOwnership(), org.DeleteTeam) + m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeam). + Patch(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.DeleteTeam) m.Group("/members", func() { - m.Get("", org.GetTeamMembers) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMembers) m.Combo("/{username}"). - Get(org.GetTeamMember). - Put(reqOrgOwnership(), org.AddTeamMember). - Delete(reqOrgOwnership(), org.RemoveTeamMember) + Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamMember). + Put(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.AddTeamMember). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), reqOrgOwnership(), org.RemoveTeamMember) }) m.Group("/repos", func() { - m.Get("", org.GetTeamRepos) + m.Get("", reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepos) m.Combo("/{org}/{reponame}"). - Put(org.AddTeamRepository). - Delete(org.RemoveTeamRepository). - Get(org.GetTeamRepo) + Put(reqToken(auth_model.AccessTokenScopeWriteOrg), org.AddTeamRepository). + Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), org.RemoveTeamRepository). + Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepo) }) - }, orgAssignment(false, true), reqToken(), reqTeamMembership()) + }, orgAssignment(false, true), reqToken(""), reqTeamMembership()) m.Group("/admin", func() { m.Group("/cron", func() { @@ -1200,7 +1235,7 @@ func Routes(ctx gocontext.Context) *web.Route { m.Post("/{username}/{reponame}", admin.AdoptRepository) m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) }) - }, reqToken(), reqSiteAdmin()) + }, reqToken(auth_model.AccessTokenScopeSudo), reqSiteAdmin()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch) diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 529211253e..699ca3161c 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -654,7 +654,7 @@ func CreateIssue(ctx *context.APIContext) { } if form.Closed { - if err := issue_service.ChangeStatus(issue, ctx.Doer, true); err != nil { + if err := issue_service.ChangeStatus(issue, ctx.Doer, "", true); err != nil { if issues_model.IsErrDependenciesLeft(err) { ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") return @@ -826,7 +826,7 @@ func EditIssue(ctx *context.APIContext) { } if statusChangeComment != nil { - notification.NotifyIssueChangeStatus(ctx, ctx.Doer, issue, statusChangeComment, issue.IsClosed) + notification.NotifyIssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed) } // Refetch from database to assign some automatic values diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 8fdbec4b89..8164b38694 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -600,7 +600,7 @@ func EditPullRequest(ctx *context.APIContext) { } if statusChangeComment != nil { - notification.NotifyIssueChangeStatus(ctx, ctx.Doer, issue, statusChangeComment, issue.IsClosed) + notification.NotifyIssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed) } // change pull target branch diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index d0b20102f7..c01e66150f 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -67,6 +67,47 @@ func GetRelease(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.ToRelease(release)) } +// GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at +func GetLatestRelease(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/releases/latest repository repoGetLatestRelease + // --- + // summary: Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/Release" + // "404": + // "$ref": "#/responses/notFound" + release, err := repo_model.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID) + if err != nil && !repo_model.IsErrReleaseNotExist(err) { + ctx.Error(http.StatusInternalServerError, "GetLatestRelease", err) + return + } + if err != nil && repo_model.IsErrReleaseNotExist(err) || + release.IsTag || release.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound() + return + } + + if err := release.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) + return + } + ctx.JSON(http.StatusOK, convert.ToRelease(release)) +} + // ListReleases list a repository's releases func ListReleases(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases diff --git a/routers/private/mail.go b/routers/private/mail.go index 622a01dd8f..e5e162c880 100644 --- a/routers/private/mail.go +++ b/routers/private/mail.go @@ -81,7 +81,7 @@ func SendEmail(ctx *context.PrivateContext) { func sendEmail(ctx *context.PrivateContext, subject, message string, to []string) { for _, email := range to { - msg := mailer.NewMessage([]string{email}, subject, message) + msg := mailer.NewMessage(email, subject, message) mailer.SendAsync(msg) } diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index eaa02588ca..1f71e81785 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -5,9 +5,11 @@ package admin import ( + "fmt" "net/http" "net/url" "os" + "strconv" "strings" system_model "code.gitea.io/gitea/models/system" @@ -201,6 +203,16 @@ func ChangeConfig(ctx *context.Context) { value := ctx.FormString("value") version := ctx.FormInt("version") + if check, ok := changeConfigChecks[key]; ok { + if err := check(ctx, value); err != nil { + log.Warn("refused to set setting: %v", err) + ctx.JSON(http.StatusOK, map[string]string{ + "err": ctx.Tr("admin.config.set_setting_failed", key), + }) + return + } + } + if err := system_model.SetSetting(&system_model.Setting{ SettingKey: key, SettingValue: value, @@ -217,3 +229,18 @@ func ChangeConfig(ctx *context.Context) { "version": version + 1, }) } + +var changeConfigChecks = map[string]func(ctx *context.Context, newValue string) error{ + system_model.KeyPictureDisableGravatar: func(_ *context.Context, newValue string) error { + if v, _ := strconv.ParseBool(newValue); setting.OfflineMode && !v { + return fmt.Errorf("%q should be true when OFFLINE_MODE is true", system_model.KeyPictureDisableGravatar) + } + return nil + }, + system_model.KeyPictureEnableFederatedAvatar: func(_ *context.Context, newValue string) error { + if v, _ := strconv.ParseBool(newValue); setting.OfflineMode && v { + return fmt.Errorf("%q cannot be false when OFFLINE_MODE is true", system_model.KeyPictureEnableFederatedAvatar) + } + return nil + }, +} diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go index 7c6d1ed840..88fb47ca01 100644 --- a/routers/web/admin/packages.go +++ b/routers/web/admin/packages.go @@ -51,12 +51,18 @@ func Packages(ctx *context.Context) { return } - totalBlobSize, err := packages_model.GetTotalBlobSize() + totalBlobSize, err := packages_model.GetTotalBlobSize(ctx) if err != nil { ctx.ServerError("GetTotalBlobSize", err) return } + totalUnreferencedBlobSize, err := packages_model.GetTotalUnreferencedBlobSize(ctx) + if err != nil { + ctx.ServerError("CalculateBlobSize", err) + return + } + ctx.Data["Title"] = ctx.Tr("packages.title") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminPackages"] = true @@ -65,8 +71,9 @@ func Packages(ctx *context.Context) { ctx.Data["AvailableTypes"] = packages_model.TypeList ctx.Data["SortType"] = sort ctx.Data["PackageDescriptors"] = pds - ctx.Data["Total"] = total - ctx.Data["TotalBlobSize"] = totalBlobSize + ctx.Data["TotalCount"] = total + ctx.Data["TotalBlobSize"] = totalBlobSize - totalUnreferencedBlobSize + ctx.Data["TotalUnreferencedBlobSize"] = totalUnreferencedBlobSize pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager.AddParamString("q", query) diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 3d70ca9a50..be60a0c73b 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -847,6 +847,11 @@ func SignInOAuth(ctx *context.Context) { return } + redirectTo := ctx.FormString("redirect_to") + if len(redirectTo) > 0 { + middleware.SetRedirectToCookie(ctx.Resp, redirectTo) + } + // try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user user, gothUser, err := oAuth2UserLoginCallback(authSource, ctx.Req, ctx.Resp) if err == nil && user != nil { diff --git a/routers/web/org/main_test.go b/routers/web/org/main_test.go new file mode 100644 index 0000000000..41323a3601 --- /dev/null +++ b/routers/web/org/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package org_test + +import ( + "path/filepath" + "testing" + + "code.gitea.io/gitea/models/unittest" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m, &unittest.TestOptions{ + GiteaRootPath: filepath.Join("..", "..", ".."), + }) +} diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go new file mode 100644 index 0000000000..1ce44d4866 --- /dev/null +++ b/routers/web/org/projects.go @@ -0,0 +1,670 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package org + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + + issues_model "code.gitea.io/gitea/models/issues" + project_model "code.gitea.io/gitea/models/project" + "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + shared_user "code.gitea.io/gitea/routers/web/shared/user" + "code.gitea.io/gitea/services/forms" +) + +const ( + tplProjects base.TplName = "org/projects/list" + tplProjectsNew base.TplName = "org/projects/new" + tplProjectsView base.TplName = "org/projects/view" + tplGenericProjectsNew base.TplName = "user/project" +) + +// MustEnableProjects check if projects are enabled in settings +func MustEnableProjects(ctx *context.Context) { + if unit.TypeProjects.UnitGlobalDisabled() { + ctx.NotFound("EnableKanbanBoard", nil) + return + } +} + +// Projects renders the home page of projects +func Projects(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.project_board") + + sortType := ctx.FormTrim("sort") + + isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed" + page := ctx.FormInt("page") + if page <= 1 { + page = 1 + } + + projects, total, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: ctx.ContextUser.ID, + Page: page, + IsClosed: util.OptionalBoolOf(isShowClosed), + SortType: sortType, + Type: project_model.TypeOrganization, + }) + if err != nil { + ctx.ServerError("FindProjects", err) + return + } + + opTotal, err := project_model.CountProjects(ctx, project_model.SearchOptions{ + OwnerID: ctx.ContextUser.ID, + IsClosed: util.OptionalBoolOf(!isShowClosed), + Type: project_model.TypeOrganization, + }) + if err != nil { + ctx.ServerError("CountProjects", err) + return + } + + if isShowClosed { + ctx.Data["OpenCount"] = opTotal + ctx.Data["ClosedCount"] = total + } else { + ctx.Data["OpenCount"] = total + ctx.Data["ClosedCount"] = opTotal + } + + ctx.Data["Projects"] = projects + shared_user.RenderUserHeader(ctx) + + if isShowClosed { + ctx.Data["State"] = "closed" + } else { + ctx.Data["State"] = "open" + } + + for _, project := range projects { + project.RenderedContent = project.Description + } + + numPages := 0 + if total > 0 { + numPages = (int(total) - 1/setting.UI.IssuePagingNum) + } + + pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, numPages) + pager.AddParam(ctx, "state", "State") + ctx.Data["Page"] = pager + + ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) + ctx.Data["IsShowClosed"] = isShowClosed + ctx.Data["PageIsViewProjects"] = true + ctx.Data["SortType"] = sortType + + ctx.HTML(http.StatusOK, tplProjects) +} + +func canWriteUnit(ctx *context.Context) bool { + if ctx.ContextUser.IsOrganization() { + return ctx.Org.CanWriteUnit(ctx, unit.TypeProjects) + } + return ctx.Doer != nil && ctx.ContextUser.ID == ctx.Doer.ID +} + +// NewProject render creating a project page +func NewProject(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.projects.new") + ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() + ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) + ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink() + shared_user.RenderUserHeader(ctx) + ctx.HTML(http.StatusOK, tplProjectsNew) +} + +// NewProjectPost creates a new project +func NewProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.CreateProjectForm) + ctx.Data["Title"] = ctx.Tr("repo.projects.new") + shared_user.RenderUserHeader(ctx) + + if ctx.HasError() { + ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) + ctx.Data["PageIsViewProjects"] = true + ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() + ctx.HTML(http.StatusOK, tplProjectsNew) + return + } + + if err := project_model.NewProject(&project_model.Project{ + OwnerID: ctx.ContextUser.ID, + Title: form.Title, + Description: form.Content, + CreatorID: ctx.Doer.ID, + BoardType: form.BoardType, + Type: project_model.TypeOrganization, + }); err != nil { + ctx.ServerError("NewProject", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.projects.create_success", form.Title)) + ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") +} + +// ChangeProjectStatus updates the status of a project between "open" and "close" +func ChangeProjectStatus(ctx *context.Context) { + toClose := false + switch ctx.Params(":action") { + case "open": + toClose = false + case "close": + toClose = true + default: + ctx.Redirect(ctx.Repo.RepoLink + "/projects") + } + id := ctx.ParamsInt64(":id") + + if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx.Repo.Repository.ID, id, toClose); err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", err) + } else { + ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err) + } + return + } + ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action"))) +} + +// DeleteProject delete a project +func DeleteProject(ctx *context.Context) { + p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return + } + if p.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("", nil) + return + } + + if err := project_model.DeleteProjectByID(ctx, p.ID); err != nil { + ctx.Flash.Error("DeleteProjectByID: " + err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success")) + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "redirect": ctx.Repo.RepoLink + "/projects", + }) +} + +// EditProject allows a project to be edited +func EditProject(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.projects.edit") + ctx.Data["PageIsEditProjects"] = true + ctx.Data["PageIsViewProjects"] = true + ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) + shared_user.RenderUserHeader(ctx) + + p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return + } + if p.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("", nil) + return + } + + ctx.Data["title"] = p.Title + ctx.Data["content"] = p.Description + + ctx.HTML(http.StatusOK, tplProjectsNew) +} + +// EditProjectPost response for editing a project +func EditProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.CreateProjectForm) + ctx.Data["Title"] = ctx.Tr("repo.projects.edit") + ctx.Data["PageIsEditProjects"] = true + ctx.Data["PageIsViewProjects"] = true + ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) + shared_user.RenderUserHeader(ctx) + + if ctx.HasError() { + ctx.HTML(http.StatusOK, tplProjectsNew) + return + } + + p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return + } + if p.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("", nil) + return + } + + p.Title = form.Title + p.Description = form.Content + if err = project_model.UpdateProject(ctx, p); err != nil { + ctx.ServerError("UpdateProjects", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.projects.edit_success", p.Title)) + ctx.Redirect(ctx.Repo.RepoLink + "/projects") +} + +// ViewProject renders the project board for a project +func ViewProject(ctx *context.Context) { + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return + } + if project.OwnerID != ctx.ContextUser.ID { + ctx.NotFound("", nil) + return + } + + boards, err := project_model.GetBoards(ctx, project.ID) + if err != nil { + ctx.ServerError("GetProjectBoards", err) + return + } + + if boards[0].ID == 0 { + boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") + } + + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards) + if err != nil { + ctx.ServerError("LoadIssuesOfBoards", err) + return + } + + linkedPrsMap := make(map[int64][]*issues_model.Issue) + for _, issuesList := range issuesMap { + for _, issue := range issuesList { + var referencedIds []int64 + for _, comment := range issue.Comments { + if comment.RefIssueID != 0 && comment.RefIsPull { + referencedIds = append(referencedIds, comment.RefIssueID) + } + } + + if len(referencedIds) > 0 { + if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ + IssueIDs: referencedIds, + IsPull: util.OptionalBoolTrue, + }); err == nil { + linkedPrsMap[issue.ID] = linkedPrs + } + } + } + } + + project.RenderedContent = project.Description + ctx.Data["LinkedPRs"] = linkedPrsMap + ctx.Data["PageIsViewProjects"] = true + ctx.Data["CanWriteProjects"] = canWriteUnit(ctx) + ctx.Data["Project"] = project + ctx.Data["IssuesMap"] = issuesMap + ctx.Data["Boards"] = boards + shared_user.RenderUserHeader(ctx) + + ctx.HTML(http.StatusOK, tplProjectsView) +} + +func getActionIssues(ctx *context.Context) []*issues_model.Issue { + commaSeparatedIssueIDs := ctx.FormString("issue_ids") + if len(commaSeparatedIssueIDs) == 0 { + return nil + } + issueIDs := make([]int64, 0, 10) + for _, stringIssueID := range strings.Split(commaSeparatedIssueIDs, ",") { + issueID, err := strconv.ParseInt(stringIssueID, 10, 64) + if err != nil { + ctx.ServerError("ParseInt", err) + return nil + } + issueIDs = append(issueIDs, issueID) + } + issues, err := issues_model.GetIssuesByIDs(ctx, issueIDs) + if err != nil { + ctx.ServerError("GetIssuesByIDs", err) + return nil + } + // Check access rights for all issues + issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues) + prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests) + for _, issue := range issues { + if issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect")) + return nil + } + if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled { + ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) + return nil + } + if err = issue.LoadAttributes(ctx); err != nil { + ctx.ServerError("LoadAttributes", err) + return nil + } + } + return issues +} + +// UpdateIssueProject change an issue's project +func UpdateIssueProject(ctx *context.Context) { + issues := getActionIssues(ctx) + if ctx.Written() { + return + } + + projectID := ctx.FormInt64("id") + for _, issue := range issues { + oldProjectID := issue.ProjectID() + if oldProjectID == projectID { + continue + } + + if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { + ctx.ServerError("ChangeProjectAssign", err) + return + } + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ok": true, + }) +} + +// DeleteProjectBoard allows for the deletion of a project board +func DeleteProjectBoard(ctx *context.Context) { + if ctx.Doer == nil { + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "Only signed in users are allowed to perform this action.", + }) + return + } + + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return + } + + pb, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + if err != nil { + ctx.ServerError("GetProjectBoard", err) + return + } + if pb.ProjectID != ctx.ParamsInt64(":id") { + ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ + "message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", pb.ID, project.ID), + }) + return + } + + if project.OwnerID != ctx.ContextUser.ID { + ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ + "message": fmt.Sprintf("ProjectBoard[%d] is not in Owner[%d] as expected", pb.ID, ctx.ContextUser.ID), + }) + return + } + + if err := project_model.DeleteBoardByID(ctx.ParamsInt64(":boardID")); err != nil { + ctx.ServerError("DeleteProjectBoardByID", err) + return + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ok": true, + }) +} + +// AddBoardToProjectPost allows a new board to be added to a project. +func AddBoardToProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.EditProjectBoardForm) + + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return + } + + if err := project_model.NewBoard(&project_model.Board{ + ProjectID: project.ID, + Title: form.Title, + Color: form.Color, + CreatorID: ctx.Doer.ID, + }); err != nil { + ctx.ServerError("NewProjectBoard", err) + return + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ok": true, + }) +} + +// CheckProjectBoardChangePermissions check permission +func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Board) { + if ctx.Doer == nil { + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "Only signed in users are allowed to perform this action.", + }) + return nil, nil + } + + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return nil, nil + } + + board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + if err != nil { + ctx.ServerError("GetProjectBoard", err) + return nil, nil + } + if board.ProjectID != ctx.ParamsInt64(":id") { + ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ + "message": fmt.Sprintf("ProjectBoard[%d] is not in Project[%d] as expected", board.ID, project.ID), + }) + return nil, nil + } + + if project.OwnerID != ctx.ContextUser.ID { + ctx.JSON(http.StatusUnprocessableEntity, map[string]string{ + "message": fmt.Sprintf("ProjectBoard[%d] is not in Repository[%d] as expected", board.ID, project.ID), + }) + return nil, nil + } + return project, board +} + +// EditProjectBoard allows a project board's to be updated +func EditProjectBoard(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.EditProjectBoardForm) + _, board := CheckProjectBoardChangePermissions(ctx) + if ctx.Written() { + return + } + + if form.Title != "" { + board.Title = form.Title + } + + board.Color = form.Color + + if form.Sorting != 0 { + board.Sorting = form.Sorting + } + + if err := project_model.UpdateBoard(ctx, board); err != nil { + ctx.ServerError("UpdateProjectBoard", err) + return + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ok": true, + }) +} + +// SetDefaultProjectBoard set default board for uncategorized issues/pulls +func SetDefaultProjectBoard(ctx *context.Context) { + project, board := CheckProjectBoardChangePermissions(ctx) + if ctx.Written() { + return + } + + if err := project_model.SetDefaultBoard(project.ID, board.ID); err != nil { + ctx.ServerError("SetDefaultBoard", err) + return + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ok": true, + }) +} + +// MoveIssues moves or keeps issues in a column and sorts them inside that column +func MoveIssues(ctx *context.Context) { + if ctx.Doer == nil { + ctx.JSON(http.StatusForbidden, map[string]string{ + "message": "Only signed in users are allowed to perform this action.", + }) + return + } + + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound("ProjectNotExist", nil) + } else { + ctx.ServerError("GetProjectByID", err) + } + return + } + if project.OwnerID != ctx.ContextUser.ID { + ctx.NotFound("InvalidRepoID", nil) + return + } + + var board *project_model.Board + + if ctx.ParamsInt64(":boardID") == 0 { + board = &project_model.Board{ + ID: 0, + ProjectID: project.ID, + Title: ctx.Tr("repo.projects.type.uncategorized"), + } + } else { + board, err = project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID")) + if err != nil { + if project_model.IsErrProjectBoardNotExist(err) { + ctx.NotFound("ProjectBoardNotExist", nil) + } else { + ctx.ServerError("GetProjectBoard", err) + } + return + } + if board.ProjectID != project.ID { + ctx.NotFound("BoardNotInProject", nil) + return + } + } + + type movedIssuesForm struct { + Issues []struct { + IssueID int64 `json:"issueID"` + Sorting int64 `json:"sorting"` + } `json:"issues"` + } + + form := &movedIssuesForm{} + if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil { + ctx.ServerError("DecodeMovedIssuesForm", err) + } + + issueIDs := make([]int64, 0, len(form.Issues)) + sortedIssueIDs := make(map[int64]int64) + for _, issue := range form.Issues { + issueIDs = append(issueIDs, issue.IssueID) + sortedIssueIDs[issue.Sorting] = issue.IssueID + } + movedIssues, err := issues_model.GetIssuesByIDs(ctx, issueIDs) + if err != nil { + if issues_model.IsErrIssueNotExist(err) { + ctx.NotFound("IssueNotExisting", nil) + } else { + ctx.ServerError("GetIssueByID", err) + } + return + } + + if len(movedIssues) != len(form.Issues) { + ctx.ServerError("some issues do not exist", errors.New("some issues do not exist")) + return + } + + if _, err = movedIssues.LoadRepositories(ctx); err != nil { + ctx.ServerError("LoadRepositories", err) + return + } + + for _, issue := range movedIssues { + if issue.RepoID != project.RepoID && issue.Repo.OwnerID != project.OwnerID { + ctx.ServerError("Some issue's repoID is not equal to project's repoID", errors.New("Some issue's repoID is not equal to project's repoID")) + return + } + } + + if err = project_model.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil { + ctx.ServerError("MoveIssuesOnProjectBoard", err) + return + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ok": true, + }) +} diff --git a/routers/web/org/projects_test.go b/routers/web/org/projects_test.go new file mode 100644 index 0000000000..3450fa8e72 --- /dev/null +++ b/routers/web/org/projects_test.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package org_test + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/routers/web/org" + + "github.com/stretchr/testify/assert" +) + +func TestCheckProjectBoardChangePermissions(t *testing.T) { + unittest.PrepareTestEnv(t) + ctx := test.MockContext(t, "user2/-/projects/4/4") + test.LoadUser(t, ctx, 2) + ctx.ContextUser = ctx.Doer // user2 + ctx.SetParams(":id", "4") + ctx.SetParams(":boardID", "4") + + project, board := org.CheckProjectBoardChangePermissions(ctx) + assert.NotNil(t, project) + assert.NotNil(t, board) + assert.False(t, ctx.Written()) +} diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index f3b72da3b8..ce48c2e438 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -363,7 +363,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti } if ctx.Repo.CanWriteIssuesOrPulls(ctx.Params(":type") == "pulls") { - projects, _, err := project_model.GetProjects(ctx, project_model.SearchOptions{ + projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ RepoID: repo.ID, Type: project_model.TypeRepository, IsClosed: util.OptionalBoolOf(isShowClosed), @@ -474,8 +474,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { var err error - - ctx.Data["OpenProjects"], _, err = project_model.GetProjects(ctx, project_model.SearchOptions{ + projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ RepoID: repo.ID, Page: -1, IsClosed: util.OptionalBoolFalse, @@ -485,8 +484,20 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } + projects2, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeOrganization, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } - ctx.Data["ClosedProjects"], _, err = project_model.GetProjects(ctx, project_model.SearchOptions{ + ctx.Data["OpenProjects"] = append(projects, projects2...) + + projects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ RepoID: repo.ID, Page: -1, IsClosed: util.OptionalBoolTrue, @@ -496,6 +507,18 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } + projects2, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolTrue, + Type: project_model.TypeOrganization, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + + ctx.Data["ClosedProjects"] = append(projects, projects2...) } // repoReviewerSelection items to bee shown @@ -784,7 +807,8 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles } } - if !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/ + + if template.Ref != "" && !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/ template.Ref = git.BranchPrefix + template.Ref } ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0 @@ -987,7 +1011,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull ctx.ServerError("GetProjectByID", err) return nil, nil, 0, 0 } - if p.RepoID != ctx.Repo.Repository.ID { + if p.RepoID != ctx.Repo.Repository.ID && p.OwnerID != ctx.Repo.Repository.OwnerID { ctx.NotFound("", nil) return nil, nil, 0, 0 } @@ -2575,7 +2599,7 @@ func UpdateIssueStatus(ctx *context.Context) { } for _, issue := range issues { if issue.IsClosed != isClosed { - if err := issue_service.ChangeStatus(issue, ctx.Doer, isClosed); err != nil { + if err := issue_service.ChangeStatus(issue, ctx.Doer, "", isClosed); err != nil { if issues_model.IsErrDependenciesLeft(err) { ctx.JSON(http.StatusPreconditionFailed, map[string]interface{}{ "error": "cannot close this issue because it still has open dependencies", @@ -2672,7 +2696,7 @@ func NewComment(ctx *context.Context) { ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index)) } else { isClosed := form.Status == "close" - if err := issue_service.ChangeStatus(issue, ctx.Doer, isClosed); err != nil { + if err := issue_service.ChangeStatus(issue, ctx.Doer, "", isClosed); err != nil { log.Error("ChangeStatus: %v", err) if issues_model.IsErrDependenciesLeft(err) { diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 75cd290b8f..3becf799c5 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -70,7 +70,7 @@ func Projects(ctx *context.Context) { total = repo.NumClosedProjects } - projects, count, err := project_model.GetProjects(ctx, project_model.SearchOptions{ + projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ RepoID: repo.ID, Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), @@ -112,7 +112,7 @@ func Projects(ctx *context.Context) { pager.AddParam(ctx, "state", "State") ctx.Data["Page"] = pager - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) + ctx.Data["CanWriteProjects"] = true ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType @@ -653,47 +653,3 @@ func MoveIssues(ctx *context.Context) { "ok": true, }) } - -// CreateProject renders the generic project creation page -func CreateProject(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.projects.new") - ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig() - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) - - ctx.HTML(http.StatusOK, tplGenericProjectsNew) -} - -// CreateProjectPost creates an individual and/or organization project -func CreateProjectPost(ctx *context.Context, form forms.UserCreateProjectForm) { - user := checkContextUser(ctx, form.UID) - if ctx.Written() { - return - } - - ctx.Data["ContextUser"] = user - - if ctx.HasError() { - ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) - ctx.HTML(http.StatusOK, tplGenericProjectsNew) - return - } - - projectType := project_model.TypeIndividual - if user.IsOrganization() { - projectType = project_model.TypeOrganization - } - - if err := project_model.NewProject(&project_model.Project{ - Title: form.Title, - Description: form.Content, - CreatorID: user.ID, - BoardType: form.BoardType, - Type: projectType, - }); err != nil { - ctx.ServerError("NewProject", err) - return - } - - ctx.Flash.Success(ctx.Tr("repo.projects.create_success", form.Title)) - ctx.Redirect(setting.AppSubURL + "/") -} diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go new file mode 100644 index 0000000000..94e59e2a49 --- /dev/null +++ b/routers/web/shared/user/header.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package user + +import ( + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" +) + +func RenderUserHeader(ctx *context.Context) { + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.Data["ContextUser"] = ctx.ContextUser +} diff --git a/routers/web/user/package.go b/routers/web/user/package.go index c0aba7583f..ed4f0dd797 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + shared_user "code.gitea.io/gitea/routers/web/shared/user" "code.gitea.io/gitea/services/forms" packages_service "code.gitea.io/gitea/services/packages" ) @@ -83,10 +84,10 @@ func ListPackages(ctx *context.Context) { return } + shared_user.RenderUserHeader(ctx) + ctx.Data["Title"] = ctx.Tr("packages.title") ctx.Data["IsPackagesPage"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["Query"] = query ctx.Data["PackageType"] = packageType ctx.Data["AvailableTypes"] = packages_model.TypeList @@ -156,10 +157,10 @@ func RedirectToLastVersion(ctx *context.Context) { func ViewPackageVersion(ctx *context.Context) { pd := ctx.Package.Descriptor + shared_user.RenderUserHeader(ctx) + ctx.Data["Title"] = pd.Package.Name ctx.Data["IsPackagesPage"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["PackageDescriptor"] = pd var ( @@ -235,10 +236,10 @@ func ListPackageVersions(ctx *context.Context) { query := ctx.FormTrim("q") sort := ctx.FormTrim("sort") + shared_user.RenderUserHeader(ctx) + ctx.Data["Title"] = ctx.Tr("packages.title") ctx.Data["IsPackagesPage"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{ Package: p, Owner: ctx.Package.Owner, @@ -311,10 +312,10 @@ func ListPackageVersions(ctx *context.Context) { func PackageSettings(ctx *context.Context) { pd := ctx.Package.Descriptor + shared_user.RenderUserHeader(ctx) + ctx.Data["Title"] = pd.Package.Name ctx.Data["IsPackagesPage"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["PackageDescriptor"] = pd repos, _, _ := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{ diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 732d45bdc3..f2a6f5b504 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -225,7 +225,7 @@ func Profile(ctx *context.Context) { total = int(count) case "projects": - ctx.Data["OpenProjects"], _, err = project_model.GetProjects(ctx, project_model.SearchOptions{ + ctx.Data["OpenProjects"], _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ Page: -1, IsClosed: util.OptionalBoolFalse, Type: project_model.TypeIndividual, diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index 23c215738d..b66806ff2d 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -42,9 +42,15 @@ func ApplicationsPost(ctx *context.Context) { return } + scope, err := form.GetScope() + if err != nil { + ctx.ServerError("GetScope", err) + return + } t := &auth_model.AccessToken{ - UID: ctx.Doer.ID, - Name: form.Name, + UID: ctx.Doer.ID, + Name: form.Name, + Scope: scope, } exist, err := auth_model.AccessTokenByNameExists(t) diff --git a/routers/web/web.go b/routers/web/web.go index f0fedd0715..d37d82820d 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -835,6 +835,46 @@ func RegisterRoutes(m *web.Route) { }) }, ignSignIn, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) } + + m.Group("/projects", func() { + m.Get("", org.Projects) + m.Get("/{id}", org.ViewProject) + m.Group("", func() { //nolint:dupl + m.Get("/new", org.NewProject) + m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost) + m.Group("/{id}", func() { + m.Post("", web.Bind(forms.EditProjectBoardForm{}), org.AddBoardToProjectPost) + m.Post("/delete", org.DeleteProject) + + m.Get("/edit", org.EditProject) + m.Post("/edit", web.Bind(forms.CreateProjectForm{}), org.EditProjectPost) + m.Post("/{action:open|close}", org.ChangeProjectStatus) + + m.Group("/{boardID}", func() { + m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard) + m.Delete("", org.DeleteProjectBoard) + m.Post("/default", org.SetDefaultProjectBoard) + + m.Post("/move", org.MoveIssues) + }) + }) + }, reqSignIn, func(ctx *context.Context) { + if ctx.ContextUser == nil { + ctx.NotFound("NewProject", nil) + return + } + if ctx.ContextUser.IsOrganization() { + if !ctx.Org.CanWriteUnit(ctx, unit.TypeProjects) { + ctx.NotFound("NewProject", nil) + return + } + } else if ctx.ContextUser.ID != ctx.Doer.ID { + ctx.NotFound("NewProject", nil) + return + } + }) + }, repo.MustEnableProjects) + m.Get("/code", user.CodeSearch) }, context_service.UserAssignmentWeb()) @@ -1168,7 +1208,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/projects", func() { m.Get("", repo.Projects) m.Get("/{id}", repo.ViewProject) - m.Group("", func() { + m.Group("", func() { //nolint:dupl m.Get("/new", repo.NewProject) m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost) m.Group("/{id}", func() { diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index c0a8250e95..1be78b85c5 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -59,6 +59,8 @@ func (o *OAuth2) Name() string { } // userIDFromToken returns the user id corresponding to the OAuth token. +// It will set 'IsApiToken' to true if the token is an API token and +// set 'ApiTokenScope' to the scope of the access token func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { _ = req.ParseForm() @@ -86,6 +88,7 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { uid := CheckOAuthAccessToken(tokenSHA) if uid != 0 { store.GetData()["IsApiToken"] = true + store.GetData()["ApiTokenScope"] = auth_model.AccessTokenScopeAll // fallback to all } return uid } @@ -101,6 +104,7 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { log.Error("UpdateAccessToken: %v", err) } store.GetData()["IsApiToken"] = true + store.GetData()["ApiTokenScope"] = t.Scope return t.UID } diff --git a/services/context/user.go b/services/context/user.go index 9dc84c3ac1..7642cba4e1 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" + org_model "code.gitea.io/gitea/models/organization" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" ) @@ -56,6 +57,14 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) } else { errCb(http.StatusInternalServerError, "GetUserByName", err) } + } else { + if ctx.ContextUser.IsOrganization() { + if ctx.Org == nil { + ctx.Org = &context.Organization{} + } + ctx.Org.Organization = (*org_model.Organization)(ctx.ContextUser) + ctx.Data["Org"] = ctx.Org.Organization + } } } } diff --git a/services/convert/pull.go b/services/convert/pull.go index db0add6cde..cdf72e7805 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -88,6 +88,10 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u }, } + if pr.Issue.ClosedUnix != 0 { + apiPullRequest.Closed = pr.Issue.ClosedUnix.AsTimePtr() + } + gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath()) if err != nil { log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err) diff --git a/services/forms/user_form.go b/services/forms/user_form.go index bbea58310a..285bc398b2 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -9,6 +9,7 @@ import ( "net/http" "strings" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" @@ -377,7 +378,8 @@ func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding // NewAccessTokenForm form for creating access token type NewAccessTokenForm struct { - Name string `binding:"Required;MaxSize(255)"` + Name string `binding:"Required;MaxSize(255)"` + Scope []string } // Validate validates the fields @@ -386,6 +388,12 @@ func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) bi return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } +func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) { + scope := strings.Join(f.Scope, ",") + s, err := auth_model.AccessTokenScope(scope).Normalize() + return s, err +} + // EditOAuth2ApplicationForm form for editing oauth2 applications type EditOAuth2ApplicationForm struct { Name string `binding:"Required;MaxSize(255)" form:"application_name"` diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go index 463b39d0bf..225686f0fe 100644 --- a/services/forms/user_form_test.go +++ b/services/forms/user_form_test.go @@ -4,8 +4,10 @@ package forms import ( + "strconv" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -83,3 +85,28 @@ func TestRegisterForm_IsDomainAllowed_BlocklistedEmail(t *testing.T) { assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) } } + +func TestNewAccessTokenForm_GetScope(t *testing.T) { + tests := []struct { + form NewAccessTokenForm + scope auth_model.AccessTokenScope + expectedErr error + }{ + { + form: NewAccessTokenForm{Name: "test", Scope: []string{"repo"}}, + scope: "repo", + }, + { + form: NewAccessTokenForm{Name: "test", Scope: []string{"repo", "user"}}, + scope: "repo,user", + }, + } + + for i, test := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + scope, err := test.form.GetScope() + assert.Equal(t, test.expectedErr, err) + assert.Equal(t, test.scope, scope) + }) + } +} diff --git a/services/issue/commit.go b/services/issue/commit.go index db31fc66bb..7a8c71e609 100644 --- a/services/issue/commit.go +++ b/services/issue/commit.go @@ -18,6 +18,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/repository" @@ -175,7 +176,8 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm if !repo.CloseIssuesViaCommitInAnyBranch { // If the issue was specified to be in a particular branch, don't allow commits in other branches to close it if refIssue.Ref != "" { - if branchName != refIssue.Ref { + issueBranchName := strings.TrimPrefix(refIssue.Ref, git.BranchPrefix) + if branchName != issueBranchName { continue } // Otherwise, only process commits to the default branch @@ -191,7 +193,7 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm } if close != refIssue.IsClosed { refIssue.Repo = refRepo - if err := ChangeStatus(refIssue, doer, close); err != nil { + if err := ChangeStatus(refIssue, doer, c.Sha1, close); err != nil { return err } } diff --git a/services/issue/status.go b/services/issue/status.go index 782ce0bd96..d4a0fce3e5 100644 --- a/services/issue/status.go +++ b/services/issue/status.go @@ -14,13 +14,13 @@ import ( ) // ChangeStatus changes issue status to open or closed. -func ChangeStatus(issue *issues_model.Issue, doer *user_model.User, closed bool) error { - return changeStatusCtx(db.DefaultContext, issue, doer, closed) +func ChangeStatus(issue *issues_model.Issue, doer *user_model.User, commitID string, closed bool) error { + return changeStatusCtx(db.DefaultContext, issue, doer, commitID, closed) } // changeStatusCtx changes issue status to open or closed. // TODO: if context is not db.DefaultContext we get a deadlock!!! -func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, closed bool) error { +func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string, closed bool) error { comment, err := issues_model.ChangeIssueStatus(ctx, issue, doer, closed) if err != nil { if issues_model.IsErrDependenciesLeft(err) && closed { @@ -37,7 +37,7 @@ func changeStatusCtx(ctx context.Context, issue *issues_model.Issue, doer *user_ } } - notification.NotifyIssueChangeStatus(ctx, doer, issue, comment, closed) + notification.NotifyIssueChangeStatus(ctx, doer, commitID, issue, comment, closed) return nil } diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 7c7ad54714..351b79b5df 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -61,7 +61,7 @@ func SendTestMail(email string) error { // No mail service configured return nil } - return gomail.Send(Sender, NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").ToMessage()) + return gomail.Send(Sender, NewMessage(email, "Gitea Test Email!", "Gitea Test Email!").ToMessage()) } // sendUserMail sends a mail to the user @@ -86,7 +86,7 @@ func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, s return } - msg := NewMessage([]string{u.Email}, subject, content.String()) + msg := NewMessage(u.Email, subject, content.String()) msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info) SendAsync(msg) @@ -137,7 +137,7 @@ func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) { return } - msg := NewMessage([]string{email.Email}, locale.Tr("mail.activate_email"), content.String()) + msg := NewMessage(email.Email, locale.Tr("mail.activate_email"), content.String()) msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID) SendAsync(msg) @@ -168,7 +168,7 @@ func SendRegisterNotifyMail(u *user_model.User) { return } - msg := NewMessage([]string{u.Email}, locale.Tr("mail.register_notify"), content.String()) + msg := NewMessage(u.Email, locale.Tr("mail.register_notify"), content.String()) msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID) SendAsync(msg) @@ -202,7 +202,7 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) return } - msg := NewMessage([]string{u.Email}, subject, content.String()) + msg := NewMessage(u.Email, subject, content.String()) msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID) SendAsync(msg) @@ -322,7 +322,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient msgs := make([]*Message, 0, len(recipients)) for _, recipient := range recipients { - msg := NewMessageFrom([]string{recipient.Email}, ctx.Doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String()) + msg := NewMessageFrom(recipient.Email, ctx.Doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String()) msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info) msg.SetHeader("Message-ID", msgID) diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index 96227270c8..ebf9285b0a 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -89,7 +89,7 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo publisherName := rel.Publisher.DisplayName() relURL := "<" + rel.HTMLURL() + ">" for _, to := range tos { - msg := NewMessageFrom([]string{to}, publisherName, setting.MailService.FromEmail, subject, mailBody.String()) + msg := NewMessageFrom(to, publisherName, setting.MailService.FromEmail, subject, mailBody.String()) msg.Info = subject msg.SetHeader("Message-ID", relURL) msgs = append(msgs, msg) diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index 5fa13f5044..9b2f24faa8 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -82,9 +82,12 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U return err } - msg := NewMessage(emails, subject, content.String()) - msg.Info = fmt.Sprintf("UID: %d, repository pending transfer notification", newOwner.ID) + for _, to := range emails { + msg := NewMessage(to, subject, content.String()) + msg.Info = fmt.Sprintf("UID: %d, repository pending transfer notification", newOwner.ID) + + SendAsync(msg) + } - SendAsync(msg) return nil } diff --git a/services/mailer/mail_team_invite.go b/services/mailer/mail_team_invite.go index 54e82b0234..917e184435 100644 --- a/services/mailer/mail_team_invite.go +++ b/services/mailer/mail_team_invite.go @@ -52,7 +52,7 @@ func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_mod return err } - msg := NewMessage([]string{invite.Email}, subject, mailBody.String()) + msg := NewMessage(invite.Email, subject, mailBody.String()) msg.Info = subject SendAsync(msg) diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go index 4e03afb961..91cc8cb405 100644 --- a/services/mailer/mailer.go +++ b/services/mailer/mailer.go @@ -35,7 +35,7 @@ type Message struct { Info string // Message information for log purpose. FromAddress string FromDisplayName string - To []string + To string // Use only one recipient to prevent leaking of addresses ReplyTo string Subject string Date time.Time @@ -47,7 +47,7 @@ type Message struct { func (m *Message) ToMessage() *gomail.Message { msg := gomail.NewMessage() msg.SetAddressHeader("From", m.FromAddress, m.FromDisplayName) - msg.SetHeader("To", m.To...) + msg.SetHeader("To", m.To) if m.ReplyTo != "" { msg.SetHeader("Reply-To", m.ReplyTo) } @@ -89,7 +89,7 @@ func (m *Message) generateAutoMessageID() string { dateMs := m.Date.UnixNano() / 1e6 h := fnv.New64() if len(m.To) > 0 { - _, _ = h.Write([]byte(m.To[0])) + _, _ = h.Write([]byte(m.To)) } _, _ = h.Write([]byte(m.Subject)) _, _ = h.Write([]byte(m.Body)) @@ -97,7 +97,7 @@ func (m *Message) generateAutoMessageID() string { } // NewMessageFrom creates new mail message object with custom From header. -func NewMessageFrom(to []string, fromDisplayName, fromAddress, subject, body string) *Message { +func NewMessageFrom(to, fromDisplayName, fromAddress, subject, body string) *Message { log.Trace("NewMessageFrom (body):\n%s", body) return &Message{ @@ -112,7 +112,7 @@ func NewMessageFrom(to []string, fromDisplayName, fromAddress, subject, body str } // NewMessage creates new mail message object with default From header. -func NewMessage(to []string, subject, body string) *Message { +func NewMessage(to, subject, body string) *Message { return NewMessageFrom(to, setting.MailService.FromName, setting.MailService.FromEmail, subject, body) } diff --git a/services/mailer/mailer_test.go b/services/mailer/mailer_test.go index 79c5231218..375ca35daa 100644 --- a/services/mailer/mailer_test.go +++ b/services/mailer/mailer_test.go @@ -21,17 +21,17 @@ func TestGenerateMessageID(t *testing.T) { setting.Domain = "localhost" date := time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC) - m := NewMessageFrom(nil, "display-name", "from-address", "subject", "body") + m := NewMessageFrom("", "display-name", "from-address", "subject", "body") m.Date = date gm := m.ToMessage() assert.Equal(t, "", gm.GetHeader("Message-ID")[0]) - m = NewMessageFrom([]string{"a@b.com"}, "display-name", "from-address", "subject", "body") + m = NewMessageFrom("a@b.com", "display-name", "from-address", "subject", "body") m.Date = date gm = m.ToMessage() assert.Equal(t, "", gm.GetHeader("Message-ID")[0]) - m = NewMessageFrom([]string{"a@b.com"}, "display-name", "from-address", "subject", "body") + m = NewMessageFrom("a@b.com", "display-name", "from-address", "subject", "body") m.SetHeader("Message-ID", "") gm = m.ToMessage() assert.Equal(t, "", gm.GetHeader("Message-ID")[0]) diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go index 65c138ed04..cc3d4fc936 100644 --- a/services/migrations/gitbucket.go +++ b/services/migrations/gitbucket.go @@ -33,10 +33,14 @@ func (f *GitBucketDownloaderFactory) New(ctx context.Context, opts base.MigrateO return nil, err } - baseURL := u.Scheme + "://" + u.Host fields := strings.Split(u.Path, "/") - oldOwner := fields[1] - oldName := strings.TrimSuffix(fields[2], ".git") + if len(fields) < 2 { + return nil, fmt.Errorf("invalid path: %s", u.Path) + } + baseURL := u.Scheme + "://" + u.Host + strings.TrimSuffix(strings.Join(fields[:len(fields)-2], "/"), "/git") + + oldOwner := fields[len(fields)-2] + oldName := strings.TrimSuffix(fields[len(fields)-1], ".git") log.Trace("Create GitBucket downloader. BaseURL: %s RepoOwner: %s RepoName: %s", baseURL, oldOwner, oldName) return NewGitBucketDownloader(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil @@ -71,6 +75,7 @@ func (g *GitBucketDownloader) ColorFormat(s fmt.State) { func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader { githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName) githubDownloader.SkipReactions = true + githubDownloader.SkipReviews = true return &GitBucketDownloader{ githubDownloader, } diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 23aa4ac2ca..20370d99f9 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -454,15 +454,36 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { if comment.Updated.IsZero() { comment.Updated = comment.Created } - + if comment.CommentType == "" { + // if type field is missing, then assume a normal comment + comment.CommentType = issues_model.CommentTypeComment.String() + } cm := issues_model.Comment{ IssueID: issue.ID, - Type: issues_model.CommentTypeComment, + Type: issues_model.AsCommentType(comment.CommentType), Content: comment.Content, CreatedUnix: timeutil.TimeStamp(comment.Created.Unix()), UpdatedUnix: timeutil.TimeStamp(comment.Updated.Unix()), } + switch cm.Type { + case issues_model.CommentTypeAssignees: + if assigneeID, ok := comment.Meta["AssigneeID"].(int); ok { + cm.AssigneeID = int64(assigneeID) + } + if comment.Meta["RemovedAssigneeID"] != nil { + cm.RemovedAssignee = true + } + case issues_model.CommentTypeChangeTitle: + if comment.Meta["OldTitle"] != nil { + cm.OldTitle = fmt.Sprintf("%s", comment.Meta["OldTitle"]) + } + if comment.Meta["NewTitle"] != nil { + cm.NewTitle = fmt.Sprintf("%s", comment.Meta["NewTitle"]) + } + default: + } + if err := g.remapUser(comment, &cm); err != nil { return err } diff --git a/services/migrations/github.go b/services/migrations/github.go index 48dd90323d..d34ad13b95 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -76,6 +76,7 @@ type GithubDownloaderV3 struct { curClientIdx int maxPerPage int SkipReactions bool + SkipReviews bool } // NewGithubDownloaderV3 creates a github Downloader via github v3 API @@ -809,6 +810,9 @@ func (g *GithubDownloaderV3) convertGithubReviewComments(cs []*github.PullReques // GetReviews returns pull requests review func (g *GithubDownloaderV3) GetReviews(reviewable base.Reviewable) ([]*base.Review, error) { allReviews := make([]*base.Review, 0, g.maxPerPage) + if g.SkipReviews { + return allReviews, nil + } opt := &github.ListOptions{ PerPage: g.maxPerPage, } diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go index a3b9d1cfa8..0ebb3411fd 100644 --- a/services/migrations/migrate.go +++ b/services/migrations/migrate.go @@ -281,7 +281,7 @@ func migrateRepository(doer *user_model.User, downloader base.Downloader, upload lbBatchSize = len(labels) } - if err := uploader.CreateLabels(labels...); err != nil { + if err := uploader.CreateLabels(labels[:lbBatchSize]...); err != nil { return err } labels = labels[lbBatchSize:] diff --git a/services/packages/packages.go b/services/packages/packages.go index 49f5a2fac4..410e73c048 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -361,11 +361,11 @@ func checkSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, p } if setting.Packages.LimitTotalOwnerSize > -1 { - totalSize, err := packages_model.CalculateBlobSize(ctx, &packages_model.PackageFileSearchOptions{ + totalSize, err := packages_model.CalculateFileSize(ctx, &packages_model.PackageFileSearchOptions{ OwnerID: owner.ID, }) if err != nil { - log.Error("CalculateBlobSize failed: %v", err) + log.Error("CalculateFileSize failed: %v", err) return err } if totalSize+uploadSize > setting.Packages.LimitTotalOwnerSize { diff --git a/services/pull/merge.go b/services/pull/merge.go index d0ec943cfa..7ffbdb78b0 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -225,7 +225,7 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U } close := ref.RefAction == references.XRefActionCloses if close != ref.Issue.IsClosed { - if err = issue_service.ChangeStatus(ref.Issue, doer, close); err != nil { + if err = issue_service.ChangeStatus(ref.Issue, doer, pr.MergedCommitID, close); err != nil { // Allow ErrDependenciesLeft if !issues_model.IsErrDependenciesLeft(err) { return err @@ -595,19 +595,25 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode headUser = pr.HeadRepo.Owner } - env = repo_module.FullPushingEnvironment( - headUser, - doer, - pr.BaseRepo, - pr.BaseRepo.Name, - pr.ID, - ) - var pushCmd *git.Command if mergeStyle == repo_model.MergeStyleRebaseUpdate { // force push the rebase result to head branch + env = repo_module.FullPushingEnvironment( + headUser, + doer, + pr.HeadRepo, + pr.HeadRepo.Name, + pr.ID, + ) pushCmd = git.NewCommand(ctx, "push", "-f", "head_repo").AddDynamicArguments(stagingBranch + ":" + git.BranchPrefix + pr.HeadBranch) } else { + env = repo_module.FullPushingEnvironment( + headUser, + doer, + pr.BaseRepo, + pr.BaseRepo.Name, + pr.ID, + ) pushCmd = git.NewCommand(ctx, "push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch) } diff --git a/services/pull/pull.go b/services/pull/pull.go index afb0fa2442..7f81def6d6 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -240,7 +240,7 @@ func checkForInvalidation(ctx context.Context, requests issues_model.PullRequest } go func() { // FIXME: graceful: We need to tell the manager we're doing something... - err := requests.InvalidateCodeComments(ctx, doer, gitRepo, branch) + err := InvalidateCodeComments(ctx, requests, doer, gitRepo, branch) if err != nil { log.Error("PullRequestList.InvalidateCodeComments: %v", err) } @@ -532,7 +532,7 @@ func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error var errs errlist for _, pr := range prs { - if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !issues_model.IsErrPullWasClosed(err) && !issues_model.IsErrDependenciesLeft(err) { + if err = issue_service.ChangeStatus(pr.Issue, doer, "", true); err != nil && !issues_model.IsErrPullWasClosed(err) && !issues_model.IsErrDependenciesLeft(err) { errs = append(errs, err) } } @@ -566,7 +566,7 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re if pr.BaseRepoID == repo.ID { continue } - if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !issues_model.IsErrPullWasClosed(err) { + if err = issue_service.ChangeStatus(pr.Issue, doer, "", true); err != nil && !issues_model.IsErrPullWasClosed(err) { errs = append(errs, err) } } diff --git a/services/pull/review.go b/services/pull/review.go index 67a10d7aad..ca386ca6b0 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -23,6 +23,53 @@ import ( issue_service "code.gitea.io/gitea/services/issue" ) +var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`) + +// checkInvalidation checks if the line of code comment got changed by another commit. +// If the line got changed the comment is going to be invalidated. +func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error { + // FIXME differentiate between previous and proposed line + commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine())) + if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { + c.Invalidated = true + return issues_model.UpdateCommentInvalidate(ctx, c) + } + if err != nil { + return err + } + if c.CommitSHA != "" && c.CommitSHA != commit.ID.String() { + c.Invalidated = true + return issues_model.UpdateCommentInvalidate(ctx, c) + } + return nil +} + +// InvalidateCodeComments will lookup the prs for code comments which got invalidated by change +func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestList, doer *user_model.User, repo *git.Repository, branch string) error { + if len(prs) == 0 { + return nil + } + issueIDs := prs.GetIssueIDs() + var codeComments []*issues_model.Comment + + if err := db.Find(ctx, &issues_model.FindCommentsOptions{ + ListOptions: db.ListOptions{ + ListAll: true, + }, + Type: issues_model.CommentTypeCode, + Invalidated: util.OptionalBoolFalse, + IssueIDs: issueIDs, + }, &codeComments); err != nil { + return fmt.Errorf("find code comments: %v", err) + } + for _, comment := range codeComments { + if err := checkInvalidation(ctx, comment, doer, repo, branch); err != nil { + return err + } + } + return nil +} + // CreateCodeComment creates a comment on the code line func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, line int64, content, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*issues_model.Comment, error) { var ( @@ -114,8 +161,6 @@ func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git. return comment, nil } -var notEnoughLines = regexp.MustCompile(`exit status 128 - fatal: file .* has only \d+ lines?`) - // createCodeComment creates a plain code comment at the specified line / path func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, treePath string, line, reviewID int64) (*issues_model.Comment, error) { var commitID, patch string diff --git a/services/pull/update.go b/services/pull/update.go index 9e29f63c7c..ede89bcdff 100644 --- a/services/pull/update.go +++ b/services/pull/update.go @@ -118,6 +118,9 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, } prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) if err != nil { + if repo_model.IsErrUnitTypeNotExist(err) { + return false, false, nil + } log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err) return false, false, err } diff --git a/services/repository/push.go b/services/repository/push.go index dc8d564cb4..0135243388 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -103,6 +103,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { var pusher *user_model.User for _, opts := range optsList { + log.Trace("pushUpdates: %-v %s %s %s", repo, opts.OldCommitID, opts.NewCommitID, opts.RefFullName) + if opts.IsNewRef() && opts.IsDelRef() { return fmt.Errorf("old and new revisions are both %s", git.EmptySHA) } @@ -128,7 +130,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } else { // is new tag newCommit, err := gitRepo.GetCommit(opts.NewCommitID) if err != nil { - return fmt.Errorf("gitRepo.GetCommit: %w", err) + return fmt.Errorf("gitRepo.GetCommit(%s) in %s/%s[%d]: %w", opts.NewCommitID, repo.OwnerName, repo.Name, repo.ID, err) } commits := repo_module.NewPushCommits() @@ -161,7 +163,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { newCommit, err := gitRepo.GetCommit(opts.NewCommitID) if err != nil { - return fmt.Errorf("gitRepo.GetCommit: %w", err) + return fmt.Errorf("gitRepo.GetCommit(%s) in %s/%s[%d]: %w", opts.NewCommitID, repo.OwnerName, repo.Name, repo.ID, err) } refName := opts.RefName() diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index ee80766032..16d2b95812 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -229,7 +229,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(ctx context.Context, doer *user } } -func (m *webhookNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { +func (m *webhookNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { mode, _ := access_model.AccessLevel(ctx, issue.Poster, issue.Repo) var err error if issue.IsPull { @@ -243,6 +243,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *use PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), + CommitID: commitID, } if isClosed { apiPullRequest.Action = api.HookIssueClosed @@ -256,6 +257,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(ctx context.Context, doer *use Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, mode), Sender: convert.ToUser(doer, nil), + CommitID: commitID, } if isClosed { apiIssue.Action = api.HookIssueClosed diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index c39f5fc128..4c96d2bf10 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -4,7 +4,9 @@
{{template "base/alert" .}}

- {{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .Total}}, {{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}}) + {{.locale.Tr "admin.packages.package_manage_panel"}} ({{.locale.Tr "admin.total" .TotalCount}}, + {{.locale.Tr "admin.packages.total_size" (FileSize .TotalBlobSize)}}, + {{.locale.Tr "admin.packages.unreferenced_size" (FileSize .TotalUnreferencedBlobSize)}})

diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index 3fa0f8e7ac..4d3e08a597 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -22,7 +22,7 @@ {{end}} {{end}} - + {{template "custom/footer" .}} diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl index 582a0c8fb3..1102610e9e 100644 --- a/templates/org/header.tmpl +++ b/templates/org/header.tmpl @@ -6,8 +6,8 @@ {{avatar . 100}} {{.DisplayName}} - {{if .Visibility.IsLimited}}
{{$.locale.Tr "org.settings.visibility.limited_shortname"}}
{{end}} - {{if .Visibility.IsPrivate}}
{{$.locale.Tr "org.settings.visibility.private_shortname"}}
{{end}} + {{if .Visibility.IsLimited}}
{{$.locale.Tr "org.settings.visibility.limited_shortname"}}
{{end}} + {{if .Visibility.IsPrivate}}
{{$.locale.Tr "org.settings.visibility.private_shortname"}}
{{end}}
diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl index 87242b94d3..5f543424fc 100644 --- a/templates/org/menu.tmpl +++ b/templates/org/menu.tmpl @@ -3,6 +3,9 @@ {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} + + {{svg "octicon-project"}} {{.locale.Tr "user.projects"}} + {{if .IsPackageEnabled}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}} diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl new file mode 100644 index 0000000000..544ed38742 --- /dev/null +++ b/templates/org/projects/list.tmpl @@ -0,0 +1,6 @@ +{{template "base/head" .}} +
+ {{template "user/overview/header" .}} + {{template "projects/list" .}} +
+{{template "base/footer" .}} diff --git a/templates/org/projects/new.tmpl b/templates/org/projects/new.tmpl new file mode 100644 index 0000000000..b3d6c6001e --- /dev/null +++ b/templates/org/projects/new.tmpl @@ -0,0 +1,6 @@ +{{template "base/head" .}} +
+ {{template "user/overview/header" .}} + {{template "projects/new" .}} +
+{{template "base/footer" .}} diff --git a/templates/org/projects/view.tmpl b/templates/org/projects/view.tmpl new file mode 100644 index 0000000000..03327e2530 --- /dev/null +++ b/templates/org/projects/view.tmpl @@ -0,0 +1,6 @@ +{{template "base/head" .}} +
+ {{template "user/overview/header" .}} + {{template "projects/view" .}} +
+{{template "base/footer" .}} diff --git a/templates/package/content/pypi.tmpl b/templates/package/content/pypi.tmpl index 1cce31f537..1ae243813d 100644 --- a/templates/package/content/pypi.tmpl +++ b/templates/package/content/pypi.tmpl @@ -4,7 +4,7 @@
-
pip install --extra-index-url {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple {{.PackageDescriptor.Package.Name}}
+
pip install --index-url {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple {{.PackageDescriptor.Package.Name}}
diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl new file mode 100644 index 0000000000..ae2eaec6ea --- /dev/null +++ b/templates/projects/list.tmpl @@ -0,0 +1,98 @@ +
+
+ {{if .CanWriteProjects}} + +
+ {{end}} + + {{template "base/alert" .}} + + + +
+ {{range .Projects}} +
  • + {{svg "octicon-project"}} {{.Title}} +
    + {{$closedDate:= TimeSinceUnix .ClosedDateUnix $.locale}} + {{if .IsClosed}} + {{svg "octicon-clock"}} {{$.locale.Tr "repo.milestones.closed" $closedDate|Str2html}} + {{end}} + + {{svg "octicon-issue-opened" 16 "mr-3"}} + {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} + {{svg "octicon-check" 16 "mr-3"}} + {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} + +
    + {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}} + + {{end}} + {{if .Description}} +
    + {{.RenderedContent|Str2html}} +
    + {{end}} +
  • + {{end}} + + {{template "base/paginate" .}} +
    +
    +
    + +{{if or .CanWriteIssues .CanWritePulls}} + +{{end}} diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl new file mode 100644 index 0000000000..1069102792 --- /dev/null +++ b/templates/projects/new.tmpl @@ -0,0 +1,66 @@ +
    +
    + +
    +

    + {{if .PageIsEditProjects}} + {{.locale.Tr "repo.projects.edit"}} +
    {{.locale.Tr "repo.projects.edit_subheader"}}
    + {{else}} + {{.locale.Tr "repo.projects.new"}} +
    {{.locale.Tr "repo.projects.new_subheader"}}
    + {{end}} +

    + {{template "base/alert" .}} + + {{.CsrfTokenHtml}} +
    +
    + + +
    +
    + + +
    + + {{if not .PageIsEditProjects}} + + + {{end}} +
    +
    +
    +
    + {{if .PageIsEditProjects}} + + {{.locale.Tr "repo.milestones.cancel"}} + + + {{else}} + + {{end}} +
    +
    + + +
    +
    diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl new file mode 100644 index 0000000000..ac72acb82b --- /dev/null +++ b/templates/projects/view.tmpl @@ -0,0 +1,279 @@ +
    +
    +
    +
    +
    +
    + {{if .CanWriteProjects}} + {{.locale.Tr "new_project_board"}} + {{end}} + +
    +
    +
    + +
    +
    +
    + +
    + {{range $board := .Boards}} + +
    +
    +
    +
    + {{.NumIssues}} +
    + {{.Title}} +
    + {{if and $.CanWriteProjects (ne .ID 0)}} + + {{end}} +
    +
    + +
    + + {{range (index $.IssuesMap .ID)}} + + +
    +
    +
    + + {{if .IsPull}} + {{if .PullRequest.HasMerged}} + {{svg "octicon-git-merge" 16 "text purple"}} + {{else}} + {{if .IsClosed}} + {{svg "octicon-git-pull-request" 16 "text red"}} + {{else}} + {{svg "octicon-git-pull-request" 16 "text green"}} + {{end}} + {{end}} + {{else}} + {{if .IsClosed}} + {{svg "octicon-issue-closed" 16 "text red"}} + {{else}} + {{svg "octicon-issue-opened" 16 "text green"}} + {{end}} + {{end}} + + + {{.Title}} + +
    +
    + + {{.Repo.FullName}}#{{.Index}} + {{$timeStr := TimeSinceUnix .GetLastEventTimestamp $.locale}} + {{if .OriginalAuthor}} + {{$.locale.Tr .GetLastEventLabelFake $timeStr (.OriginalAuthor|Escape) | Safe}} + {{else if gt .Poster.ID 0}} + {{$.locale.Tr .GetLastEventLabel $timeStr (.Poster.HomeLink|Escape) (.Poster.GetDisplayName | Escape) | Safe}} + {{else}} + {{$.locale.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}} + {{end}} + +
    + {{- if .MilestoneID}} + + {{- end}} + {{- range index $.LinkedPRs .ID}} + + {{- end}} +
    + + {{if or .Labels .Assignees}} +
    + {{range .Labels}} + {{.Name | RenderEmoji}} + {{end}} +
    + {{range .Assignees}} + {{avatar . 28 "mini mr-3"}} + {{end}} +
    +
    + {{end}} +
    + + + {{end}} +
    +
    + {{end}} +
    + +
    + +
    + +{{if or .CanWriteIssues .CanWritePulls}} + +{{end}} diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 6272b19bc6..455bbf757e 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -9,10 +9,7 @@
    {{template "base/alert" .}} - - {{if not $.DisableMigrations}} -

    {{.locale.Tr "repo.new_repo_helper" ((printf "%s%s" AppSubUrl "/repo/migrate")|Escape) | Safe}}

    - {{end}} + {{template "repo/create_helper" .}} {{if not .CanCreateRepo}}
    diff --git a/templates/repo/create_helper.tmpl b/templates/repo/create_helper.tmpl new file mode 100644 index 0000000000..ec253e961d --- /dev/null +++ b/templates/repo/create_helper.tmpl @@ -0,0 +1,3 @@ +{{if not $.DisableMigrations}} +

    {{.locale.Tr "repo.new_repo_helper" ((printf "%s%s" AppSubUrl "/repo/migrate")|Escape) | Safe}}

    +{{end}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index d013c7b761..3012b09d58 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -29,7 +29,7 @@ {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} - + {{$.locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr | Safe}} {{if $.Repository.OriginalURL}} @@ -41,7 +41,7 @@ {{avatar .Poster}} {{end}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr | Safe}} @@ -95,7 +95,7 @@
    {{svg "octicon-dot-fill"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{if .Issue.IsPull}} {{$.locale.Tr "repo.pulls.reopened_at" .EventTag $createdStr | Safe}} @@ -108,7 +108,7 @@
    {{svg "octicon-circle-slash"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{if .Issue.IsPull}} {{$.locale.Tr "repo.pulls.closed_at" .EventTag $createdStr | Safe}} @@ -121,7 +121,7 @@
    {{svg "octicon-git-merge"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$link := printf "%s/commit/%s" $.Repository.HTMLURL ($.Issue.PullRequest.MergedCommitID|PathEscape)}} {{if eq $.Issue.PullRequest.Status 3}} @@ -156,20 +156,20 @@ {{if eq .RefAction 3}}{{end}}
    {{else if eq .Type 4}}
    {{svg "octicon-bookmark"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.commit_ref_at" .EventTag $createdStr | Safe}}
    {{svg "octicon-git-commit"}} - {{.Content | Str2html}} + {{.Content | Str2html}}
    {{else if eq .Type 7}} @@ -177,7 +177,7 @@
    {{svg "octicon-tag"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{if and .AddedLabels (not .RemovedLabels)}} {{$.locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels .AddedLabels $.RepoLink) $createdStr | Safe}} @@ -193,7 +193,7 @@
    {{svg "octicon-milestone"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.locale.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr | Safe}}{{else}}{{$.locale.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.locale.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr | Safe}}{{end}} @@ -204,7 +204,7 @@ {{if gt .AssigneeID 0}} {{if .RemovedAssignee}} {{template "shared/user/avatarlink" .Assignee}} - + {{template "shared/user/authorlink" .Assignee}} {{if eq .Poster.ID .Assignee.ID}} {{$.locale.Tr "repo.issues.remove_self_assignment" $createdStr | Safe}} @@ -214,7 +214,7 @@ {{else}} {{template "shared/user/avatarlink" .Assignee}} - + {{template "shared/user/authorlink" .Assignee}} {{if eq .Poster.ID .AssigneeID}} {{$.locale.Tr "repo.issues.self_assign_at" $createdStr | Safe}} @@ -229,7 +229,7 @@
    {{svg "octicon-pencil"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.change_title_at" (.OldTitle|RenderEmoji) (.NewTitle|RenderEmoji) $createdStr | Safe}} @@ -238,7 +238,7 @@
    {{svg "octicon-git-branch"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.delete_branch_at" (.OldRef|Escape) $createdStr | Safe}} @@ -247,7 +247,7 @@
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.start_tracking_history" $createdStr | Safe}} @@ -256,35 +256,35 @@
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.stop_tracking_history" $createdStr | Safe}} {{template "repo/issue/view_content/comments_delete_time" Dict "ctx" $ "comment" .}}
    {{svg "octicon-clock"}} - {{.Content}} + {{.Content}}
    {{else if eq .Type 14}}
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.add_time_history" $createdStr | Safe}} {{template "repo/issue/view_content/comments_delete_time" Dict "ctx" $ "comment" .}}
    {{svg "octicon-clock"}} - {{.Content}} + {{.Content}}
    {{else if eq .Type 15}}
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.cancel_tracking_history" $createdStr | Safe}} @@ -293,7 +293,7 @@
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.due_date_added" .Content $createdStr | Safe}} @@ -302,7 +302,7 @@
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$parsedDeadline := .Content | ParseDeadline}} {{$.locale.Tr "repo.issues.due_date_modified" (index $parsedDeadline 0) (index $parsedDeadline 1) $createdStr | Safe}} @@ -312,7 +312,7 @@
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.due_date_remove" .Content $createdStr | Safe}} @@ -321,15 +321,15 @@
    {{svg "octicon-package-dependents"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.dependency.added_dependency" $createdStr | Safe}} {{if .DependentIssue}}
    {{svg "octicon-plus"}} - - + + {{if eq .DependentIssue.RepoID .Issue.RepoID}} #{{.DependentIssue.Index}} {{.DependentIssue.Title}} {{else}} @@ -344,15 +344,15 @@
    {{svg "octicon-package-dependents"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.dependency.removed_dependency" $createdStr | Safe}} {{if .DependentIssue}}
    - {{svg "octicon-trash"}} - - + {{svg "octicon-trash"}} + + {{if eq .DependentIssue.RepoID .Issue.RepoID}} #{{.DependentIssue.Index}} {{.DependentIssue.Title}} {{else}} @@ -373,13 +373,13 @@ {{end}} {{svg (printf "octicon-%s" .Review.Type.Icon)}} - + {{if .OriginalAuthor}} {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} - {{if $.Repository.OriginalURL}} + {{if $.Repository.OriginalURL}} ({{$.locale.Tr "repo.migrated_from" ($.Repository.OriginalURL|Escape) ($.Repository.GetOriginalURLHostname|Escape) | Safe}}){{end}} {{else}} {{template "shared/user/authorlink" .Poster}} @@ -404,13 +404,13 @@
    - + {{if .OriginalAuthor}} {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} - {{if $.Repository.OriginalURL}} + {{if $.Repository.OriginalURL}} ({{$.locale.Tr "repo.migrated_from" ($.Repository.OriginalURL|Escape) ($.Repository.GetOriginalURLHostname|Escape) | Safe}}){{end}} {{else}} {{template "shared/user/authorlink" .Poster}} @@ -532,13 +532,13 @@ {{avatar .Poster}} {{end}} - + {{if .OriginalAuthor}} {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} {{.OriginalAuthor}} - {{if $.Repository.OriginalURL}} + {{if $.Repository.OriginalURL}} ({{$.locale.Tr "repo.migrated_from" ($.Repository.OriginalURL|Escape) ($.Repository.GetOriginalURLHostname|Escape) | Safe}}){{end}} {{else}} {{template "shared/user/authorlink" .Poster}} @@ -628,12 +628,12 @@ {{svg "octicon-lock"}} {{template "shared/user/avatarlink" .Poster}} {{if .Content}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.lock_with_reason" .Content $createdStr | Safe}} {{else}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.lock_no_reason" $createdStr | Safe}} @@ -643,7 +643,7 @@
    {{svg "octicon-key"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.unlock_comment" $createdStr | Safe}} @@ -652,7 +652,7 @@
    {{svg "octicon-git-branch"}} {{template "shared/user/avatarlink" .Poster}} - + {{.Poster.Name}} {{$.locale.Tr "repo.pulls.change_target_branch_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr | Safe}} @@ -661,21 +661,21 @@
    {{svg "octicon-clock"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{$.locale.Tr "repo.issues.del_time_history" $createdStr | Safe}}
    {{svg "octicon-clock"}} - {{.Content}} + {{.Content}}
    {{else if eq .Type 27}}
    {{svg "octicon-eye"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{if (gt .AssigneeID 0)}} {{if .RemovedAssignee}} @@ -699,7 +699,7 @@ {{else if and (eq .Type 29) (or (gt .CommitsNum 0) .IsForcePush)}}
    {{svg "octicon-repo-push"}} - + {{template "shared/user/authorlink" .Poster}} {{if .IsForcePush}} {{$.locale.Tr "repo.issues.force_push_codes" ($.Issue.PullRequest.HeadBranch|Escape) (ShortSha .OldCommit) (($.Issue.Repo.CommitLink .OldCommit)|Escape) (ShortSha .NewCommit) (($.Issue.Repo.CommitLink .NewCommit)|Escape) $createdStr | Safe}} @@ -716,7 +716,7 @@
    {{svg "octicon-project"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{if gt .OldProjectID 0}} {{if gt .ProjectID 0}} @@ -737,7 +737,7 @@ {{svg "octicon-x" 16}} - + {{template "shared/user/authorlink" .Poster}} {{$reviewerName := ""}} {{if eq .Review.OriginalAuthor ""}} @@ -752,7 +752,7 @@
    - + {{$.locale.Tr "action.review_dismissed_reason"}}
    @@ -773,7 +773,7 @@
    {{svg "octicon-git-branch"}} {{template "shared/user/avatarlink" .Poster}} - + {{template "shared/user/authorlink" .Poster}} {{if and .OldRef .NewRef}} {{$.locale.Tr "repo.issues.change_ref_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr | Safe}} @@ -787,7 +787,7 @@ {{else if or (eq .Type 34) (eq .Type 35)}}
    {{svg "octicon-git-merge" 16}} - + {{template "shared/user/authorlink" .Poster}} {{if eq .Type 34}}{{$.locale.Tr "repo.pulls.auto_merge_newly_scheduled_comment" $createdStr | Safe}} {{else}}{{$.locale.Tr "repo.pulls.auto_merge_canceled_schedule_comment" $createdStr | Safe}}{{end}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 6cb00fdd1d..ca947e3612 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -219,8 +219,8 @@ {{.locale.Tr "repo.issues.new.open_projects"}}
    {{range .OpenProjects}} - - {{svg "octicon-project" 18 "mr-3"}} + + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{.Title}} {{end}} @@ -231,8 +231,8 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}
    {{range .ClosedProjects}} - - {{svg "octicon-project" 18 "mr-3"}} + + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{.Title}} {{end}} @@ -243,8 +243,8 @@ {{.locale.Tr "repo.issues.new.no_projects"}}
    {{if .Issue.ProjectID}} - - {{svg "octicon-project" 18 "mr-3"}} + + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} {{.Issue.Project.Title}} {{end}} diff --git a/templates/repo/migrate/helper.tmpl b/templates/repo/migrate/helper.tmpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/templates/repo/migrate/migrate.tmpl b/templates/repo/migrate/migrate.tmpl index a38302a663..ff96e6793b 100644 --- a/templates/repo/migrate/migrate.tmpl +++ b/templates/repo/migrate/migrate.tmpl @@ -2,6 +2,7 @@
    + {{template "repo/migrate/helper" .}}
    {{range .Services}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index e8ad8406cd..f3aa2610bb 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -118,13 +118,13 @@ {{$rejectOfficial := call $approvalCounts .ID "reject"}} {{$waitingOfficial := call $approvalCounts .ID "waiting"}} {{if gt $approveOfficial 0}} - + {{svg "octicon-check" 14 "mr-1"}} {{$.locale.TrN $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n" $approveOfficial}} {{end}} {{if gt $rejectOfficial 0}} - + {{svg "octicon-diff" 14 "mr-2"}} {{$.locale.TrN $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n" $rejectOfficial}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 96b645e1f9..20b13de34b 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -10075,6 +10075,42 @@ } } }, + "/repos/{owner}/{repo}/releases/latest": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at", + "operationId": "repoGetLatestRelease", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/Release" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/releases/tags/{tag}": { "get": { "produces": [ diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl index 61b19c6032..8fb882718c 100644 --- a/templates/user/overview/header.tmpl +++ b/templates/user/overview/header.tmpl @@ -22,6 +22,9 @@ {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} + + {{svg "octicon-project"}} {{.locale.Tr "user.projects"}} + {{if (not .UnitPackagesGlobalDisabled)}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}} diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index 6c31723e0f..74211eb67b 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -106,6 +106,9 @@ {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} + + {{svg "octicon-project"}} {{.locale.Tr "user.projects"}} + {{if .IsPackageEnabled}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index e05cebb0e4..5cea142238 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -41,6 +41,207 @@
    +
    + + {{.locale.Tr "settings.select_scopes"}} + +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    diff --git a/tests/integration/api_admin_org_test.go b/tests/integration/api_admin_org_test.go index 05825eff31..89617f7a2c 100644 --- a/tests/integration/api_admin_org_test.go +++ b/tests/integration/api_admin_org_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -20,7 +21,7 @@ import ( func TestAPIAdminOrgCreate(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) org := api.CreateOrgOption{ UserName: "user2_org", @@ -54,7 +55,7 @@ func TestAPIAdminOrgCreate(t *testing.T) { func TestAPIAdminOrgCreateBadVisibility(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) org := api.CreateOrgOption{ UserName: "user2_org", diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index 53952210fd..b608c26f6e 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -9,6 +9,7 @@ import ( "testing" asymkey_model "code.gitea.io/gitea/models/asymkey" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" @@ -24,7 +25,7 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { session := loginUser(t, "user1") keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", @@ -51,7 +52,7 @@ func TestAPIAdminDeleteMissingSSHKey(t *testing.T) { defer tests.PrepareTestEnv(t)() // user1 is an admin user - token := getUserToken(t, "user1") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeSudo) req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d?token=%s", unittest.NonexistentID, token) MakeRequest(t, req, http.StatusNotFound) } @@ -60,7 +61,7 @@ func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", adminUsername, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ @@ -81,7 +82,7 @@ func TestAPISudoUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" normalUsername := "user2" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", normalUsername, token) req := NewRequest(t, "GET", urlStr) @@ -97,7 +98,7 @@ func TestAPISudoUserForbidden(t *testing.T) { adminUsername := "user1" normalUsername := "user2" - token := getUserToken(t, normalUsername) + token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/user?sudo=%s&token=%s", adminUsername, token) req := NewRequest(t, "GET", urlStr) MakeRequest(t, req, http.StatusForbidden) @@ -106,7 +107,7 @@ func TestAPISudoUserForbidden(t *testing.T) { func TestAPIListUsers(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) req := NewRequest(t, "GET", urlStr) @@ -142,7 +143,7 @@ func TestAPIListUsersNonAdmin(t *testing.T) { func TestAPICreateUserInvalidEmail(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users?token=%s", token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "email": "invalid_email@domain.com\r\n", @@ -160,7 +161,7 @@ func TestAPICreateUserInvalidEmail(t *testing.T) { func TestAPICreateAndDeleteUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) req := NewRequestWithValues( t, @@ -186,7 +187,7 @@ func TestAPICreateAndDeleteUser(t *testing.T) { func TestAPIEditUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) urlStr := fmt.Sprintf("/api/v1/admin/users/%s?token=%s", "user2", token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -228,7 +229,7 @@ func TestAPIEditUser(t *testing.T) { func TestAPICreateRepoForUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" - token := getUserToken(t, adminUsername) + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeSudo) req := NewRequestWithJSON( t, diff --git a/tests/integration/api_branch_test.go b/tests/integration/api_branch_test.go index 278edfbf9c..0d4a750a29 100644 --- a/tests/integration/api_branch_test.go +++ b/tests/integration/api_branch_test.go @@ -8,6 +8,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -15,7 +16,7 @@ import ( ) func testAPIGetBranch(t *testing.T, branchName string, exists bool) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) resp := MakeRequest(t, req, NoExpectedStatus) if !exists { @@ -31,7 +32,7 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) { } func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) resp := MakeRequest(t, req, expectedHTTPStatus) @@ -43,7 +44,7 @@ func testAPIGetBranchProtection(t *testing.T, branchName string, expectedHTTPSta } func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/branch_protections?token="+token, &api.BranchProtection{ RuleName: branchName, }) @@ -57,7 +58,7 @@ func testAPICreateBranchProtection(t *testing.T, branchName string, expectedHTTP } func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.BranchProtection, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "PATCH", "/api/v1/repos/user2/repo1/branch_protections/"+branchName+"?token="+token, body) resp := MakeRequest(t, req, expectedHTTPStatus) @@ -69,13 +70,13 @@ func testAPIEditBranchProtection(t *testing.T, branchName string, body *api.Bran } func testAPIDeleteBranchProtection(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branch_protections/%s?token=%s", branchName, token) MakeRequest(t, req, expectedHTTPStatus) } func testAPIDeleteBranch(t *testing.T, branchName string, expectedHTTPStatus int) { - token := getUserToken(t, "user2") + token := getUserToken(t, "user2", auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/user2/repo1/branches/%s?token=%s", branchName, token) MakeRequest(t, req, expectedHTTPStatus) } @@ -101,7 +102,7 @@ func TestAPICreateBranch(t *testing.T) { func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { username := "user2" - ctx := NewAPITestContext(t, username, "my-noo-repo") + ctx := NewAPITestContext(t, username, "my-noo-repo", auth_model.AccessTokenScopeRepo) giteaURL.Path = ctx.GitPath() t.Run("CreateRepo", doAPICreateRepository(ctx, false)) @@ -149,7 +150,7 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { } func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBranch, newBranch string, status int) bool { - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+user+"/"+repo+"/branches?token="+token, &api.CreateBranchRepoOption{ BranchName: newBranch, OldBranchName: oldBranch, diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go index b23db53d28..1f916ffa15 100644 --- a/tests/integration/api_comment_attachment_test.go +++ b/tests/integration/api_comment_attachment_test.go @@ -12,6 +12,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -81,7 +82,7 @@ func TestAPICreateCommentAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets?token=%s", repoOwner.Name, repo.Name, comment.ID, token) @@ -120,7 +121,7 @@ func TestAPIEditCommentAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -143,7 +144,7 @@ func TestAPIDeleteCommentAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, attachment.ID, token) diff --git a/tests/integration/api_comment_test.go b/tests/integration/api_comment_test.go index fb2d41223e..cc7712e548 100644 --- a/tests/integration/api_comment_test.go +++ b/tests/integration/api_comment_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -75,8 +76,9 @@ func TestAPIListIssueComments(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/comments", - repoOwner.Name, repo.Name, issue.Index) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/comments?token=%s", + repoOwner.Name, repo.Name, issue.Index, token) resp := MakeRequest(t, req, http.StatusOK) var comments []*api.Comment @@ -94,7 +96,7 @@ func TestAPICreateComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/comments?token=%s", repoOwner.Name, repo.Name, issue.Index, token) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ @@ -116,7 +118,7 @@ func TestAPIGetComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d", repoOwner.Name, repo.Name, comment.ID) MakeRequest(t, req, http.StatusOK) req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) @@ -144,7 +146,7 @@ func TestAPIEditComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -168,7 +170,7 @@ func TestAPIDeleteComment(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - token := getUserToken(t, repoOwner.Name) + token := getUserToken(t, repoOwner.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/comments/%d?token=%s", repoOwner.Name, repo.Name, comment.ID, token) MakeRequest(t, req, http.StatusNoContent) diff --git a/tests/integration/api_gpg_keys_test.go b/tests/integration/api_gpg_keys_test.go index 162a5a4fd5..f66961786f 100644 --- a/tests/integration/api_gpg_keys_test.go +++ b/tests/integration/api_gpg_keys_test.go @@ -9,6 +9,7 @@ import ( "strconv" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -20,7 +21,8 @@ type makeRequestFunc func(testing.TB, *http.Request, int) *httptest.ResponseReco func TestGPGKeys(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) + tokenWithGPGKeyScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminGPGKey, auth_model.AccessTokenScopeRepo) tt := []struct { name string @@ -34,6 +36,10 @@ func TestGPGKeys(t *testing.T) { }, { name: "LoggedAsUser2", makeRequest: session.MakeRequest, token: token, + results: []int{http.StatusForbidden, http.StatusOK, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden, http.StatusForbidden}, + }, + { + name: "LoggedAsUser2WithScope", makeRequest: session.MakeRequest, token: tokenWithGPGKeyScope, results: []int{http.StatusOK, http.StatusOK, http.StatusNotFound, http.StatusNoContent, http.StatusUnprocessableEntity, http.StatusNotFound, http.StatusCreated, http.StatusNotFound, http.StatusCreated}, }, } @@ -73,7 +79,7 @@ func TestGPGKeys(t *testing.T) { t.Run("CheckState", func(t *testing.T) { var keys []*api.GPGKey - req := NewRequest(t, "GET", "/api/v1/user/gpg_keys?token="+token) // GET all keys + req := NewRequest(t, "GET", "/api/v1/user/gpg_keys?token="+tokenWithGPGKeyScope) // GET all keys resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &keys) assert.Len(t, keys, 1) @@ -89,7 +95,7 @@ func TestGPGKeys(t *testing.T) { assert.Empty(t, subKey.Emails) var key api.GPGKey - req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey1.ID, 10)+"?token="+token) // Primary key 1 + req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(primaryKey1.ID, 10)+"?token="+tokenWithGPGKeyScope) // Primary key 1 resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) assert.EqualValues(t, "38EA3BCED732982C", key.KeyID) @@ -97,7 +103,7 @@ func TestGPGKeys(t *testing.T) { assert.EqualValues(t, "user2@example.com", key.Emails[0].Email) assert.True(t, key.Emails[0].Verified) - req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)+"?token="+token) // Subkey of 38EA3BCED732982C + req = NewRequest(t, "GET", "/api/v1/user/gpg_keys/"+strconv.FormatInt(subKey.ID, 10)+"?token="+tokenWithGPGKeyScope) // Subkey of 38EA3BCED732982C resp = MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &key) assert.EqualValues(t, "70D7C694D17D03AD", key.KeyID) diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index dbfe502ec1..3524ce9834 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/json" @@ -31,9 +32,9 @@ type APITestContext struct { ExpectedCode int } -func NewAPITestContext(t *testing.T, username, reponame string) APITestContext { +func NewAPITestContext(t *testing.T, username, reponame string, scope ...auth.AccessTokenScope) APITestContext { session := loginUser(t, username) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, scope...) return APITestContext{ Session: session, Token: token, diff --git a/tests/integration/api_httpsig_test.go b/tests/integration/api_httpsig_test.go index 881bb45ca4..57f83490dc 100644 --- a/tests/integration/api_httpsig_test.go +++ b/tests/integration/api_httpsig_test.go @@ -10,6 +10,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -52,7 +53,7 @@ func TestHTTPSigPubKey(t *testing.T) { // Add our public key to user1 defer tests.PrepareTestEnv(t)() session := loginUser(t, "user1") - token := url.QueryEscape(getTokenForLoggedInUser(t, session)) + token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey, auth_model.AccessTokenScopeSudo)) keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) keyType := "ssh-rsa" keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABAQCqOZB5vkRvXFXups1/0StDRdG8plbNSwsWEnNnP4Bvurxa0+z3W9B8GLKnDiLw5MbpbMNyBlpXw13GfuIeciy10DWTz0xUbiy3J3KabCaT36asIw2y7k6Z0jL0UBnrVENwq5/lUbZYqSZ4rRU744wkhh8TULpzM14npQCZwg6aEbG+MwjzddQ72fR+3BPBrKn5dTmmu8rH99O+U+Nuto81Tg7PA+NUupcHOmhdiEGq49plgVFXK98Vks5tiybL4GuzFyWgyX73Dg/QBMn2eMHt1EMv5Gs3i6GFhKKGo4rjDi9qI6PX5oDR4LTNe6cR8td8YhVD8WFZwLLl/vaYyIqd" diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go index 0558dda56a..b4d6dab42a 100644 --- a/tests/integration/api_issue_attachment_test.go +++ b/tests/integration/api_issue_attachment_test.go @@ -12,6 +12,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -72,7 +73,7 @@ func TestAPICreateIssueAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets?token=%s", repoOwner.Name, repo.Name, issue.Index, token) @@ -110,7 +111,7 @@ func TestAPIEditIssueAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ @@ -132,7 +133,7 @@ func TestAPIDeleteIssueAttachment(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets/%d?token=%s", repoOwner.Name, repo.Name, issue.Index, attachment.ID, token) diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go index 6f0fd87913..1824015983 100644 --- a/tests/integration/api_issue_label_test.go +++ b/tests/integration/api_issue_label_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -24,7 +25,7 @@ func TestAPIModifyLabels(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/labels?token=%s", owner.Name, repo.Name, token) // CreateLabel @@ -96,7 +97,7 @@ func TestAPIAddIssueLabels(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", repo.OwnerName, repo.Name, issue.Index, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueLabelsOption{ @@ -119,7 +120,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels?token=%s", owner.Name, repo.Name, issue.Index, token) req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{ @@ -143,7 +144,7 @@ func TestAPIModifyOrgLabels(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) user := "user1" session := loginUser(t, user) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminOrg) urlStr := fmt.Sprintf("/api/v1/orgs/%s/labels?token=%s", owner.Name, token) // CreateLabel diff --git a/tests/integration/api_issue_milestone_test.go b/tests/integration/api_issue_milestone_test.go index 60766e10fd..cbce795bc9 100644 --- a/tests/integration/api_issue_milestone_test.go +++ b/tests/integration/api_issue_milestone_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -28,7 +29,7 @@ func TestAPIIssuesMilestone(t *testing.T) { assert.Equal(t, structs.StateOpen, milestone.State()) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // update values of issue milestoneState := "closed" diff --git a/tests/integration/api_issue_reaction_test.go b/tests/integration/api_issue_reaction_test.go index 4e2ae3d57d..76140d7511 100644 --- a/tests/integration/api_issue_reaction_test.go +++ b/tests/integration/api_issue_reaction_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" @@ -28,7 +29,7 @@ func TestAPIIssuesReactions(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions?token=%s", @@ -87,7 +88,7 @@ func TestAPICommentReactions(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) diff --git a/tests/integration/api_issue_stopwatch_test.go b/tests/integration/api_issue_stopwatch_test.go index d1a3e86fda..a8a832414d 100644 --- a/tests/integration/api_issue_stopwatch_test.go +++ b/tests/integration/api_issue_stopwatch_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -25,7 +26,7 @@ func TestAPIListStopWatches(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/user/stopwatches?token=%s", token) resp := MakeRequest(t, req, http.StatusOK) var apiWatches []*api.StopWatch @@ -51,7 +52,7 @@ func TestAPIStopStopWatches(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/stop?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) MakeRequest(t, req, http.StatusCreated) @@ -67,7 +68,7 @@ func TestAPICancelStopWatches(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/stopwatch/delete?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) MakeRequest(t, req, http.StatusNoContent) @@ -83,7 +84,7 @@ func TestAPIStartStopWatches(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/issues/%d/stopwatch/start?token=%s", owner.Name, issue.Repo.Name, issue.Index, token) MakeRequest(t, req, http.StatusCreated) diff --git a/tests/integration/api_issue_subscription_test.go b/tests/integration/api_issue_subscription_test.go index a32b51e6fc..473e720754 100644 --- a/tests/integration/api_issue_subscription_test.go +++ b/tests/integration/api_issue_subscription_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -30,7 +31,7 @@ func TestAPIIssueSubscriptions(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) testSubscription := func(issue *issues_model.Issue, isWatching bool) { issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index 2074bbee7c..2f27978a37 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -29,7 +30,7 @@ func TestAPIListIssues(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}, "state": {"all"}}.Encode() @@ -80,7 +81,7 @@ func TestAPICreateIssue(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ Body: body, @@ -116,7 +117,7 @@ func TestAPIEditIssue(t *testing.T) { assert.Equal(t, api.StateOpen, issueBefore.State()) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // update values of issue issueState := "closed" diff --git a/tests/integration/api_issue_tracked_time_test.go b/tests/integration/api_issue_tracked_time_test.go index eda4150f8c..7d9c785474 100644 --- a/tests/integration/api_issue_tracked_time_test.go +++ b/tests/integration/api_issue_tracked_time_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPIGetTrackedTimes(t *testing.T) { assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) resp := MakeRequest(t, req, http.StatusOK) @@ -70,7 +71,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Deletion not allowed req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time6.ID, token) @@ -105,7 +106,7 @@ func TestAPIAddTrackedTimes(t *testing.T) { admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, admin.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/times?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, token) diff --git a/tests/integration/api_keys_test.go b/tests/integration/api_keys_test.go index d24db2bd16..dc25cbfc1a 100644 --- a/tests/integration/api_keys_test.go +++ b/tests/integration/api_keys_test.go @@ -10,6 +10,7 @@ import ( "testing" asymkey_model "code.gitea.io/gitea/models/asymkey" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -53,7 +54,7 @@ func TestCreateReadOnlyDeployKey(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) rawKeyBody := api.CreateKeyOption{ Title: "read-only", @@ -79,7 +80,7 @@ func TestCreateReadWriteDeployKey(t *testing.T) { repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) keysURL := fmt.Sprintf("/api/v1/repos/%s/%s/keys?token=%s", repoOwner.Name, repo.Name, token) rawKeyBody := api.CreateKeyOption{ Title: "read-write", @@ -103,7 +104,7 @@ func TestCreateUserKey(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}) session := loginUser(t, "user1") - token := url.QueryEscape(getTokenForLoggedInUser(t, session)) + token := url.QueryEscape(getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminPublicKey)) keysURL := fmt.Sprintf("/api/v1/user/keys?token=%s", token) keyType := "ssh-rsa" keyContent := "AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM=" @@ -167,7 +168,7 @@ func TestCreateUserKey(t *testing.T) { // Now login as user 2 session2 := loginUser(t, "user2") - token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2)) + token2 := url.QueryEscape(getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeAdminPublicKey)) // Should find key even though not ours, but we shouldn't know whose it is fingerprintURL = fmt.Sprintf("/api/v1/user/keys?token=%s&fingerprint=%s", token2, newPublicKey.Fingerprint) diff --git a/tests/integration/api_notification_test.go b/tests/integration/api_notification_test.go index cd230d6883..0ff13704cf 100644 --- a/tests/integration/api_notification_test.go +++ b/tests/integration/api_notification_test.go @@ -9,6 +9,7 @@ import ( "testing" activities_model "code.gitea.io/gitea/models/activities" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPINotification(t *testing.T) { thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) // -- GET /notifications -- // test filter @@ -145,7 +146,7 @@ func TestAPINotificationPUT(t *testing.T) { thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) // Check notifications are as expected req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=true&token=%s", token)) diff --git a/tests/integration/api_oauth2_apps_test.go b/tests/integration/api_oauth2_apps_test.go index d2a85992ac..c320efb391 100644 --- a/tests/integration/api_oauth2_apps_test.go +++ b/tests/integration/api_oauth2_apps_test.go @@ -8,7 +8,7 @@ import ( "net/http" "testing" - "code.gitea.io/gitea/models/auth" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -49,15 +49,15 @@ func testAPICreateOAuth2Application(t *testing.T) { assert.True(t, createdApp.ConfidentialClient) assert.NotEmpty(t, createdApp.Created) assert.EqualValues(t, appBody.RedirectURIs[0], createdApp.RedirectURIs[0]) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{UID: user.ID, Name: createdApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{UID: user.ID, Name: createdApp.Name}) } func testAPIListOAuth2Applications(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) - existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", RedirectURIs: []string{ @@ -80,15 +80,15 @@ func testAPIListOAuth2Applications(t *testing.T) { assert.Len(t, expectedApp.ClientID, 36) assert.Empty(t, expectedApp.ClientSecret) assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } func testAPIDeleteOAuth2Application(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteApplication) - oldApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + oldApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", }) @@ -97,7 +97,7 @@ func testAPIDeleteOAuth2Application(t *testing.T) { req := NewRequest(t, "DELETE", urlStr) MakeRequest(t, req, http.StatusNoContent) - unittest.AssertNotExistsBean(t, &auth.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) + unittest.AssertNotExistsBean(t, &auth_model.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name}) // Delete again will return not found req = NewRequest(t, "DELETE", urlStr) @@ -107,9 +107,9 @@ func testAPIDeleteOAuth2Application(t *testing.T) { func testAPIGetOAuth2Application(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadApplication) - existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", RedirectURIs: []string{ @@ -133,13 +133,13 @@ func testAPIGetOAuth2Application(t *testing.T) { assert.Empty(t, expectedApp.ClientSecret) assert.Len(t, expectedApp.RedirectURIs, 1) assert.EqualValues(t, existApp.RedirectURIs[0], expectedApp.RedirectURIs[0]) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } func testAPIUpdateOAuth2Application(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - existApp := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ + existApp := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ UID: user.ID, Name: "test-app-1", RedirectURIs: []string{ @@ -169,5 +169,5 @@ func testAPIUpdateOAuth2Application(t *testing.T) { assert.EqualValues(t, expectedApp.RedirectURIs[0], appBody.RedirectURIs[0]) assert.EqualValues(t, expectedApp.RedirectURIs[1], appBody.RedirectURIs[1]) assert.Equal(t, expectedApp.ConfidentialClient, appBody.ConfidentialClient) - unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) + unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: expectedApp.ID, Name: expectedApp.Name}) } diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go index 0be0c170d6..84166861a7 100644 --- a/tests/integration/api_org_test.go +++ b/tests/integration/api_org_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" @@ -21,7 +22,7 @@ import ( func TestAPIOrgCreate(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - token := getUserToken(t, "user1") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeWriteOrg) org := api.CreateOrgOption{ UserName: "user1_org", @@ -79,7 +80,7 @@ func TestAPIOrgEdit(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) org := api.EditOrgOption{ FullName: "User3 organization new full name", Description: "A new description", @@ -106,7 +107,7 @@ func TestAPIOrgEditBadVisibility(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteOrg) org := api.EditOrgOption{ FullName: "User3 organization new full name", Description: "A new description", @@ -126,14 +127,16 @@ func TestAPIOrgDeny(t *testing.T) { setting.Service.RequireSignInView = false }() + token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrg) + orgName := "user1_org" - req := NewRequestf(t, "GET", "/api/v1/orgs/%s", orgName) + req := NewRequestf(t, "GET", "/api/v1/orgs/%s?token=%s", orgName, token) MakeRequest(t, req, http.StatusNotFound) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", orgName) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token=%s", orgName, token) MakeRequest(t, req, http.StatusNotFound) - req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", orgName) + req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members?token=%s", orgName, token) MakeRequest(t, req, http.StatusNotFound) }) } @@ -141,20 +144,23 @@ func TestAPIOrgDeny(t *testing.T) { func TestAPIGetAll(t *testing.T) { defer tests.PrepareTestEnv(t)() - req := NewRequestf(t, "GET", "/api/v1/orgs") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadOrg) + + req := NewRequestf(t, "GET", "/api/v1/orgs?token=%s", token) resp := MakeRequest(t, req, http.StatusOK) var apiOrgList []*api.Organization DecodeJSON(t, resp, &apiOrgList) - assert.Len(t, apiOrgList, 7) - assert.Equal(t, "org25", apiOrgList[0].FullName) - assert.Equal(t, "public", apiOrgList[0].Visibility) + // accessing with a token will return all orgs + assert.Len(t, apiOrgList, 9) + assert.Equal(t, "org25", apiOrgList[1].FullName) + assert.Equal(t, "public", apiOrgList[1].Visibility) } func TestAPIOrgSearchEmptyTeam(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - token := getUserToken(t, "user1") + token := getUserToken(t, "user1", auth_model.AccessTokenScopeAdminOrg) orgName := "org_with_empty_team" // create org diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go index 1dcd76a317..39297c7d94 100644 --- a/tests/integration/api_packages_container_test.go +++ b/tests/integration/api_packages_container_test.go @@ -13,6 +13,7 @@ import ( "sync" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" @@ -31,6 +32,8 @@ func TestPackageContainer(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) has := func(l packages_model.PackagePropertyList, name string) bool { for _, pp := range l { @@ -558,7 +561,7 @@ func TestPackageContainer(t *testing.T) { assert.Equal(t, c.ExpectedLink, resp.Header().Get("Link")) } - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?type=container&q=%s", user.Name, image)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?type=container&q=%s&token=%s", user.Name, image, token)) resp := MakeRequest(t, req, http.StatusOK) var apiPackages []*api.Package diff --git a/tests/integration/api_packages_test.go b/tests/integration/api_packages_test.go index 8346e3bccc..9bca6a20ee 100644 --- a/tests/integration/api_packages_test.go +++ b/tests/integration/api_packages_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" @@ -28,7 +29,8 @@ func TestPackageAPI(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) + tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeDeletePackage) packageName := "test-package" packageVersion := "1.0.3" @@ -42,7 +44,7 @@ func TestPackageAPI(t *testing.T) { t.Run("ListPackages", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", user.Name, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", user.Name, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var apiPackages []*api.Package @@ -59,10 +61,10 @@ func TestPackageAPI(t *testing.T) { t.Run("GetPackage", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var p *api.Package @@ -81,7 +83,7 @@ func TestPackageAPI(t *testing.T) { assert.NoError(t, err) // no repository link - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var ap1 *api.Package @@ -91,7 +93,7 @@ func TestPackageAPI(t *testing.T) { // link to public repository assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1)) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp = MakeRequest(t, req, http.StatusOK) var ap2 *api.Package @@ -102,7 +104,7 @@ func TestPackageAPI(t *testing.T) { // link to private repository assert.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2)) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp = MakeRequest(t, req, http.StatusOK) var ap3 *api.Package @@ -116,10 +118,10 @@ func TestPackageAPI(t *testing.T) { t.Run("ListPackageFiles", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s/files?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s/files?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s/files?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s/files?token=%s", user.Name, packageName, packageVersion, tokenReadPackage)) resp := MakeRequest(t, req, http.StatusOK) var files []*api.PackageFile @@ -137,10 +139,10 @@ func TestPackageAPI(t *testing.T) { t.Run("DeletePackage", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenDeletePackage)) MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, token)) + req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s?token=%s", user.Name, packageName, packageVersion, tokenDeletePackage)) MakeRequest(t, req, http.StatusNoContent) }) } diff --git a/tests/integration/api_pull_review_test.go b/tests/integration/api_pull_review_test.go index 4b9c601783..cfb56724a6 100644 --- a/tests/integration/api_pull_review_test.go +++ b/tests/integration/api_pull_review_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -27,7 +28,7 @@ func TestAPIPullReview(t *testing.T) { // test ListPullReviews session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token) resp := MakeRequest(t, req, http.StatusOK) @@ -230,7 +231,7 @@ func TestAPIPullReviewRequest(t *testing.T) { // Test add Review Request session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token), &api.PullReviewRequestOptions{ Reviewers: []string{"user4@example.com", "user8"}, }) @@ -250,7 +251,7 @@ func TestAPIPullReviewRequest(t *testing.T) { // Test Remove Review Request session2 := loginUser(t, "user4") - token2 := getTokenForLoggedInUser(t, session2) + token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeRepo) req = NewRequestWithJSON(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, token2), &api.PullReviewRequestOptions{ Reviewers: []string{"user4"}, diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index 89d39179a6..4427c610bf 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -9,6 +9,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -28,7 +29,7 @@ func TestAPIViewPulls(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - ctx := NewAPITestContext(t, "user2", repo.Name) + ctx := NewAPITestContext(t, "user2", repo.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/pulls?state=all&token="+ctx.Token, owner.Name, repo.Name) resp := ctx.Session.MakeRequest(t, req, http.StatusOK) @@ -74,7 +75,7 @@ func TestAPIMergePullWIP(t *testing.T) { assert.Contains(t, pr.Issue.Title, setting.Repository.PullRequest.WorkInProgressPrefixes[0]) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s", owner.Name, repo.Name, pr.Index, token), &forms.MergePullRequestForm{ MergeMessageField: pr.Issue.Title, Do: string(repo_model.MergeStyleMerge), @@ -93,7 +94,7 @@ func TestAPICreatePullSuccess(t *testing.T) { owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) session := loginUser(t, owner11.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ Head: fmt.Sprintf("%s:master", owner11.Name), Base: "master", @@ -113,7 +114,7 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) { owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) session := loginUser(t, owner11.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) opts := &api.CreatePullRequestOption{ Head: fmt.Sprintf("%s:master", owner11.Name), @@ -150,7 +151,7 @@ func TestAPICreatePullWithFieldsFailure(t *testing.T) { owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}) session := loginUser(t, owner11.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) opts := &api.CreatePullRequestOption{ Head: fmt.Sprintf("%s:master", owner11.Name), @@ -180,7 +181,7 @@ func TestAPIEditPull(t *testing.T) { owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}) session := loginUser(t, owner10.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", owner10.Name, repo10.Name, token), &api.CreatePullRequestOption{ Head: "develop", Base: "master", diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 12d2a02fb1..aa5816ad02 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -24,7 +25,7 @@ func TestAPIListReleases(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - token := getUserToken(t, user2.LowerName) + token := getUserToken(t, user2.LowerName, auth_model.AccessTokenScopeRepo) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}}.Encode() @@ -100,7 +101,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) assert.NoError(t, err) @@ -152,7 +153,7 @@ func TestAPICreateReleaseToDefaultBranch(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") } @@ -163,7 +164,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) gitRepo, err := git.OpenRepository(git.DefaultContext, repo.RepoPath()) assert.NoError(t, err) @@ -175,6 +176,24 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") } +func TestAPIGetLatestRelease(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/latest", + owner.Name, repo.Name) + + req := NewRequestf(t, "GET", urlStr) + resp := MakeRequest(t, req, http.StatusOK) + + var release *api.Release + DecodeJSON(t, resp, &release) + + assert.Equal(t, "testing-release", release.Title) +} + func TestAPIGetReleaseByTag(t *testing.T) { defer tests.PrepareTestEnv(t)() @@ -213,7 +232,7 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") diff --git a/tests/integration/api_repo_archive_test.go b/tests/integration/api_repo_archive_test.go index a3c03ba2fc..fbcc12ccb6 100644 --- a/tests/integration/api_repo_archive_test.go +++ b/tests/integration/api_repo_archive_test.go @@ -10,6 +10,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -24,7 +25,7 @@ func TestAPIDownloadArchive(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user2.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.zip", user2.Name, repo.Name)) link.RawQuery = url.Values{"token": {token}}.Encode() diff --git a/tests/integration/api_repo_collaborator_test.go b/tests/integration/api_repo_collaborator_test.go index 318c86e2c3..ed01538477 100644 --- a/tests/integration/api_repo_collaborator_test.go +++ b/tests/integration/api_repo_collaborator_test.go @@ -8,6 +8,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { user10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) user11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 11}) - testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name) + testCtx := NewAPITestContext(t, repo2Owner.Name, repo2.Name, auth_model.AccessTokenScopeRepo) t.Run("RepoOwnerShouldBeOwner", func(t *testing.T) { req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, repo2Owner.Name, testCtx.Token) @@ -84,7 +85,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) _session := loginUser(t, user5.Name) - _testCtx := NewAPITestContext(t, user5.Name, repo2.Name) + _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) resp := _session.MakeRequest(t, req, http.StatusOK) @@ -99,7 +100,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user5.Name, perm.AccessModeRead)) _session := loginUser(t, user5.Name) - _testCtx := NewAPITestContext(t, user5.Name, repo2.Name) + _testCtx := NewAPITestContext(t, user5.Name, repo2.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user5.Name, _testCtx.Token) resp := _session.MakeRequest(t, req, http.StatusOK) @@ -115,7 +116,7 @@ func TestAPIRepoCollaboratorPermission(t *testing.T) { t.Run("AddUserAsCollaboratorWithReadAccess", doAPIAddCollaborator(testCtx, user11.Name, perm.AccessModeRead)) _session := loginUser(t, user10.Name) - _testCtx := NewAPITestContext(t, user10.Name, repo2.Name) + _testCtx := NewAPITestContext(t, user10.Name, repo2.Name, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/collaborators/%s/permission?token=%s", repo2Owner.Name, repo2.Name, user11.Name, _testCtx.Token) resp := _session.MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/api_repo_edit_test.go b/tests/integration/api_repo_edit_test.go index 716cebeb7c..9594b86d7e 100644 --- a/tests/integration/api_repo_edit_test.go +++ b/tests/integration/api_repo_edit_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" @@ -146,10 +147,10 @@ func TestAPIRepoEdit(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test editing a repo1 which user2 owns, changing name and many properties origRepoEditOption := getRepoEditOptionFromRepo(repo1) diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index 476441dbb1..b2098fdd03 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -150,10 +151,10 @@ func TestAPICreateFile(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test creating a file in repo1 which user2 owns, try both with branch and empty branch for _, branch := range [...]string{ @@ -279,7 +280,7 @@ func TestAPICreateFile(t *testing.T) { MakeRequest(t, req, http.StatusForbidden) // Test creating a file in an empty repository - doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo"), true)(t) + doAPICreateRepository(NewAPITestContext(t, "user2", "empty-repo", auth_model.AccessTokenScopeRepo), true)(t) createFileOptions = getCreateFileOptions() fileID++ treePath = fmt.Sprintf("new/file%d.txt", fileID) diff --git a/tests/integration/api_repo_file_delete_test.go b/tests/integration/api_repo_file_delete_test.go index 196d3208f5..9b80dc150a 100644 --- a/tests/integration/api_repo_file_delete_test.go +++ b/tests/integration/api_repo_file_delete_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -48,10 +49,10 @@ func TestAPIDeleteFile(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test deleting a file in repo1 which user2 owns, try both with branch and empty branch for _, branch := range [...]string{ diff --git a/tests/integration/api_repo_file_get_test.go b/tests/integration/api_repo_file_get_test.go index 4fca55c93d..a6a1e63439 100644 --- a/tests/integration/api_repo_file_get_test.go +++ b/tests/integration/api_repo_file_get_test.go @@ -8,6 +8,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -24,7 +25,7 @@ func TestAPIGetRawFileOrLFS(t *testing.T) { // Test with LFS onGiteaRun(t, func(t *testing.T, u *url.URL) { - httpContext := NewAPITestContext(t, "user2", "repo-lfs-test") + httpContext := NewAPITestContext(t, "user2", "repo-lfs-test", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeDeleteRepo) doAPICreateRepository(httpContext, false, func(t *testing.T, repository api.Repository) { u.Path = httpContext.GitPath() dstPath := t.TempDir() diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index 6dd06b7125..8e07511aaf 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -12,6 +12,7 @@ import ( "path/filepath" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -116,10 +117,10 @@ func TestAPIUpdateFile(t *testing.T) { // Get user2's token session := loginUser(t, user2.Name) - token2 := getTokenForLoggedInUser(t, session) + token2 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Get user4's token session = loginUser(t, user4.Name) - token4 := getTokenForLoggedInUser(t, session) + token4 := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Test updating a file in repo1 which user2 owns, try both with branch and empty branch for _, branch := range [...]string{ diff --git a/tests/integration/api_repo_git_hook_test.go b/tests/integration/api_repo_git_hook_test.go index a3bbe9bbad..e1c4682e6d 100644 --- a/tests/integration/api_repo_git_hook_test.go +++ b/tests/integration/api_repo_git_hook_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -30,7 +31,7 @@ func TestAPIListGitHooks(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", owner.Name, repo.Name, token) resp := MakeRequest(t, req, http.StatusOK) @@ -56,7 +57,7 @@ func TestAPIListGitHooksNoHooks(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", owner.Name, repo.Name, token) resp := MakeRequest(t, req, http.StatusOK) @@ -76,7 +77,7 @@ func TestAPIListGitHooksNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git?token=%s", owner.Name, repo.Name, token) MakeRequest(t, req, http.StatusForbidden) @@ -90,7 +91,7 @@ func TestAPIGetGitHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) resp := MakeRequest(t, req, http.StatusOK) @@ -107,7 +108,7 @@ func TestAPIGetGitHookNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepoHook) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) MakeRequest(t, req, http.StatusForbidden) @@ -121,7 +122,7 @@ func TestAPIEditGitHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) @@ -150,7 +151,7 @@ func TestAPIEditGitHookNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditGitHookOption{ @@ -167,7 +168,7 @@ func TestAPIDeleteGitHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminRepoHook) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) @@ -189,7 +190,7 @@ func TestAPIDeleteGitHookNoAccess(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/hooks/git/pre-receive?token=%s", owner.Name, repo.Name, token) MakeRequest(t, req, http.StatusForbidden) diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go index 146b4b74bd..b29fc45cf5 100644 --- a/tests/integration/api_repo_git_tags_test.go +++ b/tests/integration/api_repo_git_tags_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -69,7 +70,7 @@ func TestAPIDeleteTagByName(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, owner.LowerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/delete-tag?token=%s", owner.Name, repo.Name, token) diff --git a/tests/integration/api_repo_hook_test.go b/tests/integration/api_repo_hook_test.go index cf080575da..0fa2402992 100644 --- a/tests/integration/api_repo_hook_test.go +++ b/tests/integration/api_repo_hook_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -25,7 +26,7 @@ func TestAPICreateHook(t *testing.T) { // user1 is an admin user session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepoHook) completeURL := func(lastSegment string) string { return fmt.Sprintf("/api/v1/repos/%s/%s/%s?token=%s", owner.Name, repo.Name, lastSegment, token) } diff --git a/tests/integration/api_repo_lfs_migrate_test.go b/tests/integration/api_repo_lfs_migrate_test.go index 50d0c5966b..e66ca6b147 100644 --- a/tests/integration/api_repo_lfs_migrate_test.go +++ b/tests/integration/api_repo_lfs_migrate_test.go @@ -8,6 +8,7 @@ import ( "path" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/lfs" @@ -30,7 +31,7 @@ func TestAPIRepoLFSMigrateLocal(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ CloneAddr: path.Join(setting.RepoRootPath, "migration/lfs-test.git"), diff --git a/tests/integration/api_repo_lfs_test.go b/tests/integration/api_repo_lfs_test.go index c0ceaa8ba8..a7a70baeef 100644 --- a/tests/integration/api_repo_lfs_test.go +++ b/tests/integration/api_repo_lfs_test.go @@ -11,6 +11,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" @@ -59,7 +60,7 @@ func TestAPILFSMediaType(t *testing.T) { } func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository { - ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo") + ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo", auth_model.AccessTokenScopeRepo) t.Run("CreateRepo", doAPICreateRepository(ctx, false)) repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs-"+name+"-repo") diff --git a/tests/integration/api_repo_raw_test.go b/tests/integration/api_repo_raw_test.go index a35f1285b9..60e9eeed6b 100644 --- a/tests/integration/api_repo_raw_test.go +++ b/tests/integration/api_repo_raw_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/tests" @@ -19,7 +20,7 @@ func TestAPIReposRaw(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Login as User2. session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) for _, ref := range [...]string{ "master", // Branch diff --git a/tests/integration/api_repo_tags_test.go b/tests/integration/api_repo_tags_test.go index 6c7ab7971c..d4fd9097dd 100644 --- a/tests/integration/api_repo_tags_test.go +++ b/tests/integration/api_repo_tags_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" @@ -22,7 +23,7 @@ func TestAPIRepoTags(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Login as User2. session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repoName := "repo1" diff --git a/tests/integration/api_repo_teams_test.go b/tests/integration/api_repo_teams_test.go index 102f170d94..1f444e3141 100644 --- a/tests/integration/api_repo_teams_test.go +++ b/tests/integration/api_repo_teams_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" @@ -27,7 +28,7 @@ func TestAPIRepoTeams(t *testing.T) { // user4 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // ListTeams url := fmt.Sprintf("/api/v1/repos/%s/teams?token=%s", publicOrgRepo.FullName(), token) @@ -67,7 +68,7 @@ func TestAPIRepoTeams(t *testing.T) { // AddTeam with user2 user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session = loginUser(t, user.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token) req = NewRequest(t, "PUT", url) MakeRequest(t, req, http.StatusNoContent) diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index 76850fb827..76ceb779e0 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -10,6 +10,7 @@ import ( "testing" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" @@ -286,24 +287,17 @@ func TestAPIOrgRepos(t *testing.T) { count int includesPrivate bool }{ - nil: {count: 1}, + user: {count: 1}, user: {count: 3, includesPrivate: true}, user2: {count: 3, includesPrivate: true}, user3: {count: 1}, } for userToLogin, expected := range expectedResults { - var session *TestSession - var testName string - var token string - if userToLogin != nil && userToLogin.ID > 0 { - testName = fmt.Sprintf("LoggedUser%d", userToLogin.ID) - session = loginUser(t, userToLogin.Name) - token = getTokenForLoggedInUser(t, session) - } else { - testName = "AnonymousUser" - session = emptyTestSession(t) - } + testName := fmt.Sprintf("LoggedUser%d", userToLogin.ID) + session := loginUser(t, userToLogin.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) + t.Run(testName, func(t *testing.T) { req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token="+token, sourceOrg.Name) resp := MakeRequest(t, req, http.StatusOK) @@ -324,7 +318,7 @@ func TestAPIGetRepoByIDUnauthorized(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token) MakeRequest(t, req, http.StatusNotFound) } @@ -348,7 +342,7 @@ func TestAPIRepoMigrate(t *testing.T) { for _, testCase := range testCases { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{ CloneAddr: testCase.cloneURL, RepoOwnerID: testCase.userID, @@ -378,7 +372,7 @@ func TestAPIRepoMigrateConflict(t *testing.T) { func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { username := "user2" - baseAPITestContext := NewAPITestContext(t, username, "repo1") + baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) u.Path = baseAPITestContext.GitPath() @@ -413,7 +407,7 @@ func TestAPIMirrorSyncNonMirrorRepo(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) var repo api.Repository req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") @@ -445,7 +439,7 @@ func TestAPIOrgRepoCreate(t *testing.T) { for _, testCase := range testCases { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{ Name: testCase.repoName, }) @@ -459,7 +453,7 @@ func TestAPIRepoCreateConflict(t *testing.T) { func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { username := "user2" - baseAPITestContext := NewAPITestContext(t, username, "repo1") + baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo) u.Path = baseAPITestContext.GitPath() @@ -509,7 +503,7 @@ func TestAPIRepoTransfer(t *testing.T) { // create repo to move user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repoName := "moveME" apiRepo := new(api.Repository) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ @@ -527,7 +521,7 @@ func TestAPIRepoTransfer(t *testing.T) { user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiRepo.ID}) session = loginUser(t, user.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ NewOwner: testCase.newOwner, TeamIDs: testCase.teams, @@ -544,7 +538,7 @@ func transfer(t *testing.T) *repo_model.Repository { // create repo to move user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repoName := "moveME" apiRepo := new(api.Repository) req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/user/repos?token=%s", token), &api.CreateRepoOption{ @@ -574,7 +568,7 @@ func TestAPIAcceptTransfer(t *testing.T) { // try to accept with not authorized user session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) MakeRequest(t, req, http.StatusForbidden) @@ -584,7 +578,7 @@ func TestAPIAcceptTransfer(t *testing.T) { // accept transfer session = loginUser(t, "user4") - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/accept?token=%s", repo.OwnerName, repo.Name, token)) resp := MakeRequest(t, req, http.StatusAccepted) @@ -600,7 +594,7 @@ func TestAPIRejectTransfer(t *testing.T) { // try to reject with not authorized user session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) MakeRequest(t, req, http.StatusForbidden) @@ -610,7 +604,7 @@ func TestAPIRejectTransfer(t *testing.T) { // reject transfer session = loginUser(t, "user4") - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer/reject?token=%s", repo.OwnerName, repo.Name, token)) resp := MakeRequest(t, req, http.StatusOK) @@ -624,7 +618,7 @@ func TestAPIGenerateRepo(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) templateRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 44}) @@ -660,7 +654,7 @@ func TestAPIRepoGetReviewers(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token) @@ -674,7 +668,7 @@ func TestAPIRepoGetAssignees(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token) diff --git a/tests/integration/api_repo_topic_test.go b/tests/integration/api_repo_topic_test.go index 81eb1a9427..ab9fd9bb96 100644 --- a/tests/integration/api_repo_topic_test.go +++ b/tests/integration/api_repo_topic_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -59,7 +60,7 @@ func TestAPIRepoTopic(t *testing.T) { repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // Get user2's token - token2 := getUserToken(t, user2.Name) + token2 := getUserToken(t, user2.Name, auth_model.AccessTokenScopeRepo) // Test read topics using login url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) @@ -139,7 +140,7 @@ func TestAPIRepoTopic(t *testing.T) { MakeRequest(t, req, http.StatusNotFound) // Get user4's token - token4 := getUserToken(t, user4.Name) + token4 := getUserToken(t, user4.Name, auth_model.AccessTokenScopeRepo) // Test read topics with write access url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) diff --git a/tests/integration/api_team_test.go b/tests/integration/api_team_test.go index 06d47bf70b..27fe5e12e6 100644 --- a/tests/integration/api_team_test.go +++ b/tests/integration/api_team_test.go @@ -9,6 +9,7 @@ import ( "sort" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" @@ -29,7 +30,7 @@ func TestAPITeam(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser.UID}) session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) req := NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) resp := MakeRequest(t, req, http.StatusOK) @@ -43,7 +44,7 @@ func TestAPITeam(t *testing.T) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser2.UID}) session = loginUser(t, user2.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamUser.TeamID) _ = MakeRequest(t, req, http.StatusForbidden) @@ -53,7 +54,7 @@ func TestAPITeam(t *testing.T) { // Get an admin user able to create, update and delete teams. user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session = loginUser(t, user.Name) - token = getTokenForLoggedInUser(t, session) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAdminOrg) org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 6}) @@ -227,7 +228,7 @@ func TestAPITeamSearch(t *testing.T) { var results TeamSearchResults - token := getUserToken(t, user.Name) + token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "_team", token) resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) @@ -237,7 +238,7 @@ func TestAPITeamSearch(t *testing.T) { // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) - token5 := getUserToken(t, user5.Name) + token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "team", token5) MakeRequest(t, req, http.StatusForbidden) @@ -252,7 +253,7 @@ func TestAPIGetTeamRepo(t *testing.T) { var results api.Repository - token := getUserToken(t, user.Name) + token := getUserToken(t, user.Name, auth_model.AccessTokenScopeReadOrg) req := NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token) resp := MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) @@ -260,7 +261,7 @@ func TestAPIGetTeamRepo(t *testing.T) { // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) - token5 := getUserToken(t, user5.Name) + token5 := getUserToken(t, user5.Name, auth_model.AccessTokenScopeReadOrg) req = NewRequestf(t, "GET", "/api/v1/teams/%d/repos/%s/?token=%s", team.ID, teamRepo.FullName(), token5) MakeRequest(t, req, http.StatusNotFound) diff --git a/tests/integration/api_team_user_test.go b/tests/integration/api_team_user_test.go index a5078aedcc..ec977fa572 100644 --- a/tests/integration/api_team_user_test.go +++ b/tests/integration/api_team_user_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -22,7 +23,7 @@ func TestAPITeamUser(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", "/api/v1/teams/1/members/user1?token="+token) MakeRequest(t, req, http.StatusNotFound) diff --git a/tests/integration/api_user_email_test.go b/tests/integration/api_user_email_test.go index 147f703e9a..09083d9ce8 100644 --- a/tests/integration/api_user_email_test.go +++ b/tests/integration/api_user_email_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -18,7 +19,7 @@ func TestAPIListEmails(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) req := NewRequest(t, "GET", "/api/v1/user/emails?token="+token) resp := MakeRequest(t, req, http.StatusOK) @@ -45,7 +46,7 @@ func TestAPIAddEmail(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) opts := api.CreateEmailOption{ Emails: []string{"user101@example.com"}, @@ -82,7 +83,7 @@ func TestAPIDeleteEmail(t *testing.T) { normalUsername := "user2" session := loginUser(t, normalUsername) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeUser) opts := api.DeleteEmailOption{ Emails: []string{"user2-3@example.com"}, diff --git a/tests/integration/api_user_follow_test.go b/tests/integration/api_user_follow_test.go index 65749521cc..c7ad62e649 100644 --- a/tests/integration/api_user_follow_test.go +++ b/tests/integration/api_user_follow_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -24,7 +25,7 @@ func TestAPIFollow(t *testing.T) { token1 := getTokenForLoggedInUser(t, session1) session2 := loginUser(t, user2) - token2 := getTokenForLoggedInUser(t, session2) + token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeUserFollow) t.Run("Follow", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/api_user_org_perm_test.go b/tests/integration/api_user_org_perm_test.go index 8df418494a..ac575b1f01 100644 --- a/tests/integration/api_user_org_perm_test.go +++ b/tests/integration/api_user_org_perm_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -32,7 +33,7 @@ func sampleTest(t *testing.T, auoptc apiUserOrgPermTestCase) { defer tests.PrepareTestEnv(t)() session := loginUser(t, auoptc.LoginUser) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s/orgs/%s/permissions?token=%s", auoptc.User, auoptc.Organization, token)) resp := MakeRequest(t, req, http.StatusOK) @@ -125,7 +126,7 @@ func TestUnknowUser(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/unknow/orgs/org25/permissions?token=%s", token)) resp := MakeRequest(t, req, http.StatusNotFound) @@ -139,7 +140,7 @@ func TestUnknowOrganization(t *testing.T) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrg) req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/user1/orgs/unknow/permissions?token=%s", token)) resp := MakeRequest(t, req, http.StatusNotFound) diff --git a/tests/integration/api_user_orgs_test.go b/tests/integration/api_user_orgs_test.go index 1f9ee2ea6e..831ca018b4 100644 --- a/tests/integration/api_user_orgs_test.go +++ b/tests/integration/api_user_orgs_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -61,15 +62,14 @@ func TestUserOrgs(t *testing.T) { orgs = getUserOrgs(t, unrelatedUsername, privateMemberUsername) assert.Len(t, orgs, 0) - // not authenticated call also should hide org membership - orgs = getUserOrgs(t, "", privateMemberUsername) - assert.Len(t, orgs, 0) + // not authenticated call should not be allowed + testUserOrgsUnauthenticated(t, privateMemberUsername) } func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organization) { token := "" if len(userDoer) != 0 { - token = getUserToken(t, userDoer) + token = getUserToken(t, userDoer, auth_model.AccessTokenScopeReadOrg) } urlStr := fmt.Sprintf("/api/v1/users/%s/orgs?token=%s", userCheck, token) req := NewRequest(t, "GET", urlStr) @@ -78,6 +78,12 @@ func getUserOrgs(t *testing.T, userDoer, userCheck string) (orgs []*api.Organiza return orgs } +func testUserOrgsUnauthenticated(t *testing.T, userCheck string) { + session := emptyTestSession(t) + req := NewRequestf(t, "GET", "/api/v1/users/%s/orgs", userCheck) + session.MakeRequest(t, req, http.StatusUnauthorized) +} + func TestMyOrgs(t *testing.T) { defer tests.PrepareTestEnv(t)() @@ -85,7 +91,7 @@ func TestMyOrgs(t *testing.T) { MakeRequest(t, req, http.StatusUnauthorized) normalUsername := "user2" - token := getUserToken(t, normalUsername) + token := getUserToken(t, normalUsername, auth_model.AccessTokenScopeReadOrg) req = NewRequest(t, "GET", "/api/v1/user/orgs?token="+token) resp := MakeRequest(t, req, http.StatusOK) var orgs []*api.Organization diff --git a/tests/integration/api_user_star_test.go b/tests/integration/api_user_star_test.go index 63363f22de..6a486c19a8 100644 --- a/tests/integration/api_user_star_test.go +++ b/tests/integration/api_user_star_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -22,11 +23,12 @@ func TestAPIStar(t *testing.T) { session := loginUser(t, user) token := getTokenForLoggedInUser(t, session) + tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) t.Run("Star", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) + req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) }) @@ -47,7 +49,7 @@ func TestAPIStar(t *testing.T) { t.Run("GetMyStarredRepos", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred?token=%s", tokenWithRepoScope)) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) @@ -61,17 +63,17 @@ func TestAPIStar(t *testing.T) { t.Run("IsStarring", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo+"notexisting", tokenWithRepoScope)) MakeRequest(t, req, http.StatusNotFound) }) t.Run("Unstar", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, token)) + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) }) } diff --git a/tests/integration/api_user_watch_test.go b/tests/integration/api_user_watch_test.go index 295e639fd1..5702962573 100644 --- a/tests/integration/api_user_watch_test.go +++ b/tests/integration/api_user_watch_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -22,11 +23,12 @@ func TestAPIWatch(t *testing.T) { session := loginUser(t, user) token := getTokenForLoggedInUser(t, session) + tokenWithRepoScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) t.Run("Watch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) + req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusOK) }) @@ -47,7 +49,7 @@ func TestAPIWatch(t *testing.T) { t.Run("GetMyWatchedRepos", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/subscriptions?token=%s", token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/subscriptions?token=%s", tokenWithRepoScope)) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "1", resp.Header().Get("X-Total-Count")) @@ -61,17 +63,17 @@ func TestAPIWatch(t *testing.T) { t.Run("IsWatching", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusOK) - req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo+"notexisting", token)) + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo+"notexisting", tokenWithRepoScope)) MakeRequest(t, req, http.StatusNotFound) }) t.Run("Unwatch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, token)) + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/repos/%s/subscription?token=%s", repo, tokenWithRepoScope)) MakeRequest(t, req, http.StatusNoContent) }) } diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go index 546f4d0e3e..3f85074c8a 100644 --- a/tests/integration/api_wiki_test.go +++ b/tests/integration/api_wiki_test.go @@ -9,6 +9,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" @@ -179,7 +180,7 @@ func TestAPINewWikiPage(t *testing.T) { defer tests.PrepareTestEnv(t)() username := "user2" session := loginUser(t, username) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new?token=%s", username, "repo1", token) @@ -196,7 +197,7 @@ func TestAPIEditWikiPage(t *testing.T) { defer tests.PrepareTestEnv(t)() username := "user2" session := loginUser(t, username) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/Page-With-Spaced-Name?token=%s", username, "repo1", token) diff --git a/tests/integration/dump_restore_test.go b/tests/integration/dump_restore_test.go index e34738aaf1..9ad795d53a 100644 --- a/tests/integration/dump_restore_test.go +++ b/tests/integration/dump_restore_test.go @@ -14,6 +14,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -50,7 +51,7 @@ func TestDumpRestore(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) session := loginUser(t, repoOwner.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // // Phase 1: dump repo1 from the Gitea instance to the filesystem diff --git a/tests/integration/eventsource_test.go b/tests/integration/eventsource_test.go index e810a9fa24..4fdb8cd6f5 100644 --- a/tests/integration/eventsource_test.go +++ b/tests/integration/eventsource_test.go @@ -10,6 +10,7 @@ import ( "time" activities_model "code.gitea.io/gitea/models/activities" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -59,7 +60,7 @@ func TestEventSourceManagerRun(t *testing.T) { thread5 := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{ID: 5}) assert.NoError(t, thread5.LoadAttributes(db.DefaultContext)) session := loginUser(t, user2.Name) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeNotification) var apiNL []api.NotificationThread diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index f7e1e04b1e..a11bad21b7 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -16,6 +16,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/perm" @@ -42,11 +43,11 @@ func TestGit(t *testing.T) { func testGit(t *testing.T, u *url.URL) { username := "user2" - baseAPITestContext := NewAPITestContext(t, username, "repo1") + baseAPITestContext := NewAPITestContext(t, username, "repo1", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeWritePublicKey, auth_model.AccessTokenScopeDeleteRepo) u.Path = baseAPITestContext.GitPath() - forkedUserCtx := NewAPITestContext(t, "user4", "repo1") + forkedUserCtx := NewAPITestContext(t, "user4", "repo1", auth_model.AccessTokenScopeRepo) t.Run("HTTP", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -357,7 +358,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes t.Run("CreateBranchProtected", doGitCreateBranch(dstPath, "protected")) t.Run("PushProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) - ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame) + ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) t.Run("ProtectProtectedBranchNoWhitelist", doProtectBranch(ctx, "protected", "", "")) t.Run("GenerateCommit", func(t *testing.T) { _, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") @@ -601,7 +602,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) { return func(t *testing.T) { defer tests.PrintCurrentTest(t)() - ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame) + ctx := NewAPITestContext(t, baseCtx.Username, baseCtx.Reponame, auth_model.AccessTokenScopeRepo) t.Run("CheckoutProtected", doGitCheckoutBranch(dstPath, "protected")) t.Run("PullProtected", doGitPull(dstPath, "origin", "protected")) diff --git a/tests/integration/gpg_git_test.go b/tests/integration/gpg_git_test.go index 669212ff14..36095694b0 100644 --- a/tests/integration/gpg_git_test.go +++ b/tests/integration/gpg_git_test.go @@ -10,6 +10,7 @@ import ( "os" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/process" @@ -69,7 +70,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { assert.NotNil(t, branch.Commit) @@ -93,7 +94,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -110,7 +111,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-Never", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-Never", crudActionCreateFile( t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -123,7 +124,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-Always", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-Always", crudActionCreateFile( t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { assert.NotNil(t, response.Verification) @@ -160,7 +161,7 @@ func TestGPGGit(t *testing.T) { t.Run("Unsigned-Initial-CRUD-ParentSigned", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile( t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) { assert.NotNil(t, response.Verification) @@ -183,7 +184,7 @@ func TestGPGGit(t *testing.T) { t.Run("AlwaysSign-Initial", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always") + testCtx := NewAPITestContext(t, username, "initial-always", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { assert.NotNil(t, branch.Commit) @@ -211,7 +212,7 @@ func TestGPGGit(t *testing.T) { t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always-never") + testCtx := NewAPITestContext(t, username, "initial-always-never", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CreateCRUDFile-Never", crudActionCreateFile( t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) { @@ -224,7 +225,7 @@ func TestGPGGit(t *testing.T) { u.Path = baseAPITestContext.GitPath() t.Run("AlwaysSign-Initial-CRUD-ParentSigned-On-Always", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always-parent") + testCtx := NewAPITestContext(t, username, "initial-always-parent", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { @@ -243,7 +244,7 @@ func TestGPGGit(t *testing.T) { t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-always-always") + testCtx := NewAPITestContext(t, username, "initial-always-always", auth_model.AccessTokenScopeRepo) t.Run("CreateRepository", doAPICreateRepository(testCtx, false)) t.Run("CreateCRUDFile-Always", crudActionCreateFile( t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { @@ -263,7 +264,7 @@ func TestGPGGit(t *testing.T) { t.Run("UnsignedMerging", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) var err error t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t) @@ -284,7 +285,7 @@ func TestGPGGit(t *testing.T) { t.Run("BaseSignedMerging", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) var err error t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t) @@ -305,7 +306,7 @@ func TestGPGGit(t *testing.T) { t.Run("CommitsSignedMerging", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - testCtx := NewAPITestContext(t, username, "initial-unsigned") + testCtx := NewAPITestContext(t, username, "initial-unsigned", auth_model.AccessTokenScopeRepo) var err error t.Run("CreatePullRequest", func(t *testing.T) { pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t) diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 911d9ddf4c..fbb6322785 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -21,6 +21,7 @@ import ( "testing" "time" + "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" @@ -217,8 +218,8 @@ func emptyTestSession(t testing.TB) *TestSession { return &TestSession{jar: jar} } -func getUserToken(t testing.TB, userName string) string { - return getTokenForLoggedInUser(t, loginUser(t, userName)) +func getUserToken(t testing.TB, userName string, scope ...auth.AccessTokenScope) string { + return getTokenForLoggedInUser(t, loginUser(t, userName), scope...) } func loginUser(t testing.TB, userName string) *TestSession { @@ -256,7 +257,10 @@ func loginUserWithPassword(t testing.TB, userName, password string) *TestSession // token has to be unique this counter take care of var tokenCounter int64 -func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { +// getTokenForLoggedInUser returns a token for a logged in user. +// The scope is an optional list of snake_case strings like the frontend form fields, +// but without the "scope_" prefix. +func getTokenForLoggedInUser(t testing.TB, session *TestSession, scopes ...auth.AccessTokenScope) string { t.Helper() var token string req := NewRequest(t, "GET", "/user/settings/applications") @@ -274,10 +278,13 @@ func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { csrf = doc.GetCSRF() } assert.NotEmpty(t, csrf) - req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{ - "_csrf": csrf, - "name": fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1)), - }) + urlValues := url.Values{} + urlValues.Add("_csrf", csrf) + urlValues.Add("name", fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1))) + for _, scope := range scopes { + urlValues.Add("scope", string(scope)) + } + req = NewRequestWithURLValues(t, "POST", "/user/settings/applications", urlValues) resp = session.MakeRequest(t, req, http.StatusSeeOther) // Log the flash values on failure @@ -317,6 +324,11 @@ func NewRequestWithValues(t testing.TB, method, urlStr string, values map[string for key, value := range values { urlValues[key] = []string{value} } + return NewRequestWithURLValues(t, method, urlStr, urlValues) +} + +func NewRequestWithURLValues(t testing.TB, method, urlStr string, urlValues url.Values) *http.Request { + t.Helper() req := NewRequestWithBody(t, method, urlStr, bytes.NewBufferString(urlValues.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") return req diff --git a/tests/integration/migrate_test.go b/tests/integration/migrate_test.go index 9eca69cfcf..a925493d7c 100644 --- a/tests/integration/migrate_test.go +++ b/tests/integration/migrate_test.go @@ -11,6 +11,7 @@ import ( "path/filepath" "testing" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -66,7 +67,7 @@ func TestMigrateGiteaForm(t *testing.T) { repoName := "repo1" repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName}) session := loginUser(t, ownerName) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) // Step 0: verify the repo is available req := NewRequestf(t, "GET", fmt.Sprintf("/%s/%s", ownerName, repoName)) diff --git a/tests/integration/org_count_test.go b/tests/integration/org_count_test.go index a6fe7f188e..8f850a170f 100644 --- a/tests/integration/org_count_test.go +++ b/tests/integration/org_count_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -24,7 +25,7 @@ func testOrgCounts(t *testing.T, u *url.URL) { orgOwner := "user2" orgName := "testOrg" orgCollaborator := "user4" - ctx := NewAPITestContext(t, orgOwner, "repo1") + ctx := NewAPITestContext(t, orgOwner, "repo1", auth_model.AccessTokenScopeAdminOrg) var ownerCountRepos map[string]int var collabCountRepos map[string]int diff --git a/tests/integration/org_test.go b/tests/integration/org_test.go index 09a5f42082..bfa6380e8a 100644 --- a/tests/integration/org_test.go +++ b/tests/integration/org_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" @@ -158,7 +159,7 @@ func TestOrgRestrictedUser(t *testing.T) { // Therefore create a read-only team adminSession := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, adminSession) + token := getTokenForLoggedInUser(t, adminSession, auth_model.AccessTokenScopeAdminOrg) teamToCreate := &api.CreateTeamOption{ Name: "codereader", diff --git a/tests/integration/privateactivity_test.go b/tests/integration/privateactivity_test.go index 06019406d7..6e1377ae1f 100644 --- a/tests/integration/privateactivity_test.go +++ b/tests/integration/privateactivity_test.go @@ -9,6 +9,7 @@ import ( "testing" activities_model "code.gitea.io/gitea/models/activities" + auth_model "code.gitea.io/gitea/models/auth" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -33,7 +34,7 @@ func testPrivateActivityDoSomethingForActionEntries(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}) session := loginUser(t, privateActivityTestUser) - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{ Body: "test", diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index e72d00ffb3..491fc0e0aa 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -17,6 +17,7 @@ import ( "time" "code.gitea.io/gitea/models" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -217,7 +218,7 @@ func TestCantMergeConflict(t *testing.T) { testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n") // Use API to create a conflicting pr - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ Head: "conflict", Base: "base", @@ -325,7 +326,7 @@ func TestCantMergeUnrelated(t *testing.T) { testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") // Use API to create a conflicting pr - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls?token=%s", "user1", "repo1", token), &api.CreatePullRequestOption{ Head: "unrelated", Base: "base", diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go index bca8ec848b..e60d17edc0 100644 --- a/tests/integration/pull_status_test.go +++ b/tests/integration/pull_status_test.go @@ -11,6 +11,7 @@ import ( "strings" "testing" + auth_model "code.gitea.io/gitea/models/auth" api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -63,7 +64,7 @@ func TestPullCreate_CommitStatus(t *testing.T) { api.CommitStatusWarning: "gitea-exclamation", } - testCtx := NewAPITestContext(t, "user1", "repo1") + testCtx := NewAPITestContext(t, "user1", "repo1", auth_model.AccessTokenScopeRepo) // Update commit status, and check if icon is updated as well for _, status := range statusList { diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go index 1e20a63e66..bd416e5bcf 100644 --- a/tests/integration/pull_update_test.go +++ b/tests/integration/pull_update_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" @@ -38,7 +39,7 @@ func TestAPIPullUpdate(t *testing.T) { assert.NoError(t, pr.LoadIssue(db.DefaultContext)) session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/pulls/%d/update?token="+token, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) session.MakeRequest(t, req, http.StatusOK) @@ -66,7 +67,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) { assert.NoError(t, pr.LoadIssue(db.DefaultContext)) session := loginUser(t, "user2") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeRepo) req := NewRequestf(t, "POST", "/api/v1/repos/%s/%s/pulls/%d/update?style=rebase&token="+token, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, pr.Issue.Index) session.MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/repo_commits_test.go b/tests/integration/repo_commits_test.go index ab90e72877..cbd83c6deb 100644 --- a/tests/integration/repo_commits_test.go +++ b/tests/integration/repo_commits_test.go @@ -11,6 +11,7 @@ import ( "sync" "testing" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -50,7 +51,8 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { assert.NotEmpty(t, commitURL) // Call API to add status for commit - t.Run("CreateStatus", doAPICreateCommitStatus(NewAPITestContext(t, "user2", "repo1"), path.Base(commitURL), api.CommitStatusState(state))) + ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepo) + t.Run("CreateStatus", doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CommitStatusState(state))) req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master") resp = session.MakeRequest(t, req, http.StatusOK) @@ -142,7 +144,8 @@ func TestRepoCommitsStatusParallel(t *testing.T) { wg.Add(1) go func(parentT *testing.T, i int) { parentT.Run(fmt.Sprintf("ParallelCreateStatus_%d", i), func(t *testing.T) { - runBody := doAPICreateCommitStatus(NewAPITestContext(t, "user2", "repo1"), path.Base(commitURL), api.CommitStatusState("pending")) + ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeRepoStatus) + runBody := doAPICreateCommitStatus(ctx, path.Base(commitURL), api.CommitStatusState("pending")) runBody(t) wg.Done() }) diff --git a/tests/integration/ssh_key_test.go b/tests/integration/ssh_key_test.go index 89a2774303..1e9dc264a6 100644 --- a/tests/integration/ssh_key_test.go +++ b/tests/integration/ssh_key_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" @@ -47,7 +48,9 @@ func TestPushDeployKeyOnEmptyRepo(t *testing.T) { func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { // OK login - ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1") + ctx := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeRepo) + ctxWithDeleteRepo := NewAPITestContext(t, "user2", "deploy-key-empty-repo-1", auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeDeleteRepo) + keyname := fmt.Sprintf("%s-push", ctx.Reponame) u.Path = ctx.GitPath() @@ -72,7 +75,7 @@ func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { t.Run("CheckIsNotEmpty", doCheckRepositoryEmptyStatus(ctx, false)) - t.Run("DeleteRepository", doAPIDeleteRepository(ctx)) + t.Run("DeleteRepository", doAPIDeleteRepository(ctxWithDeleteRepo)) }) } @@ -89,10 +92,13 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { keyname := fmt.Sprintf("%s-push", reponame) // OK login - ctx := NewAPITestContext(t, username, reponame) + ctx := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminPublicKey) + ctxWithDeleteRepo := NewAPITestContext(t, username, reponame, auth_model.AccessTokenScopeRepo, auth_model.AccessTokenScopeAdminPublicKey, auth_model.AccessTokenScopeDeleteRepo) otherCtx := ctx otherCtx.Reponame = "ssh-key-test-repo-2" + otherCtxWithDeleteRepo := ctxWithDeleteRepo + otherCtxWithDeleteRepo.Reponame = otherCtx.Reponame failCtx := ctx failCtx.ExpectedCode = http.StatusUnprocessableEntity @@ -160,7 +166,7 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { otherSSHURL := createSSHUrl(otherCtx.GitPath(), u) dstOtherPath := t.TempDir() - t.Run("DeleteRepository", doAPIDeleteRepository(ctx)) + t.Run("DeleteRepository", doAPIDeleteRepository(ctxWithDeleteRepo)) t.Run("FailToCreateUserKeyAsStillDeploy", doAPICreateUserKey(failCtx, keyname, keyFile)) @@ -170,9 +176,9 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { t.Run("PushToOther", doGitPushTestRepository(dstOtherPath, "origin", "master")) - t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtx)) + t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtxWithDeleteRepo)) - t.Run("RecreateRepository", doAPICreateRepository(ctx, false)) + t.Run("RecreateRepository", doAPICreateRepository(ctxWithDeleteRepo, false)) t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) { userKeyPublicKeyID = publicKey.ID diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index eeaa6d6e00..febfe576cf 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + auth_model "code.gitea.io/gitea/models/auth" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -165,7 +166,7 @@ Note: This user hasn't uploaded any GPG keys. // Import key // User1 session := loginUser(t, "user1") - token := getTokenForLoggedInUser(t, session) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteGPGKey) testCreateGPGKey(t, session.MakeRequest, token, http.StatusCreated, `-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBFyy/VUBCADJ7zbM20Z1RWmFoVgp5WkQfI2rU1Vj9cQHes9i42wVLLtcbPeo diff --git a/web_src/js/features/aria.js b/web_src/js/features/aria.js index 162843678b..a5ac84e446 100644 --- a/web_src/js/features/aria.js +++ b/web_src/js/features/aria.js @@ -98,3 +98,20 @@ function attachOneDropdownAria($dropdown) { export function attachDropdownAria($dropdowns) { $dropdowns.each((_, e) => attachOneDropdownAria($(e))); } + +export function attachCheckboxAria($checkboxes) { + $checkboxes.checkbox(); + + // Fomantic UI checkbox needs to be something like:
    + // It doesn't work well with + // To make it work with aria, the "id"/"for" attributes are necessary, so add them automatically if missing. + // In the future, refactor to use native checkbox directly, then this patch could be removed. + for (const el of $checkboxes) { + const label = el.querySelector('label'); + const input = el.querySelector('input'); + if (!label || !input || input.getAttribute('id')) continue; + const id = generateAriaId(); + input.setAttribute('id', id); + label.setAttribute('for', id); + } +} diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 2504f3be0a..4677eeac0c 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -4,9 +4,10 @@ import {mqBinarySearch} from '../utils.js'; import {createDropzone} from './dropzone.js'; import {initCompColorPicker} from './comp/ColorPicker.js'; import {showGlobalErrorMessage} from '../bootstrap.js'; -import {attachDropdownAria} from './aria.js'; +import {attachCheckboxAria, attachDropdownAria} from './aria.js'; import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.js'; import {initTooltip} from '../modules/tippy.js'; +import {svg} from '../svg.js'; const {appUrl, csrfToken} = window.config; @@ -110,7 +111,7 @@ export function initGlobalCommon() { }); attachDropdownAria($uiDropdowns); - $('.ui.checkbox').checkbox(); + attachCheckboxAria($('.ui.checkbox')); $('.tabular.menu .item').tab(); $('.tabable.menu .item').tab(); @@ -167,6 +168,21 @@ export function initGlobalDropzone() { file.uuid = data.uuid; const input = $(``).val(data.uuid); $dropzone.find('.files').append(input); + // Create a "Copy Link" element, to conveniently copy the image + // or file link as Markdown to the clipboard + const copyLinkElement = document.createElement('div'); + copyLinkElement.className = 'tc'; + // The a element has a hardcoded cursor: pointer because the default is overridden by .dropzone + copyLinkElement.innerHTML = `
    ${svg('octicon-copy', 14, 'copy link')} Copy link`; + copyLinkElement.addEventListener('click', (e) => { + e.preventDefault(); + let fileMarkdown = `[${file.name}](/attachments/${file.uuid})`; + if (file.type.startsWith('image/')) { + fileMarkdown = `!${fileMarkdown}`; + } + navigator.clipboard.writeText(fileMarkdown); + }); + file.previewTemplate.appendChild(copyLinkElement); }); this.on('removedfile', (file) => { $(`#${file.uuid}`).remove(); diff --git a/web_src/js/markup/asciicast.js b/web_src/js/markup/asciicast.js new file mode 100644 index 0000000000..902cfcb731 --- /dev/null +++ b/web_src/js/markup/asciicast.js @@ -0,0 +1,17 @@ +export async function renderAsciinemaPlayer() { + const els = document.querySelectorAll('.asciinema-player-container'); + if (!els.length) return; + + const [player] = await Promise.all([ + import(/* webpackChunkName: "asciinema-player" */'asciinema-player'), + import(/* webpackChunkName: "asciinema-player" */'asciinema-player/dist/bundle/asciinema-player.css'), + ]); + + for (const el of els) { + player.create(el.getAttribute('data-asciinema-player-src'), el, { + // poster (a preview frame) to display until the playback is started. + // Set it to 1 hour (also means the end if the video is shorter) to make the preview frame show more. + poster: 'npt:1:0:0', + }); + } +} diff --git a/web_src/js/markup/content.js b/web_src/js/markup/content.js index 319c229385..e4ec3d0b4b 100644 --- a/web_src/js/markup/content.js +++ b/web_src/js/markup/content.js @@ -1,6 +1,7 @@ import {renderMermaid} from './mermaid.js'; import {renderMath} from './math.js'; import {renderCodeCopy} from './codecopy.js'; +import {renderAsciinemaPlayer} from './asciicast.js'; import {initMarkupTasklist} from './tasklist.js'; // code that runs for all markup content @@ -8,6 +9,7 @@ export function initMarkupContent() { renderMermaid(); renderMath(); renderCodeCopy(); + renderAsciinemaPlayer(); } // code that only runs for comments diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index 646cf4e60e..7aa42b1f07 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -470,6 +470,10 @@ pre { overflow: auto; } + + .asciicast { + padding: 5px !important; + } } .sidebar { @@ -2900,6 +2904,11 @@ tbody.commit-list { display: inline; } +// but in the repo-files-table we cannot +#repo-files-table .commit-list .message-wrapper { + display: inline-block; +} + @media @mediaSm { tr.commit-list { width: 100%; diff --git a/web_src/less/index.less b/web_src/less/index.less index 2d670ac2d5..185bf7ca31 100644 --- a/web_src/less/index.less +++ b/web_src/less/index.less @@ -13,6 +13,7 @@ @import "./markup/content.less"; @import "./markup/codecopy.less"; @import "./code/linebutton.less"; +@import "./markup/asciicast.less"; @import "./chroma/base.less"; @import "./chroma/light.less"; diff --git a/web_src/less/markup/asciicast.less b/web_src/less/markup/asciicast.less new file mode 100644 index 0000000000..a52b2ae12e --- /dev/null +++ b/web_src/less/markup/asciicast.less @@ -0,0 +1,8 @@ +.asciinema-player-container { + width: 100%; + height: auto; +} + +.asciinema-terminal { + overflow: hidden !important; +}