mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-22 00:03:35 +01:00
Merge branch 'main' into domain_permission_subscriptions
This commit is contained in:
commit
25241ae20b
@ -17,7 +17,7 @@ Documentation is at [docs.gotosocial.org](https://docs.gotosocial.org). You can
|
||||
|
||||
To build from source, check the [CONTRIBUTING.md](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md) file.
|
||||
|
||||
Here's a screenshot of the instance landing page!
|
||||
Here's a screenshot of the instance landing page! Check out the project's [official account](https://gts.superseriousbusiness.org/@gotosocial) running on GoToSocial.
|
||||
|
||||
![Screenshot of the landing page for the GoToSocial instance goblin.technology. It shows basic information about the instance; number of users and posts etc.](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/instancesplash.png)
|
||||
<!--overview-end-->
|
||||
|
@ -4993,7 +4993,7 @@ paths:
|
||||
- description: The code to use for the emoji, which will be used by instance denizens to select it. This must be unique on the instance.
|
||||
in: formData
|
||||
name: shortcode
|
||||
pattern: \w{2,30}
|
||||
pattern: \w{1,30}
|
||||
required: true
|
||||
type: string
|
||||
- description: A png or gif image of the emoji. Animated pngs work too! To ensure compatibility with other fedi implementations, emoji size limit is 50kb by default.
|
||||
@ -5139,7 +5139,7 @@ paths:
|
||||
- description: The code to use for the emoji, which will be used by instance denizens to select it. This must be unique on the instance. Works for the `copy` action type only.
|
||||
in: formData
|
||||
name: shortcode
|
||||
pattern: \w{2,30}
|
||||
pattern: \w{1,30}
|
||||
type: string
|
||||
- description: A new png or gif image to use for the emoji. Animated pngs work too! To ensure compatibility with other fedi implementations, emoji size limit is 50kb by default. Works for LOCAL emojis only.
|
||||
in: formData
|
||||
@ -5859,9 +5859,9 @@ paths:
|
||||
required: true
|
||||
type: string
|
||||
- default: false
|
||||
description: When removing the domain permission draft, also create a domain ignore entry for the target domain, so that drafts will not be created for this domain in the future.
|
||||
description: When removing the domain permission draft, also create a domain exclude entry for the target domain, so that drafts will not be created for this domain in the future.
|
||||
in: formData
|
||||
name: ignore_target
|
||||
name: exclude_target
|
||||
type: boolean
|
||||
produces:
|
||||
- application/json
|
||||
@ -5888,6 +5888,182 @@ paths:
|
||||
summary: Remove a domain permission draft, optionally ignoring all future drafts targeting the given domain.
|
||||
tags:
|
||||
- admin
|
||||
/api/v1/admin/domain_permission_excludes:
|
||||
get:
|
||||
description: |-
|
||||
The excludes will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
|
||||
|
||||
The next and previous queries can be parsed from the returned Link header.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
<https://example.org/api/v1/admin/domain_permission_excludes?limit=20&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/admin/domain_permission_excludes?limit=20&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
||||
````
|
||||
operationId: domainPermissionExcludesGet
|
||||
parameters:
|
||||
- description: Return only excludes that target the given domain.
|
||||
in: query
|
||||
name: domain
|
||||
type: string
|
||||
- description: Return only items *OLDER* than the given max ID (for paging downwards). The item with the specified ID will not be included in the response.
|
||||
in: query
|
||||
name: max_id
|
||||
type: string
|
||||
- description: Return only items *NEWER* than the given since ID. The item with the specified ID will not be included in the response.
|
||||
in: query
|
||||
name: since_id
|
||||
type: string
|
||||
- description: Return only items immediately *NEWER* than the given min ID (for paging upwards). The item with the specified ID will not be included in the response.
|
||||
in: query
|
||||
name: min_id
|
||||
type: string
|
||||
- default: 20
|
||||
description: Number of items to return.
|
||||
in: query
|
||||
maximum: 100
|
||||
minimum: 1
|
||||
name: limit
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Domain permission excludes.
|
||||
headers:
|
||||
Link:
|
||||
description: Links to the next and previous queries.
|
||||
type: string
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/domainPermission'
|
||||
type: array
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"403":
|
||||
description: forbidden
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- admin
|
||||
summary: View domain permission excludes.
|
||||
tags:
|
||||
- admin
|
||||
post:
|
||||
consumes:
|
||||
- multipart/form-data
|
||||
- application/json
|
||||
description: |-
|
||||
Excluded domains (and their subdomains) will not be automatically blocked or allowed when a list of domain permissions is imported or subscribed to.
|
||||
|
||||
You can still manually create domain blocks or domain allows for excluded domains, and any new or existing domain blocks or domain allows for an excluded domain will still be enforced.
|
||||
operationId: domainPermissionExcludeCreate
|
||||
parameters:
|
||||
- description: Domain to create the permission exclude for.
|
||||
in: formData
|
||||
name: domain
|
||||
type: string
|
||||
- description: Private comment about this domain exclude.
|
||||
in: formData
|
||||
name: private_comment
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: The newly created domain permission exclude.
|
||||
schema:
|
||||
$ref: '#/definitions/domainPermission'
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"403":
|
||||
description: forbidden
|
||||
"406":
|
||||
description: not acceptable
|
||||
"409":
|
||||
description: conflict
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- admin
|
||||
summary: Create a domain permission exclude with the given parameters.
|
||||
tags:
|
||||
- admin
|
||||
/api/v1/admin/domain_permission_excludes/{id}:
|
||||
delete:
|
||||
operationId: domainPermissionExcludeDelete
|
||||
parameters:
|
||||
- description: ID of the domain permission exclude.
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: The removed domain permission exclude.
|
||||
schema:
|
||||
$ref: '#/definitions/domainPermission'
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"403":
|
||||
description: forbidden
|
||||
"406":
|
||||
description: not acceptable
|
||||
"409":
|
||||
description: conflict
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- admin
|
||||
summary: Remove a domain permission exclude.
|
||||
tags:
|
||||
- admin
|
||||
get:
|
||||
operationId: domainPermissionExcludeGet
|
||||
parameters:
|
||||
- description: ID of the domain permission exclude.
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Domain permission exclude.
|
||||
schema:
|
||||
$ref: '#/definitions/domainPermission'
|
||||
"401":
|
||||
description: unauthorized
|
||||
"403":
|
||||
description: forbidden
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- admin
|
||||
summary: Get domain permission exclude with the given ID.
|
||||
tags:
|
||||
- admin
|
||||
/api/v1/admin/email/test:
|
||||
post:
|
||||
consumes:
|
||||
|
104
internal/api/client/admin/domainpermissiondraftget.go
Normal file
104
internal/api/client/admin/domainpermissiondraftget.go
Normal file
@ -0,0 +1,104 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
// DomainPermissionDraftGETHandler swagger:operation GET /api/v1/admin/domain_permission_drafts/{id} domainPermissionDraftGet
|
||||
//
|
||||
// Get domain permission draft with the given ID.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// required: true
|
||||
// in: path
|
||||
// description: ID of the domain permission draft.
|
||||
// type: string
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Domain permission draft.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/domainPermission"
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// description: forbidden
|
||||
// '404':
|
||||
// description: not found
|
||||
// '406':
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) DomainPermissionDraftGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
permDraft, errWithCode := m.processor.Admin().DomainPermissionDraftGet(c.Request.Context(), id)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, permDraft)
|
||||
}
|
189
internal/api/client/admin/domainpermissiondraftsget.go
Normal file
189
internal/api/client/admin/domainpermissiondraftsget.go
Normal file
@ -0,0 +1,189 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||
)
|
||||
|
||||
// DomainPermissionDraftsGETHandler swagger:operation GET /api/v1/admin/domain_permission_drafts domainPermissionDraftsGet
|
||||
//
|
||||
// View domain permission drafts.
|
||||
//
|
||||
// The drafts will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
|
||||
//
|
||||
// The next and previous queries can be parsed from the returned Link header.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// ```
|
||||
// <https://example.org/api/v1/admin/domain_permission_drafts?limit=20&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/admin/domain_permission_drafts?limit=20&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
||||
// ````
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: subscription_id
|
||||
// type: string
|
||||
// description: Show only drafts created by the given subscription ID.
|
||||
// in: query
|
||||
// -
|
||||
// name: domain
|
||||
// type: string
|
||||
// description: Return only drafts that target the given domain.
|
||||
// in: query
|
||||
// -
|
||||
// name: permission_type
|
||||
// type: string
|
||||
// description: Filter on "block" or "allow" type drafts.
|
||||
// in: query
|
||||
// -
|
||||
// name: max_id
|
||||
// type: string
|
||||
// description: >-
|
||||
// Return only items *OLDER* than the given max ID (for paging downwards).
|
||||
// The item with the specified ID will not be included in the response.
|
||||
// in: query
|
||||
// -
|
||||
// name: since_id
|
||||
// type: string
|
||||
// description: >-
|
||||
// Return only items *NEWER* than the given since ID.
|
||||
// The item with the specified ID will not be included in the response.
|
||||
// in: query
|
||||
// -
|
||||
// name: min_id
|
||||
// type: string
|
||||
// description: >-
|
||||
// Return only items immediately *NEWER* than the given min ID (for paging upwards).
|
||||
// The item with the specified ID will not be included in the response.
|
||||
// in: query
|
||||
// -
|
||||
// name: limit
|
||||
// type: integer
|
||||
// description: Number of items to return.
|
||||
// default: 20
|
||||
// minimum: 1
|
||||
// maximum: 100
|
||||
// in: query
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Domain permission drafts.
|
||||
// schema:
|
||||
// type: array
|
||||
// items:
|
||||
// "$ref": "#/definitions/domainPermission"
|
||||
// headers:
|
||||
// Link:
|
||||
// type: string
|
||||
// description: Links to the next and previous queries.
|
||||
// '400':
|
||||
// description: bad request
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// description: forbidden
|
||||
// '404':
|
||||
// description: not found
|
||||
// '406':
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) DomainPermissionDraftsGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
permType := c.Query(apiutil.DomainPermissionPermTypeKey)
|
||||
switch permType {
|
||||
case "", "block", "allow":
|
||||
// No problem.
|
||||
|
||||
default:
|
||||
// Invalid.
|
||||
text := fmt.Sprintf(
|
||||
"permission_type %s not recognized, valid values are empty string, block, or allow",
|
||||
permType,
|
||||
)
|
||||
errWithCode := gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
page, errWithCode := paging.ParseIDPage(c, 1, 200, 20)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().DomainPermissionDraftsGet(
|
||||
c.Request.Context(),
|
||||
c.Query(apiutil.DomainPermissionSubscriptionIDKey),
|
||||
c.Query(apiutil.DomainPermissionDomainKey),
|
||||
gtsmodel.NewDomainPermissionType(permType),
|
||||
page,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.LinkHeader != "" {
|
||||
c.Header("Link", resp.LinkHeader)
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp.Items)
|
||||
}
|
104
internal/api/client/admin/domainpermissionexcludeget.go
Normal file
104
internal/api/client/admin/domainpermissionexcludeget.go
Normal file
@ -0,0 +1,104 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
// DomainPermissionExcludeGETHandler swagger:operation GET /api/v1/admin/domain_permission_excludes/{id} domainPermissionExcludeGet
|
||||
//
|
||||
// Get domain permission exclude with the given ID.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// required: true
|
||||
// in: path
|
||||
// description: ID of the domain permission exclude.
|
||||
// type: string
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Domain permission exclude.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/domainPermission"
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// description: forbidden
|
||||
// '404':
|
||||
// description: not found
|
||||
// '406':
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) DomainPermissionExcludeGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
permExclude, errWithCode := m.processor.Admin().DomainPermissionExcludeGet(c.Request.Context(), id)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, permExclude)
|
||||
}
|
159
internal/api/client/admin/domainpermissionexcludesget.go
Normal file
159
internal/api/client/admin/domainpermissionexcludesget.go
Normal file
@ -0,0 +1,159 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||
)
|
||||
|
||||
// DomainPermissionExcludesGETHandler swagger:operation GET /api/v1/admin/domain_permission_excludes domainPermissionExcludesGet
|
||||
//
|
||||
// View domain permission excludes.
|
||||
//
|
||||
// The excludes will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
|
||||
//
|
||||
// The next and previous queries can be parsed from the returned Link header.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// ```
|
||||
// <https://example.org/api/v1/admin/domain_permission_excludes?limit=20&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/admin/domain_permission_excludes?limit=20&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
||||
// ````
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: domain
|
||||
// type: string
|
||||
// description: Return only excludes that target the given domain.
|
||||
// in: query
|
||||
// -
|
||||
// name: max_id
|
||||
// type: string
|
||||
// description: >-
|
||||
// Return only items *OLDER* than the given max ID (for paging downwards).
|
||||
// The item with the specified ID will not be included in the response.
|
||||
// in: query
|
||||
// -
|
||||
// name: since_id
|
||||
// type: string
|
||||
// description: >-
|
||||
// Return only items *NEWER* than the given since ID.
|
||||
// The item with the specified ID will not be included in the response.
|
||||
// in: query
|
||||
// -
|
||||
// name: min_id
|
||||
// type: string
|
||||
// description: >-
|
||||
// Return only items immediately *NEWER* than the given min ID (for paging upwards).
|
||||
// The item with the specified ID will not be included in the response.
|
||||
// in: query
|
||||
// -
|
||||
// name: limit
|
||||
// type: integer
|
||||
// description: Number of items to return.
|
||||
// default: 20
|
||||
// minimum: 1
|
||||
// maximum: 100
|
||||
// in: query
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Domain permission excludes.
|
||||
// schema:
|
||||
// type: array
|
||||
// items:
|
||||
// "$ref": "#/definitions/domainPermission"
|
||||
// headers:
|
||||
// Link:
|
||||
// type: string
|
||||
// description: Links to the next and previous queries.
|
||||
// '400':
|
||||
// description: bad request
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// description: forbidden
|
||||
// '404':
|
||||
// description: not found
|
||||
// '406':
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) DomainPermissionExcludesGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
page, errWithCode := paging.ParseIDPage(c, 1, 200, 20)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().DomainPermissionExcludesGet(
|
||||
c.Request.Context(),
|
||||
c.Query(apiutil.DomainPermissionDomainKey),
|
||||
page,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.LinkHeader != "" {
|
||||
c.Header("Link", resp.LinkHeader)
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp.Items)
|
||||
}
|
@ -53,7 +53,7 @@
|
||||
// The code to use for the emoji, which will be used by instance denizens to select it.
|
||||
// This must be unique on the instance.
|
||||
// type: string
|
||||
// pattern: \w{2,30}
|
||||
// pattern: \w{1,30}
|
||||
// required: true
|
||||
// -
|
||||
// name: image
|
||||
|
@ -85,7 +85,7 @@
|
||||
// The code to use for the emoji, which will be used by instance denizens to select it.
|
||||
// This must be unique on the instance. Works for the `copy` action type only.
|
||||
// type: string
|
||||
// pattern: \w{2,30}
|
||||
// pattern: \w{1,30}
|
||||
// -
|
||||
// name: image
|
||||
// in: formData
|
||||
|
@ -560,7 +560,7 @@ func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyEmptyShortcode() {
|
||||
b, err := io.ReadAll(result.Body)
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{"error":"Bad Request: shortcode did not pass validation, must be between 2 and 30 characters, letters, numbers, and underscores only"}`, string(b))
|
||||
suite.Equal(`{"error":"Bad Request: shortcode did not pass validation, must be between 1 and 30 characters, letters, numbers, and underscores only"}`, string(b))
|
||||
}
|
||||
|
||||
func (suite *EmojiUpdateTestSuite) TestEmojiUpdateCopyNoShortcode() {
|
||||
|
@ -155,7 +155,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 20,
|
||||
"status_count": 19,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/assets/logo.webp",
|
||||
@ -296,7 +296,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 20,
|
||||
"status_count": 19,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/assets/logo.webp",
|
||||
@ -437,7 +437,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 20,
|
||||
"status_count": 19,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/assets/logo.webp",
|
||||
@ -629,7 +629,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() {
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 20,
|
||||
"status_count": 19,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/assets/logo.webp",
|
||||
@ -792,7 +792,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() {
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 20,
|
||||
"status_count": 19,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/original/`+instanceAccount.AvatarMediaAttachment.ID+`.gif",`+`
|
||||
@ -974,7 +974,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() {
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 20,
|
||||
"status_count": 19,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/assets/logo.webp",
|
||||
|
120
internal/db/bundb/domainpermissiondraft_test.go
Normal file
120
internal/db/bundb/domainpermissiondraft_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package bundb_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
type DomainPermissionDraftTestSuite struct {
|
||||
BunDBStandardTestSuite
|
||||
}
|
||||
|
||||
func (suite *DomainPermissionDraftTestSuite) TestPermDraftCreateGetDelete() {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
draft = >smodel.DomainPermissionDraft{
|
||||
ID: "01JCZN614XG85GCGAMSV9ZZAEJ",
|
||||
PermissionType: gtsmodel.DomainPermissionBlock,
|
||||
Domain: "exämple.org",
|
||||
CreatedByAccountID: suite.testAccounts["admin_account"].ID,
|
||||
PrivateComment: "this domain is poo",
|
||||
PublicComment: "this domain is poo, but phrased in a more outward-facing way",
|
||||
Obfuscate: util.Ptr(false),
|
||||
SubscriptionID: "01JCZN8PG55KKEVTDAY52D0T3P",
|
||||
}
|
||||
)
|
||||
|
||||
// Whack the draft in.
|
||||
if err := suite.state.DB.PutDomainPermissionDraft(ctx, draft); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Get the draft again.
|
||||
dbDraft, err := suite.state.DB.GetDomainPermissionDraftByID(ctx, draft.ID)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Domain should have been stored punycoded.
|
||||
suite.Equal("xn--exmple-cua.org", dbDraft.Domain)
|
||||
|
||||
// Search for domain using both
|
||||
// punycode and unicode variants.
|
||||
search1, err := suite.state.DB.GetDomainPermissionDrafts(
|
||||
ctx,
|
||||
gtsmodel.DomainPermissionUnknown,
|
||||
"",
|
||||
"exämple.org",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
if len(search1) != 1 {
|
||||
suite.FailNow("couldn't get domain perm draft exämple.org")
|
||||
}
|
||||
|
||||
search2, err := suite.state.DB.GetDomainPermissionDrafts(
|
||||
ctx,
|
||||
gtsmodel.DomainPermissionUnknown,
|
||||
"",
|
||||
"xn--exmple-cua.org",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
if len(search2) != 1 {
|
||||
suite.FailNow("couldn't get domain perm draft example.org")
|
||||
}
|
||||
|
||||
// Change ID + try to put the same draft again.
|
||||
draft.ID = "01JCZNVYSDT3JE385FABMJ7ADQ"
|
||||
err = suite.state.DB.PutDomainPermissionDraft(ctx, draft)
|
||||
if !errors.Is(err, db.ErrAlreadyExists) {
|
||||
suite.FailNow("was able to insert same domain perm draft twice")
|
||||
}
|
||||
|
||||
// Put same draft but change permission type, should work.
|
||||
draft.PermissionType = gtsmodel.DomainPermissionAllow
|
||||
if err := suite.state.DB.PutDomainPermissionDraft(ctx, draft); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Delete both drafts.
|
||||
for _, id := range []string{
|
||||
"01JCZN614XG85GCGAMSV9ZZAEJ",
|
||||
"01JCZNVYSDT3JE385FABMJ7ADQ",
|
||||
} {
|
||||
if err := suite.state.DB.DeleteDomainPermissionDraft(ctx, id); err != nil {
|
||||
suite.FailNow("error deleting domain permission draft")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainPermissionDraftTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(DomainPermissionDraftTestSuite))
|
||||
}
|
@ -22,7 +22,6 @@
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
@ -65,14 +64,6 @@ func (d *domainDB) IsDomainPermissionExcluded(ctx context.Context, domain string
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if our host and given domain are equal
|
||||
// or part of the same second-level domain; we
|
||||
// always exclude such perms as creating blocks
|
||||
// or allows in such cases may break things.
|
||||
if dns.CompareDomainName(domain, config.GetHost()) >= 2 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Func to scan list of all
|
||||
// excluded domain perms from DB.
|
||||
loadF := func() ([]string, error) {
|
||||
@ -80,12 +71,16 @@ func (d *domainDB) IsDomainPermissionExcluded(ctx context.Context, domain string
|
||||
|
||||
if err := d.db.
|
||||
NewSelect().
|
||||
Table("domain_excludes").
|
||||
Table("domain_permission_excludes").
|
||||
Column("domain").
|
||||
Scan(ctx, &domains); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Exclude our own domain as creating blocks
|
||||
// or allows for self will likely break things.
|
||||
domains = append(domains, config.GetHost())
|
||||
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
|
185
internal/db/bundb/domainpermissionexclude_test.go
Normal file
185
internal/db/bundb/domainpermissionexclude_test.go
Normal file
@ -0,0 +1,185 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package bundb_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
type DomainPermissionExcludeTestSuite struct {
|
||||
BunDBStandardTestSuite
|
||||
}
|
||||
|
||||
func (suite *DomainPermissionExcludeTestSuite) TestPermExcludeCreateGetDelete() {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
exclude = >smodel.DomainPermissionExclude{
|
||||
ID: "01JCZN614XG85GCGAMSV9ZZAEJ",
|
||||
Domain: "exämple.org",
|
||||
CreatedByAccountID: suite.testAccounts["admin_account"].ID,
|
||||
PrivateComment: "this domain is poo",
|
||||
}
|
||||
)
|
||||
|
||||
// Whack the exclude in.
|
||||
if err := suite.state.DB.PutDomainPermissionExclude(ctx, exclude); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Get the exclude again.
|
||||
dbExclude, err := suite.state.DB.GetDomainPermissionExcludeByID(ctx, exclude.ID)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Domain should have been stored punycoded.
|
||||
suite.Equal("xn--exmple-cua.org", dbExclude.Domain)
|
||||
|
||||
// Search for domain using both
|
||||
// punycode and unicode variants.
|
||||
search1, err := suite.state.DB.GetDomainPermissionExcludes(
|
||||
ctx,
|
||||
"exämple.org",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
if len(search1) != 1 {
|
||||
suite.FailNow("couldn't get domain perm exclude exämple.org")
|
||||
}
|
||||
|
||||
search2, err := suite.state.DB.GetDomainPermissionExcludes(
|
||||
ctx,
|
||||
"xn--exmple-cua.org",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
if len(search2) != 1 {
|
||||
suite.FailNow("couldn't get domain perm exclude example.org")
|
||||
}
|
||||
|
||||
// Change ID + try to put the same exclude again.
|
||||
exclude.ID = "01JCZNVYSDT3JE385FABMJ7ADQ"
|
||||
err = suite.state.DB.PutDomainPermissionExclude(ctx, exclude)
|
||||
if !errors.Is(err, db.ErrAlreadyExists) {
|
||||
suite.FailNow("was able to insert same domain perm exclude twice")
|
||||
}
|
||||
|
||||
// Delete both excludes.
|
||||
for _, id := range []string{
|
||||
"01JCZN614XG85GCGAMSV9ZZAEJ",
|
||||
"01JCZNVYSDT3JE385FABMJ7ADQ",
|
||||
} {
|
||||
if err := suite.state.DB.DeleteDomainPermissionExclude(ctx, id); err != nil {
|
||||
suite.FailNow("error deleting domain permission exclude")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *DomainPermissionExcludeTestSuite) TestExcluded() {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
createdByAccountID = suite.testAccounts["admin_account"].ID
|
||||
)
|
||||
|
||||
// Insert some excludes into the db.
|
||||
for _, exclude := range []*gtsmodel.DomainPermissionExclude{
|
||||
{
|
||||
ID: "01JD7AFFBBZSPY8R2M0JCGQGPW",
|
||||
Domain: "example.org",
|
||||
CreatedByAccountID: createdByAccountID,
|
||||
},
|
||||
{
|
||||
ID: "01JD7AMK98E2QX78KXEZJ1RF5Z",
|
||||
Domain: "boobs.com",
|
||||
CreatedByAccountID: createdByAccountID,
|
||||
},
|
||||
{
|
||||
ID: "01JD7AMXW3R3W98E91R62ACDA0",
|
||||
Domain: "rad.boobs.com",
|
||||
CreatedByAccountID: createdByAccountID,
|
||||
},
|
||||
{
|
||||
ID: "01JD7AYYN5TXQVASB30PT08CE1",
|
||||
Domain: "honkers.org",
|
||||
CreatedByAccountID: createdByAccountID,
|
||||
},
|
||||
} {
|
||||
if err := suite.state.DB.PutDomainPermissionExclude(ctx, exclude); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
domain string
|
||||
excluded bool
|
||||
}
|
||||
|
||||
for i, testCase := range []testCase{
|
||||
{
|
||||
domain: config.GetHost(),
|
||||
excluded: true,
|
||||
},
|
||||
{
|
||||
domain: "test.example.org",
|
||||
excluded: true,
|
||||
},
|
||||
{
|
||||
domain: "example.org",
|
||||
excluded: true,
|
||||
},
|
||||
{
|
||||
domain: "boobs.com",
|
||||
excluded: true,
|
||||
},
|
||||
{
|
||||
domain: "rad.boobs.com",
|
||||
excluded: true,
|
||||
},
|
||||
{
|
||||
domain: "sir.not.appearing.in.this.list",
|
||||
excluded: false,
|
||||
},
|
||||
} {
|
||||
excluded, err := suite.state.DB.IsDomainPermissionExcluded(ctx, testCase.domain)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
if excluded != testCase.excluded {
|
||||
suite.Failf("",
|
||||
"test %d: %s excluded should be %t",
|
||||
i, testCase.domain, testCase.excluded,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainPermissionExcludeTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(DomainPermissionExcludeTestSuite))
|
||||
}
|
@ -103,6 +103,9 @@ func (i *instanceDB) CountInstanceStatuses(ctx context.Context, domain string) (
|
||||
// Ignore statuses that are currently pending approval.
|
||||
q = q.Where("NOT ? = ?", bun.Ident("status.pending_approval"), true)
|
||||
|
||||
// Ignore statuses that are direct messages.
|
||||
q = q.Where("NOT ? = ?", bun.Ident("status.visibility"), "direct")
|
||||
|
||||
count, err := q.Count(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -47,7 +47,7 @@ func (suite *InstanceTestSuite) TestCountInstanceUsersRemote() {
|
||||
func (suite *InstanceTestSuite) TestCountInstanceStatuses() {
|
||||
count, err := suite.db.CountInstanceStatuses(context.Background(), config.GetHost())
|
||||
suite.NoError(err)
|
||||
suite.Equal(20, count)
|
||||
suite.Equal(19, count)
|
||||
}
|
||||
|
||||
func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() {
|
||||
|
@ -0,0 +1,82 @@
|
||||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func init() {
|
||||
up := func(ctx context.Context, db *bun.DB) error {
|
||||
return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
|
||||
// Create `domain_permission_drafts`.
|
||||
if _, err := tx.
|
||||
NewCreateTable().
|
||||
Model((*gtsmodel.DomainPermissionDraft)(nil)).
|
||||
IfNotExists().
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create `domain_permission_ignores`.
|
||||
if _, err := tx.
|
||||
NewCreateTable().
|
||||
Model((*gtsmodel.DomainPermissionExclude)(nil)).
|
||||
IfNotExists().
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create indexes. Indices. Indie sexes.
|
||||
for table, indexes := range map[string]map[string][]string{
|
||||
"domain_permission_drafts": {
|
||||
"domain_permission_drafts_domain_idx": {"domain"},
|
||||
"domain_permission_drafts_subscription_id_idx": {"subscription_id"},
|
||||
},
|
||||
} {
|
||||
for index, columns := range indexes {
|
||||
if _, err := tx.
|
||||
NewCreateIndex().
|
||||
Table(table).
|
||||
Index(index).
|
||||
Column(columns...).
|
||||
IfNotExists().
|
||||
Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
down := func(ctx context.Context, db *bun.DB) error {
|
||||
return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := Migrations.Register(up, down); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -130,6 +130,9 @@ type Domain interface {
|
||||
// DeleteDomainPermissionExclude deletes one DomainPermissionExclude with the given id.
|
||||
DeleteDomainPermissionExclude(ctx context.Context, id string) error
|
||||
|
||||
// IsDomainPermissionExcluded returns true if the given domain matches in the list of excluded domains.
|
||||
IsDomainPermissionExcluded(ctx context.Context, domain string) (bool, error)
|
||||
|
||||
/*
|
||||
Domain permission subscription stuff.
|
||||
*/
|
||||
|
@ -249,38 +249,37 @@ func (p *Processor) DomainPermissionDraftAccept(
|
||||
deleteDraft()
|
||||
|
||||
return new, actionID, errWithCode
|
||||
} else {
|
||||
// Domain permission exists but we should overwrite
|
||||
// it by just updating the existing domain permission.
|
||||
// Domain can't change, so no need to re-run side effects.
|
||||
existing.SetCreatedByAccountID(permDraft.CreatedByAccountID)
|
||||
existing.SetCreatedByAccount(permDraft.CreatedByAccount)
|
||||
existing.SetPrivateComment(permDraft.PrivateComment)
|
||||
existing.SetPublicComment(permDraft.PublicComment)
|
||||
existing.SetObfuscate(permDraft.Obfuscate)
|
||||
existing.SetSubscriptionID(permDraft.SubscriptionID)
|
||||
|
||||
var err error
|
||||
switch dp := existing.(type) {
|
||||
case *gtsmodel.DomainBlock:
|
||||
err = p.state.DB.UpdateDomainBlock(ctx, dp)
|
||||
|
||||
case *gtsmodel.DomainAllow:
|
||||
err = p.state.DB.UpdateDomainAllow(ctx, dp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err := gtserror.Newf("db error updating existing domain permission: %w", err)
|
||||
return nil, "", gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Clean up the draft
|
||||
// before returning.
|
||||
deleteDraft()
|
||||
|
||||
apiPerm, errWithCode := p.apiDomainPerm(ctx, existing, false)
|
||||
return apiPerm, "", errWithCode
|
||||
}
|
||||
|
||||
// Domain permission exists but we should overwrite
|
||||
// it by just updating the existing domain permission.
|
||||
// Domain can't change, so no need to re-run side effects.
|
||||
existing.SetCreatedByAccountID(permDraft.CreatedByAccountID)
|
||||
existing.SetCreatedByAccount(permDraft.CreatedByAccount)
|
||||
existing.SetPrivateComment(permDraft.PrivateComment)
|
||||
existing.SetPublicComment(permDraft.PublicComment)
|
||||
existing.SetObfuscate(permDraft.Obfuscate)
|
||||
existing.SetSubscriptionID(permDraft.SubscriptionID)
|
||||
|
||||
switch dp := existing.(type) {
|
||||
case *gtsmodel.DomainBlock:
|
||||
err = p.state.DB.UpdateDomainBlock(ctx, dp)
|
||||
|
||||
case *gtsmodel.DomainAllow:
|
||||
err = p.state.DB.UpdateDomainAllow(ctx, dp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err := gtserror.Newf("db error updating existing domain permission: %w", err)
|
||||
return nil, "", gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Clean up the draft
|
||||
// before returning.
|
||||
deleteDraft()
|
||||
|
||||
apiPerm, errWithCode := p.apiDomainPerm(ctx, existing, false)
|
||||
return apiPerm, "", errWithCode
|
||||
}
|
||||
|
||||
func (p *Processor) DomainPermissionDraftRemove(
|
||||
|
@ -46,7 +46,7 @@
|
||||
domainGrp = `(?:` + alphaNumeric + `|\.|\-|\:)` // Non-capturing group that matches against a single valid domain character.
|
||||
mentionName = `^@(` + usernameGrp + `+)(?:@(` + domainGrp + `+))?$` // Extract parts of one mention, maybe including domain.
|
||||
mentionFinder = `(?:^|\s)(@` + usernameGrp + `+(?:@` + domainGrp + `+)?)` // Extract all mentions from a text, each mention may include domain.
|
||||
emojiShortcode = `\w{2,30}` // Pattern for emoji shortcodes. maximumEmojiShortcodeLength = 30
|
||||
emojiShortcode = `\w{1,30}` // Pattern for emoji shortcodes. maximumEmojiShortcodeLength = 30
|
||||
emojiFinder = `(?:\b)?:(` + emojiShortcode + `):(?:\b)?` // Extract all emoji shortcodes from a text.
|
||||
emojiValidator = `^` + emojiShortcode + `$` // Validate a single emoji shortcode.
|
||||
usernameStrict = `^[a-z0-9_]{1,64}$` // Pattern for usernames on THIS instance. maximumUsernameLength = 64
|
||||
|
@ -1993,7 +1993,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() {
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 20,
|
||||
"status_count": 19,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/assets/logo.webp",
|
||||
|
@ -190,11 +190,11 @@ func CustomCSS(customCSS string) error {
|
||||
}
|
||||
|
||||
// EmojiShortcode just runs the given shortcode through the regular expression
|
||||
// for emoji shortcodes, to figure out whether it's a valid shortcode, ie., 2-30 characters,
|
||||
// for emoji shortcodes, to figure out whether it's a valid shortcode, ie., 1-30 characters,
|
||||
// a-zA-Z, numbers, and underscores.
|
||||
func EmojiShortcode(shortcode string) error {
|
||||
if !regexes.EmojiValidator.MatchString(shortcode) {
|
||||
return fmt.Errorf("shortcode %s did not pass validation, must be between 2 and 30 characters, letters, numbers, and underscores only", shortcode)
|
||||
return fmt.Errorf("shortcode %s did not pass validation, must be between 1 and 30 characters, letters, numbers, and underscores only", shortcode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ type testStruct struct {
|
||||
},
|
||||
{
|
||||
shortcode: "p",
|
||||
ok: false,
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
shortcode: "pp",
|
||||
@ -361,6 +361,10 @@ type testStruct struct {
|
||||
},
|
||||
{
|
||||
shortcode: "_",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
shortcode: "",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
|
@ -34,6 +34,7 @@ EXPECT=$(cat << "EOF"
|
||||
"client-mem-ratio": 0.1,
|
||||
"conversation-last-status-ids-mem-ratio": 2,
|
||||
"conversation-mem-ratio": 1,
|
||||
"domain-permission-draft-mem-ratio": 0.5,
|
||||
"emoji-category-mem-ratio": 0.1,
|
||||
"emoji-mem-ratio": 3,
|
||||
"filter-keyword-mem-ratio": 0.5,
|
||||
|
@ -1403,7 +1403,8 @@ button.tab-button {
|
||||
}
|
||||
}
|
||||
|
||||
.domain-permission-draft-details {
|
||||
.domain-permission-draft-details,
|
||||
.domain-permission-exclude-details {
|
||||
.info-list {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ export default function NewEmojiForm() {
|
||||
label="Shortcode, must be unique among the instance's local emoji"
|
||||
autoCapitalize="none"
|
||||
spellCheck="false"
|
||||
{...{pattern: "^\\w{2,30}$"}}
|
||||
{...{pattern: "^\\w{1,30}$"}}
|
||||
/>
|
||||
|
||||
<CategorySelect
|
||||
|
@ -22,7 +22,7 @@ import { useMemo } from "react";
|
||||
import { useTextInput } from "../../../../lib/form";
|
||||
import { useListEmojiQuery } from "../../../../lib/query/admin/custom-emoji";
|
||||
|
||||
const shortcodeRegex = /^\w{2,30}$/;
|
||||
const shortcodeRegex = /^\w{1,30}$/;
|
||||
|
||||
export default function useShortcode() {
|
||||
const { data: emoji = [] } = useListEmojiQuery({
|
||||
@ -42,8 +42,8 @@ export default function useShortcode() {
|
||||
return "Shortcode already in use";
|
||||
}
|
||||
|
||||
if (code.length < 2 || code.length > 30) {
|
||||
return "Shortcode must be between 2 and 30 characters";
|
||||
if (code.length < 1 || code.length > 30) {
|
||||
return "Shortcode must be between 1 and 30 characters";
|
||||
}
|
||||
|
||||
if (!shortcodeRegex.test(code)) {
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
export function DomainPermissionDraftHelpText() {
|
||||
return (
|
||||
<>
|
||||
Domain permission drafts are domain block or domain allow entries that are not yet in force.
|
||||
<br/>
|
||||
You can choose to accept or remove a draft.
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function DomainPermissionDraftDocsLink() {
|
||||
return (
|
||||
<a
|
||||
href="https://docs.gotosocial.org/en/latest/admin/settings/#domain-permission-drafts"
|
||||
target="_blank"
|
||||
className="docslink"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn more about domain permission drafts (opens in a new tab)
|
||||
</a>
|
||||
);
|
||||
}
|
@ -29,6 +29,7 @@ import { Error as ErrorC } from "../../../../components/error";
|
||||
import { Select, TextInput } from "../../../../components/form/inputs";
|
||||
import { formDomainValidator } from "../../../../lib/util/formvalidators";
|
||||
import { useCapitalize } from "../../../../lib/util";
|
||||
import { DomainPermissionDraftDocsLink, DomainPermissionDraftHelpText } from "./common";
|
||||
|
||||
export default function DomainPermissionDraftsSearch() {
|
||||
return (
|
||||
@ -38,10 +39,9 @@ export default function DomainPermissionDraftsSearch() {
|
||||
<p>
|
||||
You can use the form below to search through domain permission drafts.
|
||||
<br/>
|
||||
Domain permission drafts are domain block or domain allow entries that are not yet in force.
|
||||
<br/>
|
||||
You can choose to accept or remove a draft.
|
||||
<DomainPermissionDraftHelpText />
|
||||
</p>
|
||||
<DomainPermissionDraftDocsLink />
|
||||
</div>
|
||||
<DomainPermissionDraftsSearchForm />
|
||||
</div>
|
||||
|
@ -25,6 +25,7 @@ import { formDomainValidator } from "../../../../lib/util/formvalidators";
|
||||
import MutationButton from "../../../../components/form/mutation-button";
|
||||
import { Checkbox, RadioGroup, TextArea, TextInput } from "../../../../components/form/inputs";
|
||||
import { useLocation } from "wouter";
|
||||
import { DomainPermissionDraftDocsLink, DomainPermissionDraftHelpText } from "./common";
|
||||
|
||||
export default function DomainPermissionDraftNew() {
|
||||
const [ _location, setLocation ] = useLocation();
|
||||
@ -67,13 +68,8 @@ export default function DomainPermissionDraftNew() {
|
||||
>
|
||||
<div className="form-section-docs">
|
||||
<h2>New Domain Permission Draft</h2>
|
||||
<p>
|
||||
You can use the form below to create a new domain permission draft.
|
||||
<br/>
|
||||
Domain permission drafts are domain block or domain allow entries that are not yet in force.
|
||||
<br/>
|
||||
You can choose to accept or remove a draft.
|
||||
</p>
|
||||
<p><DomainPermissionDraftHelpText /></p>
|
||||
<DomainPermissionDraftDocsLink />
|
||||
</div>
|
||||
|
||||
<RadioGroup
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
GoToSocial
|
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
export function DomainPermissionExcludeHelpText() {
|
||||
return (
|
||||
<>
|
||||
Domain permission excludes prevent permissions for a domain (and all
|
||||
subdomains) from being auomatically managed by domain permission subscriptions.
|
||||
<br/>
|
||||
For example, if you create an exclude entry for <code>example.org</code>, then
|
||||
a blocklist or allowlist subscription will <em>exclude</em> entries for <code>example.org</code>
|
||||
and any of its subdomains (<code>sub.example.org</code>, <code>another.sub.example.org</code> etc.)
|
||||
when creating domain permission drafts and domain blocks/allows.
|
||||
<br/>
|
||||
This functionality allows you to manually manage permissions for excluded domains,
|
||||
in cases where you know you definitely do or don't want to federate with a given domain,
|
||||
no matter what entries are contained in a domain permission subscription.
|
||||
<br/>
|
||||
Note that by itself, creation of an exclude entry for a given domain does not affect
|
||||
federation with that domain at all, it is only useful in combination with permission subscriptions.
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function DomainPermissionExcludeDocsLink() {
|
||||
return (
|
||||
<a
|
||||
href="https://docs.gotosocial.org/en/latest/admin/settings/#domain-permission-excludes"
|
||||
target="_blank"
|
||||
className="docslink"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn more about domain permission excludes (opens in a new tab)
|
||||
</a>
|
||||
);
|
||||
}
|
@ -18,20 +18,21 @@
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { useParams } from "wouter";
|
||||
import { useLocation, useParams } from "wouter";
|
||||
import Loading from "../../../../components/loading";
|
||||
import { useBaseUrl } from "../../../../lib/navigation/util";
|
||||
import BackButton from "../../../../components/back-button";
|
||||
import { Error as ErrorC } from "../../../../components/error";
|
||||
import UsernameLozenge from "../../../../components/username-lozenge";
|
||||
import { useGetDomainPermissionExcludeQuery } from "../../../../lib/query/admin/domain-permissions/excludes";
|
||||
import { useDeleteDomainPermissionExcludeMutation, useGetDomainPermissionExcludeQuery } from "../../../../lib/query/admin/domain-permissions/excludes";
|
||||
import MutationButton from "../../../../components/form/mutation-button";
|
||||
|
||||
export default function DomainPermissionExcludeDetail() {
|
||||
const baseUrl = useBaseUrl();
|
||||
const backLocation: string = history.state?.backLocation ?? `~${baseUrl}`;
|
||||
const params = useParams();
|
||||
|
||||
let id = params.permExcludeId as string | undefined;
|
||||
const params = useParams();
|
||||
let id = params.excludeId as string | undefined;
|
||||
if (!id) {
|
||||
throw "no perm ID";
|
||||
}
|
||||
@ -54,13 +55,7 @@ export default function DomainPermissionExcludeDetail() {
|
||||
|
||||
const created = permExclude.created_at ? new Date(permExclude.created_at).toDateString(): "unknown";
|
||||
const domain = permExclude.domain;
|
||||
const permType = permExclude.permission_type;
|
||||
if (!permType) {
|
||||
return <ErrorC error={new Error("permission_type was undefined")} />;
|
||||
}
|
||||
const publicComment = permExclude.public_comment ?? "[none]";
|
||||
const privateComment = permExclude.private_comment ?? "[none]";
|
||||
const subscriptionID = permExclude.subscription_id ?? "[none]";
|
||||
|
||||
return (
|
||||
<div className="domain-permission-exclude-details">
|
||||
@ -84,37 +79,41 @@ export default function DomainPermissionExcludeDetail() {
|
||||
<dt>Domain</dt>
|
||||
<dd>{domain}</dd>
|
||||
</div>
|
||||
<div className="info-list-entry">
|
||||
<dt>Permission type</dt>
|
||||
<dd className={`permission-type ${permType}`}>
|
||||
<i
|
||||
aria-hidden={true}
|
||||
className={`fa fa-${permType === "allow" ? "check" : "close"}`}
|
||||
></i>
|
||||
{permType}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="info-list-entry">
|
||||
<dt>Private comment</dt>
|
||||
<dd>{privateComment}</dd>
|
||||
</div>
|
||||
<div className="info-list-entry">
|
||||
<dt>Public comment</dt>
|
||||
<dd>{publicComment}</dd>
|
||||
</div>
|
||||
<div className="info-list-entry">
|
||||
<dt>Subscription ID</dt>
|
||||
<dd>{subscriptionID}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<HandleExclude
|
||||
id={id}
|
||||
backLocation={backLocation}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HandleExclude({ id, backLocation }: { id: string, backLocation: string }) {
|
||||
return <></>;
|
||||
function HandleExclude({ id, backLocation}: {id: string, backLocation: string}) {
|
||||
const [_location, setLocation] = useLocation();
|
||||
const [ deleteExclude, deleteResult ] = useDeleteDomainPermissionExcludeMutation();
|
||||
|
||||
return (
|
||||
<MutationButton
|
||||
label={`Delete exclude`}
|
||||
title={`Delete exclude`}
|
||||
type="button"
|
||||
className="button danger"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
deleteExclude(id).then(res => {
|
||||
if ("data" in res) {
|
||||
setLocation(backLocation);
|
||||
}
|
||||
});
|
||||
}}
|
||||
disabled={false}
|
||||
showError={true}
|
||||
result={deleteResult}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import { DomainPerm } from "../../../../lib/types/domain-permission";
|
||||
import { Error as ErrorC } from "../../../../components/error";
|
||||
import { Select, TextInput } from "../../../../components/form/inputs";
|
||||
import { formDomainValidator } from "../../../../lib/util/formvalidators";
|
||||
import { DomainPermissionExcludeDocsLink, DomainPermissionExcludeHelpText } from "./common";
|
||||
|
||||
export default function DomainPermissionExcludesSearch() {
|
||||
return (
|
||||
@ -37,10 +38,9 @@ export default function DomainPermissionExcludesSearch() {
|
||||
<p>
|
||||
You can use the form below to search through domain permission excludes.
|
||||
<br/>
|
||||
Domain permission excludes are domain block or domain allow entries that are not yet in force.
|
||||
<br/>
|
||||
You can choose to accept or remove a exclude.
|
||||
<DomainPermissionExcludeHelpText />
|
||||
</p>
|
||||
<DomainPermissionExcludeDocsLink />
|
||||
</div>
|
||||
<DomainPermissionExcludesSearchForm />
|
||||
</div>
|
||||
@ -204,7 +204,6 @@ function ExcludeListEntry({ permExclude, linkTo, backLocation }: ExcludeEntryPro
|
||||
role="link"
|
||||
tabIndex={0}
|
||||
>
|
||||
<h3>{`Exclude ${domain}`}</h3>
|
||||
<dl className="info-list">
|
||||
<div className="info-list-entry">
|
||||
<dt>Domain:</dt>
|
||||
|
@ -25,6 +25,7 @@ import { formDomainValidator } from "../../../../lib/util/formvalidators";
|
||||
import MutationButton from "../../../../components/form/mutation-button";
|
||||
import { TextArea, TextInput } from "../../../../components/form/inputs";
|
||||
import { useLocation } from "wouter";
|
||||
import { DomainPermissionExcludeDocsLink, DomainPermissionExcludeHelpText } from "./common";
|
||||
|
||||
export default function DomainPermissionExcludeNew() {
|
||||
const [ _location, setLocation ] = useLocation();
|
||||
@ -59,13 +60,8 @@ export default function DomainPermissionExcludeNew() {
|
||||
>
|
||||
<div className="form-section-docs">
|
||||
<h2>New Domain Permission Exclude</h2>
|
||||
<p>
|
||||
You can use the form below to create a new domain permission exclude.
|
||||
<br/>
|
||||
Domain permission excludes are domain block or domain allow entries that are not yet in force.
|
||||
<br/>
|
||||
You can choose to accept or remove a exclude.
|
||||
</p>
|
||||
<p><DomainPermissionExcludeHelpText /></p>
|
||||
<DomainPermissionExcludeDocsLink />
|
||||
</div>
|
||||
|
||||
<TextInput
|
||||
@ -79,7 +75,7 @@ export default function DomainPermissionExcludeNew() {
|
||||
<TextArea
|
||||
field={form.private_comment}
|
||||
label={"Private comment"}
|
||||
placeholder="This domain is like unto a clown car full of clowns, I suggest we block it forthwith."
|
||||
placeholder="Created an exclude for this domain because we should manage it manually."
|
||||
autoCapitalize="sentences"
|
||||
rows={3}
|
||||
/>
|
||||
|
@ -2922,9 +2922,9 @@ create-require@^1.1.0:
|
||||
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
||||
|
||||
cross-spawn@^7.0.2:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
dependencies:
|
||||
path-key "^3.1.0"
|
||||
shebang-command "^2.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user