mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-08 00:50:28 +08:00
feat: add response to Records page (#2830)
* feat: add response to Records page * feat: improve AddRecord * feat: remove log and return err * feat: improve record in signup and record deny * fix: filter will generate 403 record correctly
This commit is contained in:
@ -271,10 +271,7 @@ func (c *ApiController) Signup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", fmt.Sprintf("%s/%s", application.Organization, user.Name))
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
|
|
||||||
userId := user.GetId()
|
userId := user.GetId()
|
||||||
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
||||||
|
@ -508,10 +508,7 @@ func (c *ApiController) Login() {
|
|||||||
|
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
}
|
}
|
||||||
} else if authForm.Provider != "" {
|
} else if authForm.Provider != "" {
|
||||||
var application *object.Application
|
var application *object.Application
|
||||||
@ -632,10 +629,7 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
||||||
// Sign up via OAuth
|
// Sign up via OAuth
|
||||||
if application.EnableLinkWithEmail {
|
if application.EnableLinkWithEmail {
|
||||||
@ -768,16 +762,8 @@ func (c *ApiController) Login() {
|
|||||||
|
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
c.Ctx.Input.SetParam("recordSignup", "true")
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
|
|
||||||
record2 := object.NewRecord(c.Ctx)
|
|
||||||
record2.Action = "signup"
|
|
||||||
record2.Organization = application.Organization
|
|
||||||
record2.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record2) })
|
|
||||||
} else if provider.Category == "SAML" {
|
} else if provider.Category == "SAML" {
|
||||||
// TODO: since we get the user info from SAML response, we can try to create the user
|
// TODO: since we get the user info from SAML response, we can try to create the user
|
||||||
resp = &Response{Status: "error", Msg: fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id))}
|
resp = &Response{Status: "error", Msg: fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id))}
|
||||||
@ -879,10 +865,7 @@ func (c *ApiController) Login() {
|
|||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
c.setMfaUserSession("")
|
c.setMfaUserSession("")
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
} else {
|
} else {
|
||||||
if c.GetSessionUsername() != "" {
|
if c.GetSessionUsername() != "" {
|
||||||
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
||||||
@ -901,10 +884,7 @@ func (c *ApiController) Login() {
|
|||||||
user := c.getCurrentUser()
|
user := c.getCurrentUser()
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||||
record.Organization = application.Organization
|
|
||||||
record.User = user.Name
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
|
||||||
} else {
|
} else {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(authForm)))
|
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(authForm)))
|
||||||
return
|
return
|
||||||
|
1
main.go
1
main.go
@ -59,6 +59,7 @@ func main() {
|
|||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
|
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
|
||||||
|
beego.InsertFilter("*", beego.AfterExec, routers.AfterRecordMessage, false)
|
||||||
|
|
||||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||||
beego.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"
|
beego.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -34,7 +35,12 @@ type Record struct {
|
|||||||
casvisorsdk.Record
|
casvisorsdk.Record
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
type Response struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
|
||||||
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
|
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
|
||||||
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
|
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
|
||||||
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
|
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
|
||||||
@ -47,6 +53,17 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
|||||||
object = string(ctx.Input.RequestBody)
|
object = string(ctx.Input.RequestBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
respBytes, err := json.Marshal(ctx.Input.Data()["json"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp Response
|
||||||
|
err = json.Unmarshal(respBytes, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
language := ctx.Request.Header.Get("Accept-Language")
|
language := ctx.Request.Header.Get("Accept-Language")
|
||||||
if len(language) > 2 {
|
if len(language) > 2 {
|
||||||
language = language[0:2]
|
language = language[0:2]
|
||||||
@ -63,10 +80,10 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
|||||||
Action: action,
|
Action: action,
|
||||||
Language: languageCode,
|
Language: languageCode,
|
||||||
Object: object,
|
Object: object,
|
||||||
Response: "",
|
Response: fmt.Sprintf("{status:\"%s\", msg:\"%s\"}", resp.Status, resp.Msg),
|
||||||
IsTriggered: false,
|
IsTriggered: false,
|
||||||
}
|
}
|
||||||
return &record
|
return &record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddRecord(record *casvisorsdk.Record) bool {
|
func AddRecord(record *casvisorsdk.Record) bool {
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/object"
|
||||||
|
|
||||||
"github.com/beego/beego/context"
|
"github.com/beego/beego/context"
|
||||||
"github.com/casdoor/casdoor/authz"
|
"github.com/casdoor/casdoor/authz"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -211,5 +213,17 @@ func ApiFilter(ctx *context.Context) {
|
|||||||
|
|
||||||
if !isAllowed {
|
if !isAllowed {
|
||||||
denyRequest(ctx)
|
denyRequest(ctx)
|
||||||
|
record, err := object.NewRecord(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Organization = subOwner
|
||||||
|
record.User = subName // auth:Unauthorized operation
|
||||||
|
record.Response = fmt.Sprintf("{status:\"error\", msg:\"%s\"}", T(ctx, "auth:Unauthorized operation"))
|
||||||
|
|
||||||
|
util.SafeGoroutine(func() {
|
||||||
|
object.AddRecord(record)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,12 +60,30 @@ func RecordMessage(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
record := object.NewRecord(ctx)
|
|
||||||
|
|
||||||
userId := getUser(ctx)
|
userId := getUser(ctx)
|
||||||
|
|
||||||
|
ctx.Input.SetParam("recordUserId", userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AfterRecordMessage(ctx *context.Context) {
|
||||||
|
record, err := object.NewRecord(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userId := ctx.Input.Params()["recordUserId"]
|
||||||
if userId != "" {
|
if userId != "" {
|
||||||
record.Organization, record.User = util.GetOwnerAndNameFromId(userId)
|
record.Organization, record.User = util.GetOwnerAndNameFromId(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
recordSignup := ctx.Input.Params()["recordSignup"]
|
||||||
|
if recordSignup == "true" {
|
||||||
|
record2 := *record
|
||||||
|
record2.Action = "signup"
|
||||||
|
util.SafeGoroutine(func() { object.AddRecord(&record2) })
|
||||||
|
}
|
||||||
|
|
||||||
|
util.SafeGoroutine(func() {
|
||||||
|
object.AddRecord(record)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -151,6 +151,14 @@ class RecordListPage extends BaseListPage {
|
|||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("language"),
|
...this.getColumnSearchProps("language"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("record:Response"),
|
||||||
|
dataIndex: "response",
|
||||||
|
key: "response",
|
||||||
|
width: "90px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("response"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("record:Object"),
|
title: i18next.t("record:Object"),
|
||||||
dataIndex: "object",
|
dataIndex: "object",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Kopiere den Link",
|
"Copy Link": "Kopiere den Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copiar enlace",
|
"Copy Link": "Copiar enlace",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copier le lien",
|
"Copy Link": "Copier le lien",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Salin Tautan",
|
"Copy Link": "Salin Tautan",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "コピー リンク",
|
"Copy Link": "コピー リンク",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "링크 복사하기",
|
"Copy Link": "링크 복사하기",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copiar Link",
|
"Copy Link": "Copiar Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Копировать ссылку",
|
"Copy Link": "Копировать ссылку",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Copy Link",
|
"Copy Link": "Copy Link",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "Is triggered",
|
"Is triggered": "Is triggered",
|
||||||
"Object": "Object"
|
"Object": "Object",
|
||||||
|
"Response": "Response"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "Sao chép liên kết",
|
"Copy Link": "Sao chép liên kết",
|
||||||
|
@ -892,7 +892,8 @@
|
|||||||
},
|
},
|
||||||
"record": {
|
"record": {
|
||||||
"Is triggered": "是否触发",
|
"Is triggered": "是否触发",
|
||||||
"Object": "实体"
|
"Object": "实体",
|
||||||
|
"Response": "响应"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"Copy Link": "复制链接",
|
"Copy Link": "复制链接",
|
||||||
|
Reference in New Issue
Block a user