Merge pull request #7708 from zhongwencool/i18n-conf
feat: emqx_conf_schema/emqx_plugins/emqx_dashboard I18n conf
This commit is contained in:
commit
d45700865e
File diff suppressed because it is too large
Load Diff
|
@ -105,28 +105,27 @@ fields("cluster") ->
|
||||||
sc(atom(),
|
sc(atom(),
|
||||||
#{ mapping => "ekka.cluster_name"
|
#{ mapping => "ekka.cluster_name"
|
||||||
, default => emqxcl
|
, default => emqxcl
|
||||||
, desc => "Human-friendly name of the EMQX cluster."
|
, desc => ?DESC(cluster_name)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"discovery_strategy",
|
, {"discovery_strategy",
|
||||||
sc(hoconsc:enum([manual, static, mcast, dns, etcd, k8s]),
|
sc(hoconsc:enum([manual, static, mcast, dns, etcd, k8s]),
|
||||||
#{ default => manual
|
#{ default => manual
|
||||||
, desc => "Service discovery method for the cluster nodes."
|
, desc => ?DESC(cluster_discovery_strategy)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"autoclean",
|
, {"autoclean",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ mapping => "ekka.cluster_autoclean"
|
#{ mapping => "ekka.cluster_autoclean"
|
||||||
, default => "5m"
|
, default => "5m"
|
||||||
, desc => "Remove disconnected nodes from the cluster after this interval."
|
, desc => ?DESC(cluster_autoclean)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"autoheal",
|
, {"autoheal",
|
||||||
sc(boolean(),
|
sc(boolean(),
|
||||||
#{ mapping => "ekka.cluster_autoheal"
|
#{ mapping => "ekka.cluster_autoheal"
|
||||||
, default => true
|
, default => true
|
||||||
, desc => "If <code>true</code>, the node will try to heal network partitions
|
, desc => ?DESC(cluster_autoheal)
|
||||||
automatically."
|
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"proto_dist",
|
, {"proto_dist",
|
||||||
|
@ -134,7 +133,7 @@ fields("cluster") ->
|
||||||
#{ mapping => "ekka.proto_dist"
|
#{ mapping => "ekka.proto_dist"
|
||||||
, default => inet_tcp
|
, default => inet_tcp
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "The Erlang distribution protocol for the cluster."
|
, desc => ?DESC(cluster_proto_dist)
|
||||||
})}
|
})}
|
||||||
, {"static",
|
, {"static",
|
||||||
sc(ref(cluster_static),
|
sc(ref(cluster_static),
|
||||||
|
@ -162,7 +161,7 @@ fields(cluster_static) ->
|
||||||
[ {"seeds",
|
[ {"seeds",
|
||||||
sc(hoconsc:array(atom()),
|
sc(hoconsc:array(atom()),
|
||||||
#{ default => []
|
#{ default => []
|
||||||
, desc => "List EMQX node names in the static cluster. See <code>node.name</code>."
|
, desc => ?DESC(cluster_static_seeds)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
@ -171,50 +170,49 @@ fields(cluster_mcast) ->
|
||||||
[ {"addr",
|
[ {"addr",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "239.192.0.1"
|
#{ default => "239.192.0.1"
|
||||||
, desc => "Multicast IPv4 address."
|
, desc => ?DESC(cluster_mcast_addr)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"ports",
|
, {"ports",
|
||||||
sc(hoconsc:array(integer()),
|
sc(hoconsc:array(integer()),
|
||||||
#{ default => [4369, 4370]
|
#{ default => [4369, 4370]
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "List of UDP ports used for service discovery.<br/>
|
, desc => ?DESC(cluster_mcast_ports)
|
||||||
Note: probe messages are broadcast to all the specified ports."
|
|
||||||
})}
|
})}
|
||||||
, {"iface",
|
, {"iface",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "0.0.0.0"
|
#{ default => "0.0.0.0"
|
||||||
, desc => "Local IP address the node discovery service needs to bind to."
|
, desc => ?DESC(cluster_mcast_iface)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"ttl",
|
, {"ttl",
|
||||||
sc(range(0, 255),
|
sc(range(0, 255),
|
||||||
#{ default => 255
|
#{ default => 255
|
||||||
, desc => "Time-to-live (TTL) for the outgoing UDP datagrams."
|
, desc => ?DESC(cluster_mcast_ttl)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"loop",
|
, {"loop",
|
||||||
sc(boolean(),
|
sc(boolean(),
|
||||||
#{ default => true
|
#{ default => true
|
||||||
, desc => "If <code>true</code>, loop UDP datagrams back to the local socket."
|
, desc => ?DESC(cluster_mcast_loop)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"sndbuf",
|
, {"sndbuf",
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ default => "16KB"
|
#{ default => "16KB"
|
||||||
, desc => "Size of the kernel-level buffer for outgoing datagrams."
|
, desc => ?DESC(cluster_mcast_sndbuf)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"recbuf",
|
, {"recbuf",
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ default => "16KB"
|
#{ default => "16KB"
|
||||||
, desc => "Size of the kernel-level buffer for incoming datagrams."
|
, desc => ?DESC(cluster_mcast_recbuf)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"buffer",
|
, {"buffer",
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ default =>"32KB"
|
#{ default =>"32KB"
|
||||||
, desc => "Size of the user-level buffer."
|
, desc => ?DESC(cluster_mcast_buffer)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
@ -223,13 +221,13 @@ fields(cluster_dns) ->
|
||||||
[ {"name",
|
[ {"name",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "localhost"
|
#{ default => "localhost"
|
||||||
, desc => "The domain name of the EMQX cluster."
|
, desc => ?DESC(cluster_dns_name)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"app",
|
, {"app",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "emqx"
|
#{ default => "emqx"
|
||||||
, desc => "The symbolic name of the EMQX service."
|
, desc => ?DESC(cluster_dns_app)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
@ -237,25 +235,24 @@ fields(cluster_dns) ->
|
||||||
fields(cluster_etcd) ->
|
fields(cluster_etcd) ->
|
||||||
[ {"server",
|
[ {"server",
|
||||||
sc(emqx_schema:comma_separated_list(),
|
sc(emqx_schema:comma_separated_list(),
|
||||||
#{ desc => "List of endpoint URLs of the etcd cluster"
|
#{ desc => ?DESC(cluster_etcd_server)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"prefix",
|
, {"prefix",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "emqxcl"
|
#{ default => "emqxcl"
|
||||||
, desc => "Key prefix used for EMQX service discovery."
|
, desc => ?DESC(cluster_etcd_prefix)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"node_ttl",
|
, {"node_ttl",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ default => "1m"
|
#{ default => "1m"
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "Expiration time of the etcd key associated with the node.
|
, desc => ?DESC(cluster_etcd_node_ttl)
|
||||||
It is refreshed automatically, as long as the node is alive."
|
|
||||||
})}
|
})}
|
||||||
, {"ssl",
|
, {"ssl",
|
||||||
sc(hoconsc:ref(emqx_schema, ssl_client_opts),
|
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
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
@ -263,42 +260,37 @@ It is refreshed automatically, as long as the node is alive."
|
||||||
fields(cluster_k8s) ->
|
fields(cluster_k8s) ->
|
||||||
[ {"apiserver",
|
[ {"apiserver",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ desc => "Kubernetes API endpoint URL."
|
#{ desc => ?DESC(cluster_k8s_apiserver)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"service_name",
|
, {"service_name",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "emqx"
|
#{ default => "emqx"
|
||||||
, desc => "EMQX broker service name."
|
, desc => ?DESC(cluster_k8s_service_name)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"address_type",
|
, {"address_type",
|
||||||
sc(hoconsc:enum([ip, dns, hostname]),
|
sc(hoconsc:enum([ip, dns, hostname]),
|
||||||
#{ desc => "Address type used for connecting to the discovered nodes."
|
#{ desc => ?DESC(cluster_k8s_address_type)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"app_name",
|
, {"app_name",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "emqx"
|
#{ default => "emqx"
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "This parameter should be set to the part of the <code>node.name</code>
|
, desc => ?DESC(cluster_k8s_app_name)
|
||||||
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>."
|
|
||||||
})}
|
})}
|
||||||
, {"namespace",
|
, {"namespace",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "default"
|
#{ default => "default"
|
||||||
, desc => "Kubernetes namespace."
|
, desc => ?DESC(cluster_k8s_namespace)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"suffix",
|
, {"suffix",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "pod.local"
|
#{ default => "pod.local"
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "Node name suffix.<br/>
|
, desc => ?DESC(cluster_k8s_suffix)
|
||||||
Note: this parameter is only relevant when <code>address_type</code> is <code>dns</code>
|
|
||||||
or <code>hostname</code>."
|
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -307,8 +299,7 @@ fields("node") ->
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ default => "emqx@127.0.0.1"
|
#{ default => "emqx@127.0.0.1"
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "Unique name of the EMQX node. It must follow <code>%name%@FQDN</code> or
|
, desc => ?DESC(node_name)
|
||||||
<code>%name%@IPv4</code> format."
|
|
||||||
})}
|
})}
|
||||||
, {"cookie",
|
, {"cookie",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
|
@ -316,65 +307,47 @@ fields("node") ->
|
||||||
default => "emqxsecretcookie",
|
default => "emqxsecretcookie",
|
||||||
'readOnly' => true,
|
'readOnly' => true,
|
||||||
sensitive => true,
|
sensitive => true,
|
||||||
desc => "Secret cookie is a random string that should be the same on all nodes in
|
desc => ?DESC(node_cookie)
|
||||||
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."
|
|
||||||
})}
|
})}
|
||||||
, {"data_dir",
|
, {"data_dir",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ required => true,
|
#{ required => true,
|
||||||
'readOnly' => true,
|
'readOnly' => true,
|
||||||
mapping => "emqx.data_dir",
|
mapping => "emqx.data_dir",
|
||||||
desc =>
|
desc => ?DESC(node_data_dir)
|
||||||
"""
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
})}
|
})}
|
||||||
, {"config_files",
|
, {"config_files",
|
||||||
sc(list(string()),
|
sc(list(string()),
|
||||||
#{ mapping => "emqx.config_files"
|
#{ mapping => "emqx.config_files"
|
||||||
, default => undefined
|
, default => undefined
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "List of configuration files that are read during startup. The order is
|
, desc => ?DESC(node_config_files)
|
||||||
significant: later configuration files override the previous ones."
|
|
||||||
})}
|
})}
|
||||||
, {"global_gc_interval",
|
, {"global_gc_interval",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ mapping => "emqx_machine.global_gc_interval"
|
#{ mapping => "emqx_machine.global_gc_interval"
|
||||||
, default => "15m"
|
, default => "15m"
|
||||||
, desc => "Periodic garbage collection interval."
|
, desc => ?DESC(node_global_gc_interval)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"crash_dump_file",
|
, {"crash_dump_file",
|
||||||
sc(file(),
|
sc(file(),
|
||||||
#{ mapping => "vm_args.-env ERL_CRASH_DUMP"
|
#{ mapping => "vm_args.-env ERL_CRASH_DUMP"
|
||||||
, desc => "Location of the crash dump file"
|
, desc => ?DESC(node_crash_dump_file)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"crash_dump_seconds",
|
, {"crash_dump_seconds",
|
||||||
sc(emqx_schema:duration_s(),
|
sc(emqx_schema:duration_s(),
|
||||||
#{ mapping => "vm_args.-env ERL_CRASH_DUMP_SECONDS"
|
#{ mapping => "vm_args.-env ERL_CRASH_DUMP_SECONDS"
|
||||||
, default => "30s"
|
, default => "30s"
|
||||||
, desc => "The number of seconds that the broker is allowed to spend writing
|
, desc => ?DESC(node_crash_dump_seconds)
|
||||||
a crash dump"
|
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"crash_dump_bytes",
|
, {"crash_dump_bytes",
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ mapping => "vm_args.-env ERL_CRASH_DUMP_BYTES"
|
#{ mapping => "vm_args.-env ERL_CRASH_DUMP_BYTES"
|
||||||
, default => "100MB"
|
, default => "100MB"
|
||||||
, desc => "The maximum size of a crash dump file in bytes."
|
, desc => ?DESC(node_crash_dump_bytes)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
})}
|
})}
|
||||||
, {"dist_net_ticktime",
|
, {"dist_net_ticktime",
|
||||||
|
@ -382,28 +355,25 @@ a crash dump"
|
||||||
#{ mapping => "vm_args.-kernel net_ticktime"
|
#{ mapping => "vm_args.-kernel net_ticktime"
|
||||||
, default => "2m"
|
, default => "2m"
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "This is the approximate time an EMQX node may be unresponsive "
|
, desc => ?DESC(node_dist_net_ticktime)
|
||||||
"until it is considered down and thereby disconnected."
|
|
||||||
})}
|
})}
|
||||||
, {"backtrace_depth",
|
, {"backtrace_depth",
|
||||||
sc(integer(),
|
sc(integer(),
|
||||||
#{ mapping => "emqx_machine.backtrace_depth"
|
#{ mapping => "emqx_machine.backtrace_depth"
|
||||||
, default => 23
|
, default => 23
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "Maximum depth of the call stack printed in error messages and
|
, desc => ?DESC(node_backtrace_depth)
|
||||||
<code>process_info</code>."
|
|
||||||
})}
|
})}
|
||||||
, {"applications",
|
, {"applications",
|
||||||
sc(emqx_schema:comma_separated_atoms(),
|
sc(emqx_schema:comma_separated_atoms(),
|
||||||
#{ mapping => "emqx_machine.applications"
|
#{ mapping => "emqx_machine.applications"
|
||||||
, default => []
|
, default => []
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => "List of Erlang applications that shall be rebooted when the EMQX broker joins
|
, desc => ?DESC(node_applications)
|
||||||
the cluster."
|
|
||||||
})}
|
})}
|
||||||
, {"etc_dir",
|
, {"etc_dir",
|
||||||
sc(string(),
|
sc(string(),
|
||||||
#{ desc => "<code>etc</code> dir for the node"
|
#{ desc => ?DESC(node_etc_dir)
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
@ -420,81 +390,45 @@ fields("db") ->
|
||||||
#{ mapping => "mria.db_backend"
|
#{ mapping => "mria.db_backend"
|
||||||
, default => rlog
|
, default => rlog
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => """
|
, desc => ?DESC(db_backend)
|
||||||
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.
|
|
||||||
"""
|
|
||||||
})}
|
})}
|
||||||
, {"role",
|
, {"role",
|
||||||
sc(hoconsc:enum([core, replicant]),
|
sc(hoconsc:enum([core, replicant]),
|
||||||
#{ mapping => "mria.node_role"
|
#{ mapping => "mria.node_role"
|
||||||
, default => core
|
, default => core
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => """
|
, desc => ?DESC(db_role)
|
||||||
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>.
|
|
||||||
"""
|
|
||||||
})}
|
})}
|
||||||
, {"core_nodes",
|
, {"core_nodes",
|
||||||
sc(emqx_schema:comma_separated_atoms(),
|
sc(emqx_schema:comma_separated_atoms(),
|
||||||
#{ mapping => "mria.core_nodes"
|
#{ mapping => "mria.core_nodes"
|
||||||
, default => []
|
, default => []
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => """
|
, desc => ?DESC(db_core_nodes)
|
||||||
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.
|
|
||||||
"""
|
|
||||||
})}
|
})}
|
||||||
, {"rpc_module",
|
, {"rpc_module",
|
||||||
sc(hoconsc:enum([gen_rpc, rpc]),
|
sc(hoconsc:enum([gen_rpc, rpc]),
|
||||||
#{ mapping => "mria.rlog_rpc_module"
|
#{ mapping => "mria.rlog_rpc_module"
|
||||||
, default => gen_rpc
|
, default => gen_rpc
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => """
|
, desc => ?DESC(db_rpc_module)
|
||||||
Protocol used for pushing transaction logs to the replicant nodes.
|
|
||||||
"""
|
|
||||||
})}
|
})}
|
||||||
, {"tlog_push_mode",
|
, {"tlog_push_mode",
|
||||||
sc(hoconsc:enum([sync, async]),
|
sc(hoconsc:enum([sync, async]),
|
||||||
#{ mapping => "mria.tlog_push_mode"
|
#{ mapping => "mria.tlog_push_mode"
|
||||||
, default => async
|
, default => async
|
||||||
, 'readOnly' => true
|
, 'readOnly' => true
|
||||||
, desc => """
|
, desc => ?DESC(db_tlog_push_mode)
|
||||||
In sync mode the core node waits for an ack from the replicant nodes before sending the next
|
|
||||||
transaction log entry.
|
|
||||||
"""
|
|
||||||
})}
|
})}
|
||||||
, {"default_shard_transport",
|
, {"default_shard_transport",
|
||||||
sc(hoconsc:enum([gen_rpc, distr]),
|
sc(hoconsc:enum([gen_rpc, distr]),
|
||||||
#{ mapping => "mria.shard_transport"
|
#{ mapping => "mria.shard_transport"
|
||||||
, default => gen_rpc
|
, default => gen_rpc
|
||||||
, desc =>
|
, desc => ?DESC(db_default_shard_transport)
|
||||||
"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/>"
|
|
||||||
})}
|
})}
|
||||||
, {"shard_transports",
|
, {"shard_transports",
|
||||||
sc(map(shard, hoconsc:enum([gen_rpc, distr])),
|
sc(map(shard, hoconsc:enum([gen_rpc, distr])),
|
||||||
#{ desc =>
|
#{ desc => ?DESC(db_shard_transports)
|
||||||
"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>."
|
|
||||||
, mapping => "emqx_machine.custom_shard_transports"
|
, mapping => "emqx_machine.custom_shard_transports"
|
||||||
, default => #{}
|
, default => #{}
|
||||||
})}
|
})}
|
||||||
|
@ -503,19 +437,17 @@ transaction log entry.
|
||||||
fields("cluster_call") ->
|
fields("cluster_call") ->
|
||||||
[ {"retry_interval",
|
[ {"retry_interval",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ desc => "Time interval to retry after a failed call."
|
#{ desc => ?DESC(cluster_call_retry_interval)
|
||||||
, default => "1s"
|
, default => "1s"
|
||||||
})}
|
})}
|
||||||
, {"max_history",
|
, {"max_history",
|
||||||
sc(range(1, 500),
|
sc(range(1, 500),
|
||||||
#{ desc => "Retain the maximum number of completed transactions (for queries)."
|
#{ desc => ?DESC(cluster_call_max_history)
|
||||||
, default => 100
|
, default => 100
|
||||||
})}
|
})}
|
||||||
, {"cleanup_interval",
|
, {"cleanup_interval",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ desc =>
|
#{ desc => ?DESC(cluster_call_cleanup_interval)
|
||||||
"Time interval to clear completed but stale transactions.
|
|
||||||
Ensure that the number of completed transactions is less than the <code>max_history</code>."
|
|
||||||
, default => "5m"
|
, default => "5m"
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
|
@ -524,136 +456,118 @@ fields("rpc") ->
|
||||||
[ {"mode",
|
[ {"mode",
|
||||||
sc(hoconsc:enum([sync, async]),
|
sc(hoconsc:enum([sync, async]),
|
||||||
#{ default => async
|
#{ default => async
|
||||||
, desc => "In <code>sync</code> mode the sending side waits for the ack from the "
|
, desc => ?DESC(rpc_mode)
|
||||||
"receiving side."
|
|
||||||
})}
|
})}
|
||||||
, {"driver",
|
, {"driver",
|
||||||
sc(hoconsc:enum([tcp, ssl]),
|
sc(hoconsc:enum([tcp, ssl]),
|
||||||
#{ mapping => "gen_rpc.driver"
|
#{ mapping => "gen_rpc.driver"
|
||||||
, default => tcp
|
, default => tcp
|
||||||
, desc => "Transport protocol used for inter-broker communication"
|
, desc => ?DESC(rpc_driver)
|
||||||
})}
|
})}
|
||||||
, {"async_batch_size",
|
, {"async_batch_size",
|
||||||
sc(integer(),
|
sc(integer(),
|
||||||
#{ mapping => "gen_rpc.max_batch_size"
|
#{ mapping => "gen_rpc.max_batch_size"
|
||||||
, default => 256
|
, default => 256
|
||||||
, desc => "The maximum number of batch messages sent in asynchronous mode. "
|
, desc => ?DESC(rpc_async_batch_size)
|
||||||
"Note that this configuration does not work in synchronous mode."
|
|
||||||
})}
|
})}
|
||||||
, {"port_discovery",
|
, {"port_discovery",
|
||||||
sc(hoconsc:enum([manual, stateless]),
|
sc(hoconsc:enum([manual, stateless]),
|
||||||
#{ mapping => "gen_rpc.port_discovery"
|
#{ mapping => "gen_rpc.port_discovery"
|
||||||
, default => stateless
|
, default => stateless
|
||||||
, desc => "<code>manual</code>: discover ports by <code>tcp_server_port</code>.<br/>"
|
, desc => ?DESC(rpc_port_discovery)
|
||||||
"<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."
|
|
||||||
})}
|
})}
|
||||||
, {"tcp_server_port",
|
, {"tcp_server_port",
|
||||||
sc(integer(),
|
sc(integer(),
|
||||||
#{ mapping => "gen_rpc.tcp_server_port"
|
#{ mapping => "gen_rpc.tcp_server_port"
|
||||||
, default => 5369
|
, default => 5369
|
||||||
, desc => "Listening port used by RPC local service.<br/> "
|
, desc => ?DESC(rpc_tcp_server_port)
|
||||||
"Note that this config only takes effect when rpc.port_discovery "
|
|
||||||
"is set to manual."
|
|
||||||
})}
|
})}
|
||||||
, {"ssl_server_port",
|
, {"ssl_server_port",
|
||||||
sc(integer(),
|
sc(integer(),
|
||||||
#{ mapping => "gen_rpc.ssl_server_port"
|
#{ mapping => "gen_rpc.ssl_server_port"
|
||||||
, default => 5369
|
, default => 5369
|
||||||
, desc => "Listening port used by RPC local service.<br/> "
|
, desc => ?DESC(rpc_ssl_server_port)
|
||||||
"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>."
|
|
||||||
})}
|
})}
|
||||||
, {"tcp_client_num",
|
, {"tcp_client_num",
|
||||||
sc(range(1, 256),
|
sc(range(1, 256),
|
||||||
#{ default => 10
|
#{ default => 10
|
||||||
, desc => "Set the maximum number of RPC communication channels initiated by this node "
|
, desc => ?DESC(rpc_tcp_client_num)
|
||||||
"to each remote node."
|
|
||||||
})}
|
})}
|
||||||
, {"connect_timeout",
|
, {"connect_timeout",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ mapping => "gen_rpc.connect_timeout"
|
#{ mapping => "gen_rpc.connect_timeout"
|
||||||
, default => "5s"
|
, default => "5s"
|
||||||
, desc => "Timeout for establishing an RPC connection."
|
, desc => ?DESC(rpc_connect_timeout)
|
||||||
})}
|
})}
|
||||||
, {"certfile",
|
, {"certfile",
|
||||||
sc(file(),
|
sc(file(),
|
||||||
#{ mapping => "gen_rpc.certfile"
|
#{ mapping => "gen_rpc.certfile"
|
||||||
, desc => "Path to TLS certificate file used to validate identity of the cluster nodes. "
|
, desc => ?DESC(rpc_certfile)
|
||||||
"Note that this config only takes effect when <code>rpc.driver</code> "
|
|
||||||
"is set to <code>ssl</code>."
|
|
||||||
})}
|
})}
|
||||||
, {"keyfile",
|
, {"keyfile",
|
||||||
sc(file(),
|
sc(file(),
|
||||||
#{ mapping => "gen_rpc.keyfile"
|
#{ mapping => "gen_rpc.keyfile"
|
||||||
, desc => "Path to the private key file for the <code>rpc.certfile</code>.<br/>"
|
, desc => ?DESC(rpc_keyfile)
|
||||||
"Note: contents of this file are secret, so "
|
|
||||||
"it's necessary to set permissions to 600."
|
|
||||||
})}
|
})}
|
||||||
, {"cacertfile",
|
, {"cacertfile",
|
||||||
sc(file(),
|
sc(file(),
|
||||||
#{ mapping => "gen_rpc.cacertfile"
|
#{ mapping => "gen_rpc.cacertfile"
|
||||||
, desc => "Path to certification authority TLS certificate file used to validate "
|
, desc => ?DESC(rpc_cacertfile)
|
||||||
"<code>rpc.certfile</code>.<br/>"
|
|
||||||
"Note: certificates of all nodes in the cluster must be signed by the same CA."
|
|
||||||
})}
|
})}
|
||||||
, {"send_timeout",
|
, {"send_timeout",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ mapping => "gen_rpc.send_timeout"
|
#{ mapping => "gen_rpc.send_timeout"
|
||||||
, default => "5s"
|
, default => "5s"
|
||||||
, desc => "Timeout for sending the RPC request."
|
, desc => ?DESC(rpc_send_timeout)
|
||||||
})}
|
})}
|
||||||
, {"authentication_timeout",
|
, {"authentication_timeout",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ mapping=> "gen_rpc.authentication_timeout"
|
#{ mapping=> "gen_rpc.authentication_timeout"
|
||||||
, default => "5s"
|
, default => "5s"
|
||||||
, desc => "Timeout for the remote node authentication."
|
, desc => ?DESC(rpc_authentication_timeout)
|
||||||
})}
|
})}
|
||||||
, {"call_receive_timeout",
|
, {"call_receive_timeout",
|
||||||
sc(emqx_schema:duration(),
|
sc(emqx_schema:duration(),
|
||||||
#{ mapping => "gen_rpc.call_receive_timeout"
|
#{ mapping => "gen_rpc.call_receive_timeout"
|
||||||
, default => "15s"
|
, default => "15s"
|
||||||
, desc => "Timeout for the reply to a synchronous RPC."
|
, desc => ?DESC(rpc_call_receive_timeout)
|
||||||
})}
|
})}
|
||||||
, {"socket_keepalive_idle",
|
, {"socket_keepalive_idle",
|
||||||
sc(emqx_schema:duration_s(),
|
sc(emqx_schema:duration_s(),
|
||||||
#{ mapping => "gen_rpc.socket_keepalive_idle"
|
#{ mapping => "gen_rpc.socket_keepalive_idle"
|
||||||
, default => "7200s"
|
, default => "7200s"
|
||||||
, desc => "How long the connections between the brokers "
|
, desc => ?DESC(rpc_socket_keepalive_idle)
|
||||||
"should remain open after the last message is sent."
|
|
||||||
})}
|
})}
|
||||||
, {"socket_keepalive_interval",
|
, {"socket_keepalive_interval",
|
||||||
sc(emqx_schema:duration_s(),
|
sc(emqx_schema:duration_s(),
|
||||||
#{ mapping => "gen_rpc.socket_keepalive_interval"
|
#{ mapping => "gen_rpc.socket_keepalive_interval"
|
||||||
, default => "75s"
|
, default => "75s"
|
||||||
, desc => "The interval between keepalive messages."
|
, desc => ?DESC(rpc_socket_keepalive_interval)
|
||||||
})}
|
})}
|
||||||
, {"socket_keepalive_count",
|
, {"socket_keepalive_count",
|
||||||
sc(integer(),
|
sc(integer(),
|
||||||
#{ mapping => "gen_rpc.socket_keepalive_count"
|
#{ mapping => "gen_rpc.socket_keepalive_count"
|
||||||
, default => 9
|
, default => 9
|
||||||
, desc => "How many times the keepalive probe message can fail to receive a reply "
|
, desc => ?DESC(rpc_socket_keepalive_count)
|
||||||
"until the RPC connection is considered lost."
|
|
||||||
})}
|
})}
|
||||||
, {"socket_sndbuf",
|
, {"socket_sndbuf",
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ mapping => "gen_rpc.socket_sndbuf"
|
#{ mapping => "gen_rpc.socket_sndbuf"
|
||||||
, default => "1MB"
|
, default => "1MB"
|
||||||
, desc => "TCP tuning parameters. TCP sending buffer size."
|
, desc => ?DESC(rpc_socket_sndbuf)
|
||||||
})}
|
})}
|
||||||
, {"socket_recbuf",
|
, {"socket_recbuf",
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ mapping => "gen_rpc.socket_recbuf"
|
#{ mapping => "gen_rpc.socket_recbuf"
|
||||||
, default => "1MB"
|
, default => "1MB"
|
||||||
, desc => "TCP tuning parameters. TCP receiving buffer size."
|
, desc => ?DESC(rpc_socket_recbuf)
|
||||||
})}
|
})}
|
||||||
, {"socket_buffer",
|
, {"socket_buffer",
|
||||||
sc(emqx_schema:bytesize(),
|
sc(emqx_schema:bytesize(),
|
||||||
#{ mapping => "gen_rpc.socket_buffer"
|
#{ mapping => "gen_rpc.socket_buffer"
|
||||||
, default => "1MB"
|
, 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),
|
Roots = emqx_schema:roots(high),
|
||||||
Authz = {"authorization",
|
Authz = {"authorization",
|
||||||
sc(hoconsc:ref(?MODULE, "authorization"),
|
sc(hoconsc:ref(?MODULE, "authorization"),
|
||||||
#{ desc => """
|
#{ desc => ?DESC(authorization)})},
|
||||||
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>
|
|
||||||
""" })},
|
|
||||||
lists:keyreplace("authorization", 1, Roots, Authz).
|
lists:keyreplace("authorization", 1, Roots, Authz).
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
emqx_dashboard_schema {
|
|
||||||
protocol {
|
|
||||||
desc {
|
|
||||||
en: "Protocol Name"
|
|
||||||
zh: "协议名"
|
|
||||||
}
|
|
||||||
label: {
|
|
||||||
en: "Protocol"
|
|
||||||
zh: "协议"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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: "多语言支持"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -313,7 +313,7 @@ next_interval() ->
|
||||||
sample(Time) ->
|
sample(Time) ->
|
||||||
Fun =
|
Fun =
|
||||||
fun(Key, Res) ->
|
fun(Key, Res) ->
|
||||||
maps:put(Key, value(Key), Res)
|
maps:put(Key, getstats(Key), Res)
|
||||||
end,
|
end,
|
||||||
Data = lists:foldl(Fun, #{}, ?SAMPLER_LIST),
|
Data = lists:foldl(Fun, #{}, ?SAMPLER_LIST),
|
||||||
#emqx_monit{time = Time, data = Data}.
|
#emqx_monit{time = Time, data = Data}.
|
||||||
|
@ -362,11 +362,17 @@ count_map(M1, M2) ->
|
||||||
end,
|
end,
|
||||||
lists:foldl(Fun, #{}, ?SAMPLER_LIST).
|
lists:foldl(Fun, #{}, ?SAMPLER_LIST).
|
||||||
|
|
||||||
value(connections) -> emqx_stats:getstat('connections.count');
|
getstats(Key) ->
|
||||||
value(topics) -> emqx_stats:getstat('topics.count');
|
%% Stats ets maybe not exist when ekka join.
|
||||||
value(subscriptions) -> emqx_stats:getstat('subscriptions.count');
|
try stats(Key)
|
||||||
value(received) -> emqx_metrics:val('messages.received');
|
catch _: _ -> 0
|
||||||
value(received_bytes) -> emqx_metrics:val('bytes.received');
|
end.
|
||||||
value(sent) -> emqx_metrics:val('messages.sent');
|
|
||||||
value(sent_bytes) -> emqx_metrics:val('bytes.sent');
|
stats(connections) -> emqx_stats:getstat('connections.count');
|
||||||
value(dropped) -> emqx_metrics:val('messages.dropped').
|
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').
|
||||||
|
|
|
@ -32,16 +32,7 @@ fields("dashboard") ->
|
||||||
{listeners,
|
{listeners,
|
||||||
sc(
|
sc(
|
||||||
ref("listeners"),
|
ref("listeners"),
|
||||||
#{
|
#{ desc => ?DESC(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."
|
|
||||||
}
|
|
||||||
)},
|
)},
|
||||||
{default_username, fun default_username/1},
|
{default_username, fun default_username/1},
|
||||||
{default_password, fun default_password/1},
|
{default_password, fun default_password/1},
|
||||||
|
@ -50,9 +41,8 @@ fields("dashboard") ->
|
||||||
emqx_schema:duration_s(),
|
emqx_schema:duration_s(),
|
||||||
#{
|
#{
|
||||||
default => "10s",
|
default => "10s",
|
||||||
desc =>
|
desc => ?DESC(sample_interval),
|
||||||
"How often to update metrics displayed in the dashboard.<br/>"
|
validator => fun validate_sample_interval/1
|
||||||
"Note: `sample_interval` should be a divisor of 60."
|
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{token_expired_time,
|
{token_expired_time,
|
||||||
|
@ -60,7 +50,7 @@ fields("dashboard") ->
|
||||||
emqx_schema:duration(),
|
emqx_schema:duration(),
|
||||||
#{
|
#{
|
||||||
default => "30m",
|
default => "30m",
|
||||||
desc => "JWT token expiration time."
|
desc => ?DESC(token_expired_time)
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{cors, fun cors/1},
|
{cors, fun cors/1},
|
||||||
|
@ -93,7 +83,7 @@ fields("http") ->
|
||||||
integer(),
|
integer(),
|
||||||
#{
|
#{
|
||||||
default => 4,
|
default => 4,
|
||||||
desc => "Socket acceptor pool size for TCP protocols."
|
desc => ?DESC(num_acceptors)
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"max_connections",
|
{"max_connections",
|
||||||
|
@ -101,7 +91,7 @@ fields("http") ->
|
||||||
integer(),
|
integer(),
|
||||||
#{
|
#{
|
||||||
default => 512,
|
default => 512,
|
||||||
desc => "Maximum number of simultaneous connections."
|
desc => ?DESC(max_connections)
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"backlog",
|
{"backlog",
|
||||||
|
@ -109,8 +99,7 @@ fields("http") ->
|
||||||
integer(),
|
integer(),
|
||||||
#{
|
#{
|
||||||
default => 1024,
|
default => 1024,
|
||||||
desc =>
|
desc => ?DESC(backlog)
|
||||||
"Defines the maximum length that the queue of pending connections can grow to."
|
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"send_timeout",
|
{"send_timeout",
|
||||||
|
@ -118,7 +107,7 @@ fields("http") ->
|
||||||
emqx_schema:duration(),
|
emqx_schema:duration(),
|
||||||
#{
|
#{
|
||||||
default => "5s",
|
default => "5s",
|
||||||
desc => "Send timeout for the socket."
|
desc => ?DESC(send_timeout)
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"inet6",
|
{"inet6",
|
||||||
|
@ -126,7 +115,7 @@ fields("http") ->
|
||||||
boolean(),
|
boolean(),
|
||||||
#{
|
#{
|
||||||
default => false,
|
default => false,
|
||||||
desc => "Sets up the listener for IPv6."
|
desc => ?DESC(inet6)
|
||||||
}
|
}
|
||||||
)},
|
)},
|
||||||
{"ipv6_v6only",
|
{"ipv6_v6only",
|
||||||
|
@ -134,7 +123,7 @@ fields("http") ->
|
||||||
boolean(),
|
boolean(),
|
||||||
#{
|
#{
|
||||||
default => false,
|
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)
|
emqx_schema:server_ssl_opts_schema(#{}, true)
|
||||||
).
|
).
|
||||||
|
|
||||||
desc("dashboard") ->
|
desc("dashboard") -> ?DESC(desc_dashboard);
|
||||||
"Configuration for EMQX dashboard.";
|
desc("listeners") -> ?DESC(desc_listeners);
|
||||||
desc("listeners") ->
|
desc("http") -> ?DESC(desc_http);
|
||||||
"Configuration for the dashboard listener.";
|
desc("https") -> ?DESC(desc_https);
|
||||||
desc("http") ->
|
desc(_) -> undefined.
|
||||||
"Configuration for the dashboard listener (plaintext).";
|
|
||||||
desc("https") ->
|
|
||||||
"Configuration for the dashboard listener (TLS).";
|
|
||||||
desc(_) ->
|
|
||||||
undefined.
|
|
||||||
|
|
||||||
bind(type) -> hoconsc:union([non_neg_integer(), emqx_schema:ip_port()]);
|
bind(type) -> hoconsc:union([non_neg_integer(), emqx_schema:ip_port()]);
|
||||||
bind(default) -> 18083;
|
bind(default) -> 18083;
|
||||||
bind(required) -> true;
|
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.
|
bind(_) -> undefined.
|
||||||
|
|
||||||
default_username(type) -> string();
|
default_username(type) -> string();
|
||||||
default_username(default) -> "admin";
|
default_username(default) -> "admin";
|
||||||
default_username(required) -> true;
|
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('readOnly') -> true;
|
||||||
default_username(_) -> undefined.
|
default_username(_) -> undefined.
|
||||||
|
|
||||||
|
@ -180,11 +164,7 @@ default_password('readOnly') ->
|
||||||
default_password(sensitive) ->
|
default_password(sensitive) ->
|
||||||
true;
|
true;
|
||||||
default_password(desc) ->
|
default_password(desc) ->
|
||||||
""
|
?DESC(default_password);
|
||||||
"\n"
|
|
||||||
"The initial default password for dashboard 'admin' user.\n"
|
|
||||||
"For safety, it should be changed as soon as possible."
|
|
||||||
"";
|
|
||||||
default_password(_) ->
|
default_password(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
@ -195,18 +175,22 @@ cors(default) ->
|
||||||
cors(required) ->
|
cors(required) ->
|
||||||
false;
|
false;
|
||||||
cors(desc) ->
|
cors(desc) ->
|
||||||
"Support Cross-Origin Resource Sharing (CORS).\n"
|
?DESC(cors);
|
||||||
"Allows a server to indicate any origins (domain, scheme, or port) other than\n"
|
|
||||||
"its own from which a browser should permit loading resources.";
|
|
||||||
cors(_) ->
|
cors(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
i18n_lang(type) -> ?ENUM([en, zh]);
|
i18n_lang(type) -> ?ENUM([en, zh]);
|
||||||
i18n_lang(default) -> en;
|
i18n_lang(default) -> en;
|
||||||
i18n_lang('readOnly') -> true;
|
i18n_lang('readOnly') -> true;
|
||||||
i18n_lang(desc) -> "Internationalization language support.";
|
i18n_lang(desc) -> ?DESC(i18n_lang);
|
||||||
i18n_lang(_) -> undefined.
|
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).
|
sc(Type, Meta) -> hoconsc:mk(Type, Meta).
|
||||||
|
|
||||||
ref(Field) -> hoconsc:ref(?MODULE, Field).
|
ref(Field) -> hoconsc:ref(?MODULE, Field).
|
||||||
|
|
|
@ -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次检查结果不一致,则报警。
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
, namespace/0
|
, namespace/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("typerefl/include/types.hrl").
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
-include("emqx_plugins.hrl").
|
-include("emqx_plugins.hrl").
|
||||||
|
|
||||||
namespace() -> "plugin".
|
namespace() -> "plugin".
|
||||||
|
@ -32,33 +32,22 @@ roots() -> [?CONF_ROOT].
|
||||||
|
|
||||||
fields(?CONF_ROOT) ->
|
fields(?CONF_ROOT) ->
|
||||||
#{fields => root_fields(),
|
#{fields => root_fields(),
|
||||||
desc => """
|
desc => ?DESC(?CONF_ROOT)
|
||||||
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.
|
|
||||||
"""
|
|
||||||
};
|
};
|
||||||
fields(state) ->
|
fields(state) ->
|
||||||
#{ fields => state_fields(),
|
#{ fields => state_fields(),
|
||||||
desc => "A per-plugin config to describe the desired state of the plugin."
|
desc => ?DESC(state)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
state_fields() ->
|
state_fields() ->
|
||||||
[ {name_vsn,
|
[ {name_vsn,
|
||||||
hoconsc:mk(string(),
|
hoconsc:mk(string(),
|
||||||
#{ desc => "The {name}-{version} of the plugin.<br>"
|
#{ desc => ?DESC(name_vsn)
|
||||||
"It should match the plugin application name-version as the "
|
|
||||||
"for the plugin release package name<br>"
|
|
||||||
"For example: my_plugin-0.1.0."
|
|
||||||
, required => true
|
, required => true
|
||||||
})}
|
})}
|
||||||
, {enable,
|
, {enable,
|
||||||
hoconsc:mk(boolean(),
|
hoconsc:mk(boolean(),
|
||||||
#{ desc => "Set to 'true' to enable this plugin"
|
#{ desc => ?DESC(enable)
|
||||||
, required => true
|
, required => true
|
||||||
})}
|
})}
|
||||||
].
|
].
|
||||||
|
@ -72,27 +61,16 @@ root_fields() ->
|
||||||
states(type) -> hoconsc:array(hoconsc:ref(?MODULE, state));
|
states(type) -> hoconsc:array(hoconsc:ref(?MODULE, state));
|
||||||
states(required) -> false;
|
states(required) -> false;
|
||||||
states(default) -> [];
|
states(default) -> [];
|
||||||
states(desc) -> "An array of plugins in the desired states.<br>"
|
states(desc) -> ?DESC(states);
|
||||||
"The plugins are started in the defined order";
|
|
||||||
states(_) -> undefined.
|
states(_) -> undefined.
|
||||||
|
|
||||||
install_dir(type) -> string();
|
install_dir(type) -> string();
|
||||||
install_dir(required) -> false;
|
install_dir(required) -> false;
|
||||||
install_dir(default) -> "plugins"; %% runner's root dir
|
install_dir(default) -> "plugins"; %% runner's root dir
|
||||||
install_dir(T) when T =/= desc -> undefined;
|
install_dir(T) when T =/= desc -> undefined;
|
||||||
install_dir(desc) -> """
|
install_dir(desc) -> ?DESC(install_dir).
|
||||||
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).
|
|
||||||
""".
|
|
||||||
|
|
||||||
check_interval(type) -> emqx_schema:duration();
|
check_interval(type) -> emqx_schema:duration();
|
||||||
check_interval(default) -> "5s";
|
check_interval(default) -> "5s";
|
||||||
check_interval(T) when T =/= desc -> undefined;
|
check_interval(T) when T =/= desc -> undefined;
|
||||||
check_interval(desc) -> """
|
check_interval(desc) -> ?DESC(check_interval).
|
||||||
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.
|
|
||||||
""".
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
-mode(compile).
|
-mode(compile).
|
||||||
|
|
||||||
main(_) ->
|
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/"),
|
Cfgs = get_all_cfgs("apps/"),
|
||||||
Conf = [merge(BaseConf, Cfgs),
|
Conf = [merge(BaseConf, Cfgs),
|
||||||
|
|
Loading…
Reference in New Issue