Merge pull request #7708 from zhongwencool/i18n-conf

feat: emqx_conf_schema/emqx_plugins/emqx_dashboard I18n conf
This commit is contained in:
zhongwencool 2022-04-22 09:50:10 +08:00 committed by GitHub
commit d45700865e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1777 additions and 1492 deletions

File diff suppressed because it is too large Load Diff

View File

@ -105,28 +105,27 @@ fields("cluster") ->
sc(atom(),
#{ mapping => "ekka.cluster_name"
, default => emqxcl
, desc => "Human-friendly name of the EMQX cluster."
, desc => ?DESC(cluster_name)
, 'readOnly' => true
})}
, {"discovery_strategy",
sc(hoconsc:enum([manual, static, mcast, dns, etcd, k8s]),
#{ default => manual
, desc => "Service discovery method for the cluster nodes."
, desc => ?DESC(cluster_discovery_strategy)
, 'readOnly' => true
})}
, {"autoclean",
sc(emqx_schema:duration(),
#{ mapping => "ekka.cluster_autoclean"
, default => "5m"
, desc => "Remove disconnected nodes from the cluster after this interval."
, desc => ?DESC(cluster_autoclean)
, 'readOnly' => true
})}
, {"autoheal",
sc(boolean(),
#{ mapping => "ekka.cluster_autoheal"
, default => true
, desc => "If <code>true</code>, the node will try to heal network partitions
automatically."
, desc => ?DESC(cluster_autoheal)
, 'readOnly' => true
})}
, {"proto_dist",
@ -134,7 +133,7 @@ fields("cluster") ->
#{ mapping => "ekka.proto_dist"
, default => inet_tcp
, 'readOnly' => true
, desc => "The Erlang distribution protocol for the cluster."
, desc => ?DESC(cluster_proto_dist)
})}
, {"static",
sc(ref(cluster_static),
@ -162,7 +161,7 @@ fields(cluster_static) ->
[ {"seeds",
sc(hoconsc:array(atom()),
#{ default => []
, desc => "List EMQX node names in the static cluster. See <code>node.name</code>."
, desc => ?DESC(cluster_static_seeds)
, 'readOnly' => true
})}
];
@ -171,50 +170,49 @@ fields(cluster_mcast) ->
[ {"addr",
sc(string(),
#{ default => "239.192.0.1"
, desc => "Multicast IPv4 address."
, desc => ?DESC(cluster_mcast_addr)
, 'readOnly' => true
})}
, {"ports",
sc(hoconsc:array(integer()),
#{ default => [4369, 4370]
, 'readOnly' => true
, desc => "List of UDP ports used for service discovery.<br/>
Note: probe messages are broadcast to all the specified ports."
, desc => ?DESC(cluster_mcast_ports)
})}
, {"iface",
sc(string(),
#{ default => "0.0.0.0"
, desc => "Local IP address the node discovery service needs to bind to."
, desc => ?DESC(cluster_mcast_iface)
, 'readOnly' => true
})}
, {"ttl",
sc(range(0, 255),
#{ default => 255
, desc => "Time-to-live (TTL) for the outgoing UDP datagrams."
, desc => ?DESC(cluster_mcast_ttl)
, 'readOnly' => true
})}
, {"loop",
sc(boolean(),
#{ default => true
, desc => "If <code>true</code>, loop UDP datagrams back to the local socket."
, desc => ?DESC(cluster_mcast_loop)
, 'readOnly' => true
})}
, {"sndbuf",
sc(emqx_schema:bytesize(),
#{ default => "16KB"
, desc => "Size of the kernel-level buffer for outgoing datagrams."
, desc => ?DESC(cluster_mcast_sndbuf)
, 'readOnly' => true
})}
, {"recbuf",
sc(emqx_schema:bytesize(),
#{ default => "16KB"
, desc => "Size of the kernel-level buffer for incoming datagrams."
, desc => ?DESC(cluster_mcast_recbuf)
, 'readOnly' => true
})}
, {"buffer",
sc(emqx_schema:bytesize(),
#{ default =>"32KB"
, desc => "Size of the user-level buffer."
, desc => ?DESC(cluster_mcast_buffer)
, 'readOnly' => true
})}
];
@ -223,13 +221,13 @@ fields(cluster_dns) ->
[ {"name",
sc(string(),
#{ default => "localhost"
, desc => "The domain name of the EMQX cluster."
, desc => ?DESC(cluster_dns_name)
, 'readOnly' => true
})}
, {"app",
sc(string(),
#{ default => "emqx"
, desc => "The symbolic name of the EMQX service."
, desc => ?DESC(cluster_dns_app)
, 'readOnly' => true
})}
];
@ -237,25 +235,24 @@ fields(cluster_dns) ->
fields(cluster_etcd) ->
[ {"server",
sc(emqx_schema:comma_separated_list(),
#{ desc => "List of endpoint URLs of the etcd cluster"
#{ desc => ?DESC(cluster_etcd_server)
, 'readOnly' => true
})}
, {"prefix",
sc(string(),
#{ default => "emqxcl"
, desc => "Key prefix used for EMQX service discovery."
, desc => ?DESC(cluster_etcd_prefix)
, 'readOnly' => true
})}
, {"node_ttl",
sc(emqx_schema:duration(),
#{ default => "1m"
, 'readOnly' => true
, desc => "Expiration time of the etcd key associated with the node.
It is refreshed automatically, as long as the node is alive."
, desc => ?DESC(cluster_etcd_node_ttl)
})}
, {"ssl",
sc(hoconsc:ref(emqx_schema, ssl_client_opts),
#{ desc => "Options for the TLS connection to the etcd cluster."
#{ desc => ?DESC(cluster_etcd_ssl)
, 'readOnly' => true
})}
];
@ -263,42 +260,37 @@ It is refreshed automatically, as long as the node is alive."
fields(cluster_k8s) ->
[ {"apiserver",
sc(string(),
#{ desc => "Kubernetes API endpoint URL."
#{ desc => ?DESC(cluster_k8s_apiserver)
, 'readOnly' => true
})}
, {"service_name",
sc(string(),
#{ default => "emqx"
, desc => "EMQX broker service name."
, desc => ?DESC(cluster_k8s_service_name)
, 'readOnly' => true
})}
, {"address_type",
sc(hoconsc:enum([ip, dns, hostname]),
#{ desc => "Address type used for connecting to the discovered nodes."
#{ desc => ?DESC(cluster_k8s_address_type)
, 'readOnly' => true
})}
, {"app_name",
sc(string(),
#{ default => "emqx"
, 'readOnly' => true
, desc => "This parameter should be set to the part of the <code>node.name</code>
before the '@'.<br/>
For example, if the <code>node.name</code> is <code>emqx@127.0.0.1</code>, then this parameter
should be set to <code>emqx</code>."
, desc => ?DESC(cluster_k8s_app_name)
})}
, {"namespace",
sc(string(),
#{ default => "default"
, desc => "Kubernetes namespace."
, desc => ?DESC(cluster_k8s_namespace)
, 'readOnly' => true
})}
, {"suffix",
sc(string(),
#{ default => "pod.local"
, 'readOnly' => true
, desc => "Node name suffix.<br/>
Note: this parameter is only relevant when <code>address_type</code> is <code>dns</code>
or <code>hostname</code>."
, desc => ?DESC(cluster_k8s_suffix)
})}
];
@ -307,8 +299,7 @@ fields("node") ->
sc(string(),
#{ default => "emqx@127.0.0.1"
, 'readOnly' => true
, desc => "Unique name of the EMQX node. It must follow <code>%name%@FQDN</code> or
<code>%name%@IPv4</code> format."
, desc => ?DESC(node_name)
})}
, {"cookie",
sc(string(),
@ -316,65 +307,47 @@ fields("node") ->
default => "emqxsecretcookie",
'readOnly' => true,
sensitive => true,
desc => "Secret cookie is a random string that should be the same on all nodes in
the given EMQX cluster, but unique per EMQX cluster. It is used to prevent EMQX nodes that
belong to different clusters from accidentally connecting to each other."
desc => ?DESC(node_cookie)
})}
, {"data_dir",
sc(string(),
#{ required => true,
'readOnly' => true,
mapping => "emqx.data_dir",
desc =>
"""
Path to the persistent data directory.
Possible auto-created subdirectories are:
- `mnesia/\<node_name>`: EMQX's built-in database directory.
For example, `mnesia/emqx@127.0.0.1`.
There should be only one such subdirectory.
Meaning, in case the node is to be renamed (to e.g. `emqx@10.0.1.1`),
the old dir should be deleted first.
- `configs`: Generated configs at boot time, and cluster/local override configs.
- `patches`: Hot-patch beam files are to be placed here.
- `trace`: Trace log files.
**NOTE**: One data dir cannot be shared by two or more EMQX nodes.
"""
desc => ?DESC(node_data_dir)
})}
, {"config_files",
sc(list(string()),
#{ mapping => "emqx.config_files"
, default => undefined
, 'readOnly' => true
, desc => "List of configuration files that are read during startup. The order is
significant: later configuration files override the previous ones."
, desc => ?DESC(node_config_files)
})}
, {"global_gc_interval",
sc(emqx_schema:duration(),
#{ mapping => "emqx_machine.global_gc_interval"
, default => "15m"
, desc => "Periodic garbage collection interval."
, desc => ?DESC(node_global_gc_interval)
, 'readOnly' => true
})}
, {"crash_dump_file",
sc(file(),
#{ mapping => "vm_args.-env ERL_CRASH_DUMP"
, desc => "Location of the crash dump file"
, desc => ?DESC(node_crash_dump_file)
, 'readOnly' => true
})}
, {"crash_dump_seconds",
sc(emqx_schema:duration_s(),
#{ mapping => "vm_args.-env ERL_CRASH_DUMP_SECONDS"
, default => "30s"
, desc => "The number of seconds that the broker is allowed to spend writing
a crash dump"
, desc => ?DESC(node_crash_dump_seconds)
, 'readOnly' => true
})}
, {"crash_dump_bytes",
sc(emqx_schema:bytesize(),
#{ mapping => "vm_args.-env ERL_CRASH_DUMP_BYTES"
, default => "100MB"
, desc => "The maximum size of a crash dump file in bytes."
, desc => ?DESC(node_crash_dump_bytes)
, 'readOnly' => true
})}
, {"dist_net_ticktime",
@ -382,28 +355,25 @@ a crash dump"
#{ mapping => "vm_args.-kernel net_ticktime"
, default => "2m"
, 'readOnly' => true
, desc => "This is the approximate time an EMQX node may be unresponsive "
"until it is considered down and thereby disconnected."
, desc => ?DESC(node_dist_net_ticktime)
})}
, {"backtrace_depth",
sc(integer(),
#{ mapping => "emqx_machine.backtrace_depth"
, default => 23
, 'readOnly' => true
, desc => "Maximum depth of the call stack printed in error messages and
<code>process_info</code>."
, desc => ?DESC(node_backtrace_depth)
})}
, {"applications",
sc(emqx_schema:comma_separated_atoms(),
#{ mapping => "emqx_machine.applications"
, default => []
, 'readOnly' => true
, desc => "List of Erlang applications that shall be rebooted when the EMQX broker joins
the cluster."
, desc => ?DESC(node_applications)
})}
, {"etc_dir",
sc(string(),
#{ desc => "<code>etc</code> dir for the node"
#{ desc => ?DESC(node_etc_dir)
, 'readOnly' => true
}
)}
@ -420,81 +390,45 @@ fields("db") ->
#{ mapping => "mria.db_backend"
, default => rlog
, 'readOnly' => true
, desc => """
Select the backend for the embedded database.<br/>
<code>rlog</code> is the default backend,
that is suitable for very large clusters.<br/>
<code>mnesia</code> is a backend that offers decent performance in small clusters.
"""
, desc => ?DESC(db_backend)
})}
, {"role",
sc(hoconsc:enum([core, replicant]),
#{ mapping => "mria.node_role"
, default => core
, 'readOnly' => true
, desc => """
Select a node role.<br/>
<code>core</code> nodes provide durability of the data, and take care of writes.
It is recommended to place core nodes in different racks or different availability zones.<br/>
<code>replicant</code> nodes are ephemeral worker nodes. Removing them from the cluster
doesn't affect database redundancy<br/>
It is recommended to have more replicant nodes than core nodes.<br/>
Note: this parameter only takes effect when the <code>backend</code> is set
to <code>rlog</code>.
"""
, desc => ?DESC(db_role)
})}
, {"core_nodes",
sc(emqx_schema:comma_separated_atoms(),
#{ mapping => "mria.core_nodes"
, default => []
, 'readOnly' => true
, desc => """
List of core nodes that the replicant will connect to.<br/>
Note: this parameter only takes effect when the <code>backend</code> is set
to <code>rlog</code> and the <code>role</code> is set to <code>replicant</code>.<br/>
This value needs to be defined for manual or static cluster discovery mechanisms.<br/>
If an automatic cluster discovery mechanism is being used (such as <code>etcd</code>),
there is no need to set this value.
"""
, desc => ?DESC(db_core_nodes)
})}
, {"rpc_module",
sc(hoconsc:enum([gen_rpc, rpc]),
#{ mapping => "mria.rlog_rpc_module"
, default => gen_rpc
, 'readOnly' => true
, desc => """
Protocol used for pushing transaction logs to the replicant nodes.
"""
, desc => ?DESC(db_rpc_module)
})}
, {"tlog_push_mode",
sc(hoconsc:enum([sync, async]),
#{ mapping => "mria.tlog_push_mode"
, default => async
, 'readOnly' => true
, desc => """
In sync mode the core node waits for an ack from the replicant nodes before sending the next
transaction log entry.
"""
, desc => ?DESC(db_tlog_push_mode)
})}
, {"default_shard_transport",
sc(hoconsc:enum([gen_rpc, distr]),
#{ mapping => "mria.shard_transport"
, default => gen_rpc
, desc =>
"Defines the default transport for pushing transaction logs.<br/>"
"This may be overridden on a per-shard basis in <code>db.shard_transports</code>."
"<code>gen_rpc</code> uses the <code>gen_rpc</code> library, "
"<code>distr</code> uses the Erlang distribution.<br/>"
, desc => ?DESC(db_default_shard_transport)
})}
, {"shard_transports",
sc(map(shard, hoconsc:enum([gen_rpc, distr])),
#{ desc =>
"Allows to tune the transport method used for transaction log replication, "
"on a per-shard basis.<br/>"
"<code>gen_rpc</code> uses the <code>gen_rpc</code> library, "
"<code>distr</code> uses the Erlang distribution.<br/>"
"If not specified, the default is to use the value "
"set in <code>db.default_shard_transport</code>."
#{ desc => ?DESC(db_shard_transports)
, mapping => "emqx_machine.custom_shard_transports"
, default => #{}
})}
@ -503,19 +437,17 @@ transaction log entry.
fields("cluster_call") ->
[ {"retry_interval",
sc(emqx_schema:duration(),
#{ desc => "Time interval to retry after a failed call."
#{ desc => ?DESC(cluster_call_retry_interval)
, default => "1s"
})}
, {"max_history",
sc(range(1, 500),
#{ desc => "Retain the maximum number of completed transactions (for queries)."
#{ desc => ?DESC(cluster_call_max_history)
, default => 100
})}
, {"cleanup_interval",
sc(emqx_schema:duration(),
#{ desc =>
"Time interval to clear completed but stale transactions.
Ensure that the number of completed transactions is less than the <code>max_history</code>."
#{ desc => ?DESC(cluster_call_cleanup_interval)
, default => "5m"
})}
];
@ -524,136 +456,118 @@ fields("rpc") ->
[ {"mode",
sc(hoconsc:enum([sync, async]),
#{ default => async
, desc => "In <code>sync</code> mode the sending side waits for the ack from the "
"receiving side."
, desc => ?DESC(rpc_mode)
})}
, {"driver",
sc(hoconsc:enum([tcp, ssl]),
#{ mapping => "gen_rpc.driver"
, default => tcp
, desc => "Transport protocol used for inter-broker communication"
, desc => ?DESC(rpc_driver)
})}
, {"async_batch_size",
sc(integer(),
#{ mapping => "gen_rpc.max_batch_size"
, default => 256
, desc => "The maximum number of batch messages sent in asynchronous mode. "
"Note that this configuration does not work in synchronous mode."
, desc => ?DESC(rpc_async_batch_size)
})}
, {"port_discovery",
sc(hoconsc:enum([manual, stateless]),
#{ mapping => "gen_rpc.port_discovery"
, default => stateless
, desc => "<code>manual</code>: discover ports by <code>tcp_server_port</code>.<br/>"
"<code>stateless</code>: discover ports in a stateless manner, "
"using the following algorithm. "
"If node name is <code>emqxN@127.0.0.1</code>, where the N is an integer, "
"then the listening port will be 5370 + N."
, desc => ?DESC(rpc_port_discovery)
})}
, {"tcp_server_port",
sc(integer(),
#{ mapping => "gen_rpc.tcp_server_port"
, default => 5369
, desc => "Listening port used by RPC local service.<br/> "
"Note that this config only takes effect when rpc.port_discovery "
"is set to manual."
, desc => ?DESC(rpc_tcp_server_port)
})}
, {"ssl_server_port",
sc(integer(),
#{ mapping => "gen_rpc.ssl_server_port"
, default => 5369
, desc => "Listening port used by RPC local service.<br/> "
"Note that this config only takes effect when rpc.port_discovery "
"is set to manual and <code>driver</code> is set to <code>ssl</code>."
, desc => ?DESC(rpc_ssl_server_port)
})}
, {"tcp_client_num",
sc(range(1, 256),
#{ default => 10
, desc => "Set the maximum number of RPC communication channels initiated by this node "
"to each remote node."
, desc => ?DESC(rpc_tcp_client_num)
})}
, {"connect_timeout",
sc(emqx_schema:duration(),
#{ mapping => "gen_rpc.connect_timeout"
, default => "5s"
, desc => "Timeout for establishing an RPC connection."
, desc => ?DESC(rpc_connect_timeout)
})}
, {"certfile",
sc(file(),
#{ mapping => "gen_rpc.certfile"
, desc => "Path to TLS certificate file used to validate identity of the cluster nodes. "
"Note that this config only takes effect when <code>rpc.driver</code> "
"is set to <code>ssl</code>."
, desc => ?DESC(rpc_certfile)
})}
, {"keyfile",
sc(file(),
#{ mapping => "gen_rpc.keyfile"
, desc => "Path to the private key file for the <code>rpc.certfile</code>.<br/>"
"Note: contents of this file are secret, so "
"it's necessary to set permissions to 600."
, desc => ?DESC(rpc_keyfile)
})}
, {"cacertfile",
sc(file(),
#{ mapping => "gen_rpc.cacertfile"
, desc => "Path to certification authority TLS certificate file used to validate "
"<code>rpc.certfile</code>.<br/>"
"Note: certificates of all nodes in the cluster must be signed by the same CA."
, desc => ?DESC(rpc_cacertfile)
})}
, {"send_timeout",
sc(emqx_schema:duration(),
#{ mapping => "gen_rpc.send_timeout"
, default => "5s"
, desc => "Timeout for sending the RPC request."
, desc => ?DESC(rpc_send_timeout)
})}
, {"authentication_timeout",
sc(emqx_schema:duration(),
#{ mapping=> "gen_rpc.authentication_timeout"
, default => "5s"
, desc => "Timeout for the remote node authentication."
, desc => ?DESC(rpc_authentication_timeout)
})}
, {"call_receive_timeout",
sc(emqx_schema:duration(),
#{ mapping => "gen_rpc.call_receive_timeout"
, default => "15s"
, desc => "Timeout for the reply to a synchronous RPC."
, desc => ?DESC(rpc_call_receive_timeout)
})}
, {"socket_keepalive_idle",
sc(emqx_schema:duration_s(),
#{ mapping => "gen_rpc.socket_keepalive_idle"
, default => "7200s"
, desc => "How long the connections between the brokers "
"should remain open after the last message is sent."
, desc => ?DESC(rpc_socket_keepalive_idle)
})}
, {"socket_keepalive_interval",
sc(emqx_schema:duration_s(),
#{ mapping => "gen_rpc.socket_keepalive_interval"
, default => "75s"
, desc => "The interval between keepalive messages."
, desc => ?DESC(rpc_socket_keepalive_interval)
})}
, {"socket_keepalive_count",
sc(integer(),
#{ mapping => "gen_rpc.socket_keepalive_count"
, default => 9
, desc => "How many times the keepalive probe message can fail to receive a reply "
"until the RPC connection is considered lost."
, desc => ?DESC(rpc_socket_keepalive_count)
})}
, {"socket_sndbuf",
sc(emqx_schema:bytesize(),
#{ mapping => "gen_rpc.socket_sndbuf"
, default => "1MB"
, desc => "TCP tuning parameters. TCP sending buffer size."
, desc => ?DESC(rpc_socket_sndbuf)
})}
, {"socket_recbuf",
sc(emqx_schema:bytesize(),
#{ mapping => "gen_rpc.socket_recbuf"
, default => "1MB"
, desc => "TCP tuning parameters. TCP receiving buffer size."
, desc => ?DESC(rpc_socket_recbuf)
})}
, {"socket_buffer",
sc(emqx_schema:bytesize(),
#{ mapping => "gen_rpc.socket_buffer"
, default => "1MB"
, desc => "TCP tuning parameters. Socket buffer size in user mode."
, desc => ?DESC(rpc_socket_buffer)
})}
];
@ -1094,15 +1008,5 @@ emqx_schema_high_prio_roots() ->
Roots = emqx_schema:roots(high),
Authz = {"authorization",
sc(hoconsc:ref(?MODULE, "authorization"),
#{ desc => """
Authorization a.k.a. ACL.<br>
In EMQX, MQTT client access control is extremely flexible.<br>
An out-of-the-box set of authorization data sources are supported.
For example,<br>
'file' source is to support concise and yet generic ACL rules in a file;<br>
'built_in_database' source can be used to store per-client customizable rule sets,
natively in the EMQX node;<br>
'http' source to make EMQX call an external HTTP API to make the decision;<br>
'PostgreSQL' etc. to look up clients or rules from external databases;<br>
""" })},
#{ desc => ?DESC(authorization)})},
lists:keyreplace("authorization", 1, Roots, Authz).

View File

@ -1,12 +0,0 @@
emqx_dashboard_schema {
protocol {
desc {
en: "Protocol Name"
zh: "协议名"
}
label: {
en: "Protocol"
zh: "协议"
}
}
}

View File

@ -0,0 +1,190 @@
emqx_dashboard_schema {
listeners {
desc {
en: """HTTP(s) listeners are identified by their protocol type and are <br>
used to serve dashboard UI and restful HTTP API.<br>
Listeners must have a unique combination of port number and IP address.<br>
For example, an HTTP listener can listen on all configured IP addresses
on a given port for a machine by specifying the IP address 0.0.0.0.<br>
Alternatively, the HTTP listener can specify a unique IP address for each listener,
but use the same port."""
zh: """仪表盘监听器设置。"""
}
label {
en: "Listeners"
zh: "监听器"
}
}
sample_interval {
desc {
en: """How often to update metrics displayed in the dashboard.<br/>"
Note: `sample_interval` should be a divisor of 60."""
zh: """更新仪表板中显示的指标的时间间隔。"""
}
}
token_expired_time {
desc {
en: "JWT token expiration time."
zh: "JWT token 过期时间"
}
label {
en: "Token expired time"
zh: "JWT 过期时间"
}
}
num_acceptors {
desc {
en: "Socket acceptor pool size for TCP protocols."
zh: "TCP协议的Socket acceptor池大小"
}
label {
en: "Number of acceptors"
zh: "Acceptor 数量"
}
}
max_connections {
desc {
en: "Maximum number of simultaneous connections."
zh: "同时处理的最大连接数"
}
label {
en: "Maximum connections"
zh: "最大连接数"
}
}
backlog {
desc {
en: "Defines the maximum length that the queue of pending connections can grow to."
zh: "排队等待连接的队列的最大长度"
}
label {
en: "Backlog"
zh: "排队长度"
}
}
send_timeout {
desc {
en: "Send timeout for the socket."
zh: "Socket发送超时时间"
}
label {
en: "Send timeout"
zh: "发送超时时间"
}
}
inet6 {
desc {
en: "Enable IPv6 support."
zh: "启用IPv6"
}
label {
en: "IPv6"
zh: "IPv6"
}
}
ipv6_v6only {
desc {
en: "Disable IPv4-to-IPv6 mapping for the listener."
zh: "禁用IPv4-to-IPv6映射"
}
label {
en: "IPv6 only"
zh: "IPv6 only"
}
}
desc_dashboard {
desc {
en: "Configuration for EMQX dashboard."
zh: "EMQX仪表板配置"
}
label {
en: "Dashboard"
zh: "仪表板"
}
}
desc_listeners {
desc {
en: "Configuration for the dashboard listener."
zh: "仪表板监听器配置"
}
label {
en: "Listeners"
zh: "监听器"
}
}
desc_http {
desc {
en: "Configuration for the dashboard listener (plaintext)."
zh: "仪表板监听器(HTTP)配置"
}
label {
en: "HTTP"
zh: "HTTP"
}
}
desc_https {
desc {
en: "Configuration for the dashboard listener (TLS)."
zh: "仪表板监听器(HTTPS)配置"
}
label {
en: "HTTPS"
zh: "HTTPS"
}
}
bind {
desc {
en: "Port without IP(18083) or port with specified IP(127.0.0.1:18083)."
zh: "监听的地址与端口"
}
label {
en: "Bind"
zh: "绑定端口"
}
}
default_username {
desc {
en: "The default username of the automatically created dashboard user."
zh: "默认的仪表板用户名"
}
label {
en: "Default username"
zh: "默认用户名"
}
}
default_password {
desc {
en: """The initial default password for dashboard 'admin' user.<br>"
For safety, it should be changed as soon as possible."""
zh: """默认的仪表板用户密码<br>
为了安全,应该尽快修改密码。"""
}
label {
en: "Default password"
zh: "默认密码"
}
}
cors {
desc {
en: """Support Cross-Origin Resource Sharing (CORS).<br>
Allows a server to indicate any origins (domain, scheme, or port) other than <br
its own from which a browser should permit loading resources."""
zh: """支持跨域资源共享(CORS)<br>
允许服务器指示任何来源(域名、协议或端口),除了本服务器之外的任何浏览器应允许加载资源。"""
}
label {
en: "CORS"
zh: "跨域资源共享"
}
}
i18n_lang {
desc {
en: "Internationalization language support."
zh: "多语言支持"
}
label {
en: "I18n language"
zh: "多语言支持"
}
}
}

View File

@ -313,7 +313,7 @@ next_interval() ->
sample(Time) ->
Fun =
fun(Key, Res) ->
maps:put(Key, value(Key), Res)
maps:put(Key, getstats(Key), Res)
end,
Data = lists:foldl(Fun, #{}, ?SAMPLER_LIST),
#emqx_monit{time = Time, data = Data}.
@ -362,11 +362,17 @@ count_map(M1, M2) ->
end,
lists:foldl(Fun, #{}, ?SAMPLER_LIST).
value(connections) -> emqx_stats:getstat('connections.count');
value(topics) -> emqx_stats:getstat('topics.count');
value(subscriptions) -> emqx_stats:getstat('subscriptions.count');
value(received) -> emqx_metrics:val('messages.received');
value(received_bytes) -> emqx_metrics:val('bytes.received');
value(sent) -> emqx_metrics:val('messages.sent');
value(sent_bytes) -> emqx_metrics:val('bytes.sent');
value(dropped) -> emqx_metrics:val('messages.dropped').
getstats(Key) ->
%% Stats ets maybe not exist when ekka join.
try stats(Key)
catch _: _ -> 0
end.
stats(connections) -> emqx_stats:getstat('connections.count');
stats(topics) -> emqx_stats:getstat('topics.count');
stats(subscriptions) -> emqx_stats:getstat('subscriptions.count');
stats(received) -> emqx_metrics:val('messages.received');
stats(received_bytes) -> emqx_metrics:val('bytes.received');
stats(sent) -> emqx_metrics:val('messages.sent');
stats(sent_bytes) -> emqx_metrics:val('bytes.sent');
stats(dropped) -> emqx_metrics:val('messages.dropped').

View File

@ -32,16 +32,7 @@ fields("dashboard") ->
{listeners,
sc(
ref("listeners"),
#{
desc =>
"HTTP(s) listeners are identified by their protocol type and are\n"
"used to serve dashboard UI and restful HTTP API.<br>\n"
"Listeners must have a unique combination of port number and IP address.<br>\n"
"For example, an HTTP listener can listen on all configured IP addresses\n"
"on a given port for a machine by specifying the IP address 0.0.0.0.<br>\n"
"Alternatively, the HTTP listener can specify a unique IP address for each listener,\n"
"but use the same port."
}
#{ desc => ?DESC(listeners)}
)},
{default_username, fun default_username/1},
{default_password, fun default_password/1},
@ -50,9 +41,8 @@ fields("dashboard") ->
emqx_schema:duration_s(),
#{
default => "10s",
desc =>
"How often to update metrics displayed in the dashboard.<br/>"
"Note: `sample_interval` should be a divisor of 60."
desc => ?DESC(sample_interval),
validator => fun validate_sample_interval/1
}
)},
{token_expired_time,
@ -60,7 +50,7 @@ fields("dashboard") ->
emqx_schema:duration(),
#{
default => "30m",
desc => "JWT token expiration time."
desc => ?DESC(token_expired_time)
}
)},
{cors, fun cors/1},
@ -93,7 +83,7 @@ fields("http") ->
integer(),
#{
default => 4,
desc => "Socket acceptor pool size for TCP protocols."
desc => ?DESC(num_acceptors)
}
)},
{"max_connections",
@ -101,7 +91,7 @@ fields("http") ->
integer(),
#{
default => 512,
desc => "Maximum number of simultaneous connections."
desc => ?DESC(max_connections)
}
)},
{"backlog",
@ -109,8 +99,7 @@ fields("http") ->
integer(),
#{
default => 1024,
desc =>
"Defines the maximum length that the queue of pending connections can grow to."
desc => ?DESC(backlog)
}
)},
{"send_timeout",
@ -118,7 +107,7 @@ fields("http") ->
emqx_schema:duration(),
#{
default => "5s",
desc => "Send timeout for the socket."
desc => ?DESC(send_timeout)
}
)},
{"inet6",
@ -126,7 +115,7 @@ fields("http") ->
boolean(),
#{
default => false,
desc => "Sets up the listener for IPv6."
desc => ?DESC(inet6)
}
)},
{"ipv6_v6only",
@ -134,7 +123,7 @@ fields("http") ->
boolean(),
#{
default => false,
desc => "Disable IPv4-to-IPv6 mapping for the listener."
desc => ?DESC(ipv6_v6only)
}
)}
];
@ -145,27 +134,22 @@ fields("https") ->
emqx_schema:server_ssl_opts_schema(#{}, true)
).
desc("dashboard") ->
"Configuration for EMQX dashboard.";
desc("listeners") ->
"Configuration for the dashboard listener.";
desc("http") ->
"Configuration for the dashboard listener (plaintext).";
desc("https") ->
"Configuration for the dashboard listener (TLS).";
desc(_) ->
undefined.
desc("dashboard") -> ?DESC(desc_dashboard);
desc("listeners") -> ?DESC(desc_listeners);
desc("http") -> ?DESC(desc_http);
desc("https") -> ?DESC(desc_https);
desc(_) -> undefined.
bind(type) -> hoconsc:union([non_neg_integer(), emqx_schema:ip_port()]);
bind(default) -> 18083;
bind(required) -> true;
bind(desc) -> "Port without IP(18083) or port with specified IP(127.0.0.1:18083).";
bind(desc) -> ?DESC(bind);
bind(_) -> undefined.
default_username(type) -> string();
default_username(default) -> "admin";
default_username(required) -> true;
default_username(desc) -> "The default username of the automatically created dashboard user.";
default_username(desc) -> ?DESC(default_username);
default_username('readOnly') -> true;
default_username(_) -> undefined.
@ -180,11 +164,7 @@ default_password('readOnly') ->
default_password(sensitive) ->
true;
default_password(desc) ->
""
"\n"
"The initial default password for dashboard 'admin' user.\n"
"For safety, it should be changed as soon as possible."
"";
?DESC(default_password);
default_password(_) ->
undefined.
@ -195,18 +175,22 @@ cors(default) ->
cors(required) ->
false;
cors(desc) ->
"Support Cross-Origin Resource Sharing (CORS).\n"
"Allows a server to indicate any origins (domain, scheme, or port) other than\n"
"its own from which a browser should permit loading resources.";
?DESC(cors);
cors(_) ->
undefined.
i18n_lang(type) -> ?ENUM([en, zh]);
i18n_lang(default) -> en;
i18n_lang('readOnly') -> true;
i18n_lang(desc) -> "Internationalization language support.";
i18n_lang(desc) -> ?DESC(i18n_lang);
i18n_lang(_) -> undefined.
validate_sample_interval(Second) ->
case Second >= 1 andalso Second =< 60 andalso (60 rem Second =:= 0) of
true -> ok;
false -> error({"Sample interval must be between 1 and 60 and be a divisor of 60.", Second})
end.
sc(Type, Meta) -> hoconsc:mk(Type, Meta).
ref(Field) -> hoconsc:ref(?MODULE, Field).

View File

@ -0,0 +1,93 @@
emqx_plugins_schema {
plugins {
desc {
en: """
Manage EMQX plugins.<br>
Plugins can be pre-built as a part of EMQX package,
or installed as a standalone package in a location specified by
<code>install_dir</code> config key<br>
The standalone-installed plugins are referred to as 'external' plugins.
"""
zh: """管理EMQX插件。<br>
插件可以是EMQX安装包中的一部分也可以是一个独立的安装包。<br>
独立安装的插件称为“外部插件”。
"""
}
label {
en: "Plugins"
zh: "插件"
}
}
state {
desc {
en: "A per-plugin config to describe the desired state of the plugin."
zh: "描述插件的状态"
}
label {
en: "State"
zh: "插件状态"
}
}
name_vsn {
desc {
en: """The {name}-{version} of the plugin.<br>
It should match the plugin application name-version as the for the plugin release package name<br>
For example: my_plugin-0.1.0.
"""
zh: """插件的名称{name}-{version}。<br>
它应该与插件的发布包名称一致如my_plugin-0.1.0。"""
}
label {
en: "Name-Version"
zh: "名称-版本"
}
}
enable {
desc {
en: "Set to 'true' to enable this plugin"
zh: "设置为“true”以启用此插件"
}
label {
en: "Enable"
zh: "启用"
}
}
states {
desc {
en: """An array of plugins in the desired states.<br>
The plugins are started in the defined order"""
zh: """一组插件的状态。插件将按照定义的顺序启动"""
}
label {
en: "States"
zh: "插件启动顺序及状态"
}
}
install_dir {
desc {
en: """
The installation directory for the external plugins.
The plugin beam files and configuration files should reside in
the subdirectory named as <code>emqx_foo_bar-0.1.0</code>.
<br>
NOTE: For security reasons, this directory should **NOT** be writable
by anyone except <code>emqx</code> (or any user which runs EMQX).
"""
zh: "插件安装包的目录, 不要自己创建, 只能由emqx用户创建与修改"
}
label {
en: "Install Directory"
zh: "安装目录"
}
}
check_interval {
desc {
en: """Check interval: check if the status of the plugins in the cluster is consistent, <br>
if the results of 3 consecutive checks are not consistent, then alarm.
"""
zh: """检查间隔:检查集群中插件的状态是否一致,<br>
如果连续3次检查结果不一致则报警。
"""
}
}
}

View File

@ -23,7 +23,7 @@
, namespace/0
]).
-include_lib("typerefl/include/types.hrl").
-include_lib("hocon/include/hoconsc.hrl").
-include("emqx_plugins.hrl").
namespace() -> "plugin".
@ -32,33 +32,22 @@ roots() -> [?CONF_ROOT].
fields(?CONF_ROOT) ->
#{fields => root_fields(),
desc => """
Manage EMQX plugins.
<br>
Plugins can be pre-built as a part of EMQX package,
or installed as a standalone package in a location specified by
<code>install_dir</code> config key
<br>
The standalone-installed plugins are referred to as 'external' plugins.
"""
desc => ?DESC(?CONF_ROOT)
};
fields(state) ->
#{ fields => state_fields(),
desc => "A per-plugin config to describe the desired state of the plugin."
desc => ?DESC(state)
}.
state_fields() ->
[ {name_vsn,
hoconsc:mk(string(),
#{ desc => "The {name}-{version} of the plugin.<br>"
"It should match the plugin application name-version as the "
"for the plugin release package name<br>"
"For example: my_plugin-0.1.0."
#{ desc => ?DESC(name_vsn)
, required => true
})}
, {enable,
hoconsc:mk(boolean(),
#{ desc => "Set to 'true' to enable this plugin"
#{ desc => ?DESC(enable)
, required => true
})}
].
@ -72,27 +61,16 @@ root_fields() ->
states(type) -> hoconsc:array(hoconsc:ref(?MODULE, state));
states(required) -> false;
states(default) -> [];
states(desc) -> "An array of plugins in the desired states.<br>"
"The plugins are started in the defined order";
states(desc) -> ?DESC(states);
states(_) -> undefined.
install_dir(type) -> string();
install_dir(required) -> false;
install_dir(default) -> "plugins"; %% runner's root dir
install_dir(T) when T =/= desc -> undefined;
install_dir(desc) -> """
The installation directory for the external plugins.
The plugin beam files and configuration files should reside in
the subdirectory named as <code>emqx_foo_bar-0.1.0</code>.
<br>
NOTE: For security reasons, this directory should **NOT** be writable
by anyone except <code>emqx</code> (or any user which runs EMQX).
""".
install_dir(desc) -> ?DESC(install_dir).
check_interval(type) -> emqx_schema:duration();
check_interval(default) -> "5s";
check_interval(T) when T =/= desc -> undefined;
check_interval(desc) -> """
Check interval: check if the status of the plugins in the cluster is consistent, <br>
if the results of 3 consecutive checks are not consistent, then alarm.
""".
check_interval(desc) -> ?DESC(check_interval).

View File

@ -3,7 +3,7 @@
-mode(compile).
main(_) ->
{ok, BaseConf} = file:read_file("apps/emqx_dashboard/i18n/emqx_dashboard_i18n.conf"),
{ok, BaseConf} = file:read_file("apps/emqx_dashboard/i18n/emqx_dashboard_schema.conf"),
Cfgs = get_all_cfgs("apps/"),
Conf = [merge(BaseConf, Cfgs),