Compare commits

...
Author SHA1 Message Date
Logic cd3d70e437 style(web-app): format monitor list component HTML
- Reformat HTML code for better readability
- Improve indentation and line breaks
2025-06-26 18:16:55 +08:00
YxYL e3235e202c [feature:export-allmonitors] fix eslint ci check 2025-06-26 17:34:17 +08:00
YxYL 36084af96d [feature:export-allmonitors] fix json parse in en-US.json 2025-06-26 10:54:44 +08:00
YxYL d2dd8068d3 Merge branch 'master' into feature-export-allmonitors 2025-06-26 10:42:32 +08:00
Calvin 62f40d3ad1 [doc] japanese hpe switch (#3511) 2025-06-26 00:07:54 +08:00
Calvin a52f6afa90 Merge branch 'master' into feature-export-allmonitors 2025-06-25 23:36:21 +08:00
tomsun28 57601ef989 [release] release new version 1.7.2 (#3510)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-06-25 22:01:18 +08:00
YxYL 9cfc431f16 Merge branch 'master' into feature-export-allmonitors 2025-06-25 20:58:41 +08:00
YxYL be2987e901 [feature:export-allmonitors] add export all monitors handler on fe 2025-06-25 20:50:15 +08:00
YxYL c0753f08ae [feature:export-allmonitors] add export all monitors handler on fe 2025-06-25 20:50:09 +08:00
puruidong 0a05edbec5 [bugfix]Fixed an error in the format of the flying book notification (#3508)
Signed-off-by: puruidong <pruidong@gmail.com>
2025-06-25 19:18:21 +08:00
YxYL 5ef8db0c52 [feature:export-allmonitors] add export all monitors handler on backend 2025-06-25 17:51:02 +08:00
Yang Chenandtomsun28 fcef24fdbf [bugfix] Correctly parse binary comparison expressions (#3504)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-06-25 11:01:12 +08:00
e45c31abfb [bugfix] Fix incorrect expression parsing in alert setting component (#3497)
Co-authored-by: Calvin <zhengqiwei@apache.org>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-06-24 22:56:16 +08:00
198c190ca7 fixed:a minor issue change (#3428)
Signed-off-by: 淞筱 <2030509072@qq.com>
Co-authored-by: kangli <likang@apache.org>
Co-authored-by: Calvin <zhengqiwei@apache.org>
Co-authored-by: aias00 <liuhongyu@apache.org>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: liutianyou <tianyou.liu@qq.com>
Co-authored-by: 淞筱 <2030509072@qq.com>
2025-06-24 22:50:22 +08:00
铁甲小宝anda-little-fool 392a59f9a9 add:a small jdbc modified. (#3500)
Co-authored-by: a-little-fool <2030509072@qq.com>
2025-06-24 22:07:18 +08:00
aias00 c01b7f7bce [feat] update_mvnd_version (#3498) 2025-06-23 21:18:04 +08:00
27 changed files with 779 additions and 446 deletions
@@ -2177,11 +2177,8 @@ public class AlertExpressionParser extends Parser {
return getRuleContext(AggregationOperatorContext.class,0);
}
public TerminalNode LPAREN() { return getToken(AlertExpressionParser.LPAREN, 0); }
public List<PromqlContext> promql() {
return getRuleContexts(PromqlContext.class);
}
public PromqlContext promql(int i) {
return getRuleContext(PromqlContext.class,i);
public PromqlContext promql() {
return getRuleContext(PromqlContext.class,0);
}
public TerminalNode RPAREN() { return getToken(AlertExpressionParser.RPAREN, 0); }
public TerminalNode BY() { return getToken(AlertExpressionParser.BY, 0); }
@@ -2192,9 +2189,6 @@ public class AlertExpressionParser extends Parser {
return getRuleContext(FunctionCallContext.class,0);
}
public TerminalNode IDENTIFIER() { return getToken(AlertExpressionParser.IDENTIFIER, 0); }
public BinaryOperatorContext binaryOperator() {
return getRuleContext(BinaryOperatorContext.class,0);
}
public TerminalNode LBRACKET() { return getToken(AlertExpressionParser.LBRACKET, 0); }
public List<DurationContext> duration() {
return getRuleContexts(DurationContext.class);
@@ -2289,7 +2283,7 @@ public class AlertExpressionParser extends Parser {
break;
}
_ctx.stop = _input.LT(-1);
setState(351);
setState(347);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,39,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -2297,7 +2291,7 @@ public class AlertExpressionParser extends Parser {
if ( _parseListeners!=null ) triggerExitRuleEvent();
_prevctx = _localctx;
{
setState(349);
setState(345);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,38,_ctx) ) {
case 1:
@@ -2305,24 +2299,30 @@ public class AlertExpressionParser extends Parser {
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(330);
if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)");
if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
setState(331);
binaryOperator();
match(LBRACKET);
setState(332);
promql(7);
duration();
setState(333);
match(RBRACKET);
}
break;
case 2:
{
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(334);
if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
setState(335);
match(LBRACKET);
if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
setState(336);
duration();
match(LBRACKET);
setState(337);
duration();
setState(338);
match(COLON);
setState(339);
duration();
setState(340);
match(RBRACKET);
}
break;
@@ -2330,36 +2330,18 @@ public class AlertExpressionParser extends Parser {
{
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(339);
if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)");
setState(340);
match(LBRACKET);
setState(341);
duration();
setState(342);
match(COLON);
setState(343);
duration();
setState(344);
match(RBRACKET);
}
break;
case 4:
{
_localctx = new PromqlContext(_parentctx, _parentState);
pushNewRecursionContext(_localctx, _startState, RULE_promql);
setState(346);
if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)");
setState(347);
setState(343);
match(OFFSET);
setState(348);
setState(344);
duration();
}
break;
}
}
}
setState(353);
setState(349);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,39,_ctx);
}
@@ -2401,19 +2383,19 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(354);
setState(350);
match(LBRACE);
setState(356);
setState(352);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==IDENTIFIER) {
{
setState(355);
setState(351);
labelMatcherList();
}
}
setState(358);
setState(354);
match(RBRACE);
}
}
@@ -2458,21 +2440,21 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(360);
setState(356);
labelMatcherItem();
setState(365);
setState(361);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(361);
setState(357);
match(COMMA);
setState(362);
setState(358);
labelMatcherItem();
}
}
setState(367);
setState(363);
_errHandler.sync(this);
_la = _input.LA(1);
}
@@ -2515,11 +2497,11 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(368);
setState(364);
match(IDENTIFIER);
setState(369);
setState(365);
labelMatcherOp();
setState(370);
setState(366);
string();
}
}
@@ -2556,7 +2538,7 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(372);
setState(368);
_la = _input.LA(1);
if ( !(_la==EQ || _la==NE) ) {
_errHandler.recoverInline(this);
@@ -2609,27 +2591,27 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(374);
setState(370);
match(LPAREN);
setState(375);
setState(371);
match(IDENTIFIER);
setState(380);
setState(376);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
setState(376);
setState(372);
match(COMMA);
setState(377);
setState(373);
match(IDENTIFIER);
}
}
setState(382);
setState(378);
_errHandler.sync(this);
_la = _input.LA(1);
}
setState(383);
setState(379);
match(RPAREN);
}
}
@@ -2668,11 +2650,11 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(385);
setState(381);
match(LBRACKET);
setState(386);
setState(382);
duration();
setState(387);
setState(383);
match(RBRACKET);
}
}
@@ -2717,7 +2699,7 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(389);
setState(385);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 121324437504L) != 0)) ) {
_errHandler.recoverInline(this);
@@ -2769,7 +2751,7 @@ public class AlertExpressionParser extends Parser {
try {
enterOuterAlt(_localctx, 1);
{
setState(391);
setState(387);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 2216615441596430L) != 0)) ) {
_errHandler.recoverInline(this);
@@ -2828,19 +2810,17 @@ public class AlertExpressionParser extends Parser {
private boolean promql_sempred(PromqlContext _localctx, int predIndex) {
switch (predIndex) {
case 6:
return precpred(_ctx, 6);
case 7:
return precpred(_ctx, 4);
case 8:
case 7:
return precpred(_ctx, 3);
case 9:
case 8:
return precpred(_ctx, 2);
}
return true;
}
public static final String _serializedATN =
"\u0004\u0001E\u018a\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+
"\u0004\u0001E\u0186\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+
"\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+
"\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+
"\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+
@@ -2894,217 +2874,215 @@ public class AlertExpressionParser extends Parser {
"\u0016\u0001\u0016\u0003\u0016\u0149\b\u0016\u0001\u0016\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001"+
"\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0005\u0016\u015e"+
"\b\u0016\n\u0016\f\u0016\u0161\t\u0016\u0001\u0017\u0001\u0017\u0003\u0017"+
"\u0165\b\u0017\u0001\u0017\u0001\u0017\u0001\u0018\u0001\u0018\u0001\u0018"+
"\u0005\u0018\u016c\b\u0018\n\u0018\f\u0018\u016f\t\u0018\u0001\u0019\u0001"+
"\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001\u001a\u0001\u001b\u0001"+
"\u001b\u0001\u001b\u0001\u001b\u0005\u001b\u017b\b\u001b\n\u001b\f\u001b"+
"\u017e\t\u001b\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001\u001c"+
"\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e"+
"\u0000\u0003\u0002$,\u001f\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012"+
"\u0014\u0016\u0018\u001a\u001c\u001e \"$&(*,.02468:<\u0000\u0007\u0001"+
"\u0000-2\u0001\u0000=?\u0004\u0000\u0017\u001b\u001f!%*BB\u0001\u0000"+
"\u000f\u0010\u0001\u000012\u0002\u0000\u0017\u001d\"$\u0002\u0000\u0001"+
"\u0003-2\u01b3\u0000>\u0001\u0000\u0000\u0000\u0002S\u0001\u0000\u0000"+
"\u0000\u0004f\u0001\u0000\u0000\u0000\u0006k\u0001\u0000\u0000\u0000\b"+
"w\u0001\u0000\u0000\u0000\ny\u0001\u0000\u0000\u0000\f{\u0001\u0000\u0000"+
"\u0000\u000e}\u0001\u0000\u0000\u0000\u0010\u007f\u0001\u0000\u0000\u0000"+
"\u0012\u0081\u0001\u0000\u0000\u0000\u0014\u009b\u0001\u0000\u0000\u0000"+
"\u0016\u00c1\u0001\u0000\u0000\u0000\u0018\u00c3\u0001\u0000\u0000\u0000"+
"\u001a\u00cb\u0001\u0000\u0000\u0000\u001c\u00db\u0001\u0000\u0000\u0000"+
"\u001e\u00dd\u0001\u0000\u0000\u0000 \u00df\u0001\u0000\u0000\u0000\""+
"\u00f4\u0001\u0000\u0000\u0000$\u00fc\u0001\u0000\u0000\u0000&\u0118\u0001"+
"\u0000\u0000\u0000(\u0128\u0001\u0000\u0000\u0000*\u0136\u0001\u0000\u0000"+
"\u0000,\u0148\u0001\u0000\u0000\u0000.\u0162\u0001\u0000\u0000\u00000"+
"\u0168\u0001\u0000\u0000\u00002\u0170\u0001\u0000\u0000\u00004\u0174\u0001"+
"\u0000\u0000\u00006\u0176\u0001\u0000\u0000\u00008\u0181\u0001\u0000\u0000"+
"\u0000:\u0185\u0001\u0000\u0000\u0000<\u0187\u0001\u0000\u0000\u0000>"+
"?\u0003\u0002\u0001\u0000?@\u0005\u0000\u0000\u0001@\u0001\u0001\u0000"+
"\u0000\u0000AB\u0006\u0001\uffff\uffff\u0000BC\u00053\u0000\u0000CD\u0003"+
"\u0002\u0001\u0000DE\u00054\u0000\u0000ET\u0001\u0000\u0000\u0000FT\u0003"+
",\u0016\u0000GT\u0003\u0012\t\u0000HT\u0003\n\u0005\u0000IJ\u0005+\u0000"+
"\u0000JK\u00053\u0000\u0000KL\u0003\f\u0006\u0000LM\u00054\u0000\u0000"+
"MT\u0001\u0000\u0000\u0000NO\u0005,\u0000\u0000OP\u00053\u0000\u0000P"+
"Q\u0003\f\u0006\u0000QR\u00054\u0000\u0000RT\u0001\u0000\u0000\u0000S"+
"A\u0001\u0000\u0000\u0000SF\u0001\u0000\u0000\u0000SG\u0001\u0000\u0000"+
"\u0000SH\u0001\u0000\u0000\u0000SI\u0001\u0000\u0000\u0000SN\u0001\u0000"+
"\u0000\u0000Tc\u0001\u0000\u0000\u0000UV\n\t\u0000\u0000VW\u0007\u0000"+
"\u0000\u0000Wb\u0003\u0002\u0001\nXY\n\b\u0000\u0000YZ\u0005\u0001\u0000"+
"\u0000Zb\u0003\u0002\u0001\t[\\\n\u0007\u0000\u0000\\]\u0005\u0003\u0000"+
"\u0000]b\u0003\u0002\u0001\b^_\n\u0006\u0000\u0000_`\u0005\u0002\u0000"+
"\u0000`b\u0003\u0002\u0001\u0007aU\u0001\u0000\u0000\u0000aX\u0001\u0000"+
"\u0000\u0000a[\u0001\u0000\u0000\u0000a^\u0001\u0000\u0000\u0000be\u0001"+
"\u0000\u0000\u0000ca\u0001\u0000\u0000\u0000cd\u0001\u0000\u0000\u0000"+
"d\u0003\u0001\u0000\u0000\u0000ec\u0001\u0000\u0000\u0000fg\u0003\u0010"+
"\b\u0000gh\u00053\u0000\u0000hi\u0003\u0006\u0003\u0000ij\u00054\u0000"+
"\u0000j\u0005\u0001\u0000\u0000\u0000kp\u0003\b\u0004\u0000lm\u00059\u0000"+
"\u0000mo\u0003\b\u0004\u0000nl\u0001\u0000\u0000\u0000or\u0001\u0000\u0000"+
"\u0000pn\u0001\u0000\u0000\u0000pq\u0001\u0000\u0000\u0000q\u0007\u0001"+
"\u0000\u0000\u0000rp\u0001\u0000\u0000\u0000sx\u0003\u0002\u0001\u0000"+
"tx\u0005\u0016\u0000\u0000ux\u0003\f\u0006\u0000vx\u0003\u000e\u0007\u0000"+
"ws\u0001\u0000\u0000\u0000wt\u0001\u0000\u0000\u0000wu\u0001\u0000\u0000"+
"\u0000wv\u0001\u0000\u0000\u0000x\t\u0001\u0000\u0000\u0000yz\u0007\u0001"+
"\u0000\u0000z\u000b\u0001\u0000\u0000\u0000{|\u0005A\u0000\u0000|\r\u0001"+
"\u0000\u0000\u0000}~\u0005@\u0000\u0000~\u000f\u0001\u0000\u0000\u0000"+
"\u007f\u0080\u0007\u0002\u0000\u0000\u0080\u0011\u0001\u0000\u0000\u0000"+
"\u0081\u0082\u0005\u0005\u0000\u0000\u0082\u0083\u0003\u0014\n\u0000\u0083"+
"\u0084\u0005\u0006\u0000\u0000\u0084\u0087\u0003 \u0010\u0000\u0085\u0086"+
"\u0005\u0007\u0000\u0000\u0086\u0088\u0003$\u0012\u0000\u0087\u0085\u0001"+
"\u0000\u0000\u0000\u0087\u0088\u0001\u0000\u0000\u0000\u0088\u008c\u0001"+
"\u0000\u0000\u0000\u0089\u008a\u0005\b\u0000\u0000\u008a\u008b\u0005\t"+
"\u0000\u0000\u008b\u008d\u0003\u0018\f\u0000\u008c\u0089\u0001\u0000\u0000"+
"\u0000\u008c\u008d\u0001\u0000\u0000\u0000\u008d\u0090\u0001\u0000\u0000"+
"\u0000\u008e\u008f\u0005\n\u0000\u0000\u008f\u0091\u0003$\u0012\u0000"+
"\u0090\u008e\u0001\u0000\u0000\u0000\u0090\u0091\u0001\u0000\u0000\u0000"+
"\u0091\u0095\u0001\u0000\u0000\u0000\u0092\u0093\u0005\u000b\u0000\u0000"+
"\u0093\u0094\u0005\t\u0000\u0000\u0094\u0096\u0003\u001a\r\u0000\u0095"+
"\u0092\u0001\u0000\u0000\u0000\u0095\u0096\u0001\u0000\u0000\u0000\u0096"+
"\u0099\u0001\u0000\u0000\u0000\u0097\u0098\u0005\f\u0000\u0000\u0098\u009a"+
"\u0003\u001e\u000f\u0000\u0099\u0097\u0001\u0000\u0000\u0000\u0099\u009a"+
"\u0001\u0000\u0000\u0000\u009a\u0013\u0001\u0000\u0000\u0000\u009b\u00a0"+
"\u0003\u0016\u000b\u0000\u009c\u009d\u00059\u0000\u0000\u009d\u009f\u0003"+
"\u0016\u000b\u0000\u009e\u009c\u0001\u0000\u0000\u0000\u009f\u00a2\u0001"+
"\u0000\u0000\u0000\u00a0\u009e\u0001\u0000\u0000\u0000\u00a0\u00a1\u0001"+
"\u0000\u0000\u0000\u00a1\u0015\u0001\u0000\u0000\u0000\u00a2\u00a0\u0001"+
"\u0000\u0000\u0000\u00a3\u00a8\u0003\u0004\u0002\u0000\u00a4\u00a6\u0005"+
"\u000e\u0000\u0000\u00a5\u00a4\u0001\u0000\u0000\u0000\u00a5\u00a6\u0001"+
"\u0000\u0000\u0000\u00a6\u00a7\u0001\u0000\u0000\u0000\u00a7\u00a9\u0005"+
"B\u0000\u0000\u00a8\u00a5\u0001\u0000\u0000\u0000\u00a8\u00a9\u0001\u0000"+
"\u0000\u0000\u00a9\u00c2\u0001\u0000\u0000\u0000\u00aa\u00af\u0005B\u0000"+
"\u0000\u00ab\u00ad\u0005\u000e\u0000\u0000\u00ac\u00ab\u0001\u0000\u0000"+
"\u0000\u00ac\u00ad\u0001\u0000\u0000\u0000\u00ad\u00ae\u0001\u0000\u0000"+
"\u0000\u00ae\u00b0\u0005B\u0000\u0000\u00af\u00ac\u0001\u0000\u0000\u0000"+
"\u00af\u00b0\u0001\u0000\u0000\u0000\u00b0\u00c2\u0001\u0000\u0000\u0000"+
"\u00b1\u00b6\u0005\u0016\u0000\u0000\u00b2\u00b4\u0005\u000e\u0000\u0000"+
"\u00b3\u00b2\u0001\u0000\u0000\u0000\u00b3\u00b4\u0001\u0000\u0000\u0000"+
"\u00b4\u00b5\u0001\u0000\u0000\u0000\u00b5\u00b7\u0005B\u0000\u0000\u00b6"+
"\u00b3\u0001\u0000\u0000\u0000\u00b6\u00b7\u0001\u0000\u0000\u0000\u00b7"+
"\u00c2\u0001\u0000\u0000\u0000\u00b8\u00b9\u0005B\u0000\u0000\u00b9\u00ba"+
"\u0005:\u0000\u0000\u00ba\u00bf\u0005B\u0000\u0000\u00bb\u00bd\u0005\u000e"+
"\u0000\u0000\u00bc\u00bb\u0001\u0000\u0000\u0000\u00bc\u00bd\u0001\u0000"+
"\u0000\u0000\u00bd\u00be\u0001\u0000\u0000\u0000\u00be\u00c0\u0005B\u0000"+
"\u0000\u00bf\u00bc\u0001\u0000\u0000\u0000\u00bf\u00c0\u0001\u0000\u0000"+
"\u0000\u00c0\u00c2\u0001\u0000\u0000\u0000\u00c1\u00a3\u0001\u0000\u0000"+
"\u0000\u00c1\u00aa\u0001\u0000\u0000\u0000\u00c1\u00b1\u0001\u0000\u0000"+
"\u0000\u00c1\u00b8\u0001\u0000\u0000\u0000\u00c2\u0017\u0001\u0000\u0000"+
"\u0000\u00c3\u00c8\u0005B\u0000\u0000\u00c4\u00c5\u00059\u0000\u0000\u00c5"+
"\u00c7\u0005B\u0000\u0000\u00c6\u00c4\u0001\u0000\u0000\u0000\u00c7\u00ca"+
"\u0001\u0000\u0000\u0000\u00c8\u00c6\u0001\u0000\u0000\u0000\u00c8\u00c9"+
"\u0001\u0000\u0000\u0000\u00c9\u0019\u0001\u0000\u0000\u0000\u00ca\u00c8"+
"\u0001\u0000\u0000\u0000\u00cb\u00d0\u0003\u001c\u000e\u0000\u00cc\u00cd"+
"\u00059\u0000\u0000\u00cd\u00cf\u0003\u001c\u000e\u0000\u00ce\u00cc\u0001"+
"\u0000\u0000\u0000\u00cf\u00d2\u0001\u0000\u0000\u0000\u00d0\u00ce\u0001"+
"\u0000\u0000\u0000\u00d0\u00d1\u0001\u0000\u0000\u0000\u00d1\u001b\u0001"+
"\u0000\u0000\u0000\u00d2\u00d0\u0001\u0000\u0000\u0000\u00d3\u00d5\u0005"+
"B\u0000\u0000\u00d4\u00d6\u0007\u0003\u0000\u0000\u00d5\u00d4\u0001\u0000"+
"\u0000\u0000\u00d5\u00d6\u0001\u0000\u0000\u0000\u00d6\u00dc\u0001\u0000"+
"\u0000\u0000\u00d7\u00d9\u0003\u0004\u0002\u0000\u00d8\u00da\u0007\u0003"+
"\u0000\u0000\u00d9\u00d8\u0001\u0000\u0000\u0000\u00d9\u00da\u0001\u0000"+
"\u0000\u0000\u00da\u00dc\u0001\u0000\u0000\u0000\u00db\u00d3\u0001\u0000"+
"\u0000\u0000\u00db\u00d7\u0001\u0000\u0000\u0000\u00dc\u001d\u0001\u0000"+
"\u0000\u0000\u00dd\u00de\u0005?\u0000\u0000\u00de\u001f\u0001\u0000\u0000"+
"\u0000\u00df\u00e4\u0003\"\u0011\u0000\u00e0\u00e1\u00059\u0000\u0000"+
"\u00e1\u00e3\u0003\"\u0011\u0000\u00e2\u00e0\u0001\u0000\u0000\u0000\u00e3"+
"\u00e6\u0001\u0000\u0000\u0000\u00e4\u00e2\u0001\u0000\u0000\u0000\u00e4"+
"\u00e5\u0001\u0000\u0000\u0000\u00e5!\u0001\u0000\u0000\u0000\u00e6\u00e4"+
"\u0001\u0000\u0000\u0000\u00e7\u00ec\u0005B\u0000\u0000\u00e8\u00ea\u0005"+
"\u000e\u0000\u0000\u00e9\u00e8\u0001\u0000\u0000\u0000\u00e9\u00ea\u0001"+
"\u0000\u0000\u0000\u00ea\u00eb\u0001\u0000\u0000\u0000\u00eb\u00ed\u0005"+
"B\u0000\u0000\u00ec\u00e9\u0001\u0000\u0000\u0000\u00ec\u00ed\u0001\u0000"+
"\u0000\u0000\u00ed\u00f5\u0001\u0000\u0000\u0000\u00ee\u00ef\u00053\u0000"+
"\u0000\u00ef\u00f0\u0003\u0012\t\u0000\u00f0\u00f1\u00054\u0000\u0000"+
"\u00f1\u00f2\u0005\u000e\u0000\u0000\u00f2\u00f3\u0005B\u0000\u0000\u00f3"+
"\u00f5\u0001\u0000\u0000\u0000\u00f4\u00e7\u0001\u0000\u0000\u0000\u00f4"+
"\u00ee\u0001\u0000\u0000\u0000\u00f5#\u0001\u0000\u0000\u0000\u00f6\u00f7"+
"\u0006\u0012\uffff\uffff\u0000\u00f7\u00fd\u0003(\u0014\u0000\u00f8\u00f9"+
"\u00053\u0000\u0000\u00f9\u00fa\u0003$\u0012\u0000\u00fa\u00fb\u00054"+
"\u0000\u0000\u00fb\u00fd\u0001\u0000\u0000\u0000\u00fc\u00f6\u0001\u0000"+
"\u0000\u0000\u00fc\u00f8\u0001\u0000\u0000\u0000\u00fd\u0106\u0001\u0000"+
"\u0000\u0000\u00fe\u00ff\n\u0004\u0000\u0000\u00ff\u0100\u0005\u0001\u0000"+
"\u0000\u0100\u0105\u0003$\u0012\u0005\u0101\u0102\n\u0003\u0000\u0000"+
"\u0102\u0103\u0005\u0002\u0000\u0000\u0103\u0105\u0003$\u0012\u0004\u0104"+
"\u00fe\u0001\u0000\u0000\u0000\u0104\u0101\u0001\u0000\u0000\u0000\u0105"+
"\u0108\u0001\u0000\u0000\u0000\u0106\u0104\u0001\u0000\u0000\u0000\u0106"+
"\u0107\u0001\u0000\u0000\u0000\u0107%\u0001\u0000\u0000\u0000\u0108\u0106"+
"\u0001\u0000\u0000\u0000\u0109\u0119\u00051\u0000\u0000\u010a\u0119\u0005"+
"/\u0000\u0000\u010b\u0119\u0005-\u0000\u0000\u010c\u0119\u00050\u0000"+
"\u0000\u010d\u0119\u0005.\u0000\u0000\u010e\u0119\u00052\u0000\u0000\u010f"+
"\u0119\u0005\u0014\u0000\u0000\u0110\u0111\u0005\u0004\u0000\u0000\u0111"+
"\u0119\u0005\u0014\u0000\u0000\u0112\u0119\u0005\u0011\u0000\u0000\u0113"+
"\u0114\u0005\u0004\u0000\u0000\u0114\u0119\u0005\u0011\u0000\u0000\u0115"+
"\u0119\u0005\u0012\u0000\u0000\u0116\u0117\u0005\u0012\u0000\u0000\u0117"+
"\u0119\u0005\u0004\u0000\u0000\u0118\u0109\u0001\u0000\u0000\u0000\u0118"+
"\u010a\u0001\u0000\u0000\u0000\u0118\u010b\u0001\u0000\u0000\u0000\u0118"+
"\u010c\u0001\u0000\u0000\u0000\u0118\u010d\u0001\u0000\u0000\u0000\u0118"+
"\u010e\u0001\u0000\u0000\u0000\u0118\u010f\u0001\u0000\u0000\u0000\u0118"+
"\u0110\u0001\u0000\u0000\u0000\u0118\u0112\u0001\u0000\u0000\u0000\u0118"+
"\u0113\u0001\u0000\u0000\u0000\u0118\u0115\u0001\u0000\u0000\u0000\u0118"+
"\u0116\u0001\u0000\u0000\u0000\u0119\'\u0001\u0000\u0000\u0000\u011a\u011b"+
"\u0003*\u0015\u0000\u011b\u011c\u0003&\u0013\u0000\u011c\u011d\u0003*"+
"\u0015\u0000\u011d\u0129\u0001\u0000\u0000\u0000\u011e\u011f\u00053\u0000"+
"\u0000\u011f\u0120\u0003(\u0014\u0000\u0120\u0121\u00054\u0000\u0000\u0121"+
"\u0129\u0001\u0000\u0000\u0000\u0122\u0123\u0005B\u0000\u0000\u0123\u0124"+
"\u0005\u0015\u0000\u0000\u0124\u0125\u0003\n\u0005\u0000\u0125\u0126\u0005"+
"\u0001\u0000\u0000\u0126\u0127\u0003\n\u0005\u0000\u0127\u0129\u0001\u0000"+
"\u0000\u0000\u0128\u011a\u0001\u0000\u0000\u0000\u0128\u011e\u0001\u0000"+
"\u0000\u0000\u0128\u0122\u0001\u0000\u0000\u0000\u0129)\u0001\u0000\u0000"+
"\u0000\u012a\u0137\u0003\n\u0005\u0000\u012b\u0137\u0003\f\u0006\u0000"+
"\u012c\u0137\u0005B\u0000\u0000\u012d\u012e\u0005B\u0000\u0000\u012e\u012f"+
"\u0005:\u0000\u0000\u012f\u0137\u0005B\u0000\u0000\u0130\u0137\u0005\u0013"+
"\u0000\u0000\u0131\u0132\u00053\u0000\u0000\u0132\u0133\u0003\u0012\t"+
"\u0000\u0133\u0134\u00054\u0000\u0000\u0134\u0137\u0001\u0000\u0000\u0000"+
"\u0135\u0137\u0003\u0004\u0002\u0000\u0136\u012a\u0001\u0000\u0000\u0000"+
"\u0136\u012b\u0001\u0000\u0000\u0000\u0136\u012c\u0001\u0000\u0000\u0000"+
"\u0136\u012d\u0001\u0000\u0000\u0000\u0136\u0130\u0001\u0000\u0000\u0000"+
"\u0136\u0131\u0001\u0000\u0000\u0000\u0136\u0135\u0001\u0000\u0000\u0000"+
"\u0137+\u0001\u0000\u0000\u0000\u0138\u0139\u0006\u0016\uffff\uffff\u0000"+
"\u0139\u013b\u0003.\u0017\u0000\u013a\u013c\u00038\u001c\u0000\u013b\u013a"+
"\u0001\u0000\u0000\u0000\u013b\u013c\u0001\u0000\u0000\u0000\u013c\u0149"+
"\u0001\u0000\u0000\u0000\u013d\u013e\u0003:\u001d\u0000\u013e\u013f\u0005"+
"3\u0000\u0000\u013f\u0142\u0003,\u0016\u0000\u0140\u0141\u0005\t\u0000"+
"\u0000\u0141\u0143\u00036\u001b\u0000\u0142\u0140\u0001\u0000\u0000\u0000"+
"\u0142\u0143\u0001\u0000\u0000\u0000\u0143\u0144\u0001\u0000\u0000\u0000"+
"\u0144\u0145\u00054\u0000\u0000\u0145\u0149\u0001\u0000\u0000\u0000\u0146"+
"\u0149\u0003\u0004\u0002\u0000\u0147\u0149\u0005B\u0000\u0000\u0148\u0138"+
"\u0001\u0000\u0000\u0000\u0148\u013d\u0001\u0000\u0000\u0000\u0148\u0146"+
"\u0001\u0000\u0000\u0000\u0148\u0147\u0001\u0000\u0000\u0000\u0149\u015f"+
"\u0001\u0000\u0000\u0000\u014a\u014b\n\u0006\u0000\u0000\u014b\u014c\u0003"+
"<\u001e\u0000\u014c\u014d\u0003,\u0016\u0007\u014d\u015e\u0001\u0000\u0000"+
"\u0000\u014e\u014f\n\u0004\u0000\u0000\u014f\u0150\u00057\u0000\u0000"+
"\u0150\u0151\u0003\u000e\u0007\u0000\u0151\u0152\u00058\u0000\u0000\u0152"+
"\u015e\u0001\u0000\u0000\u0000\u0153\u0154\n\u0003\u0000\u0000\u0154\u0155"+
"\u00057\u0000\u0000\u0155\u0156\u0003\u000e\u0007\u0000\u0156\u0157\u0005"+
";\u0000\u0000\u0157\u0158\u0003\u000e\u0007\u0000\u0158\u0159\u00058\u0000"+
"\u0000\u0159\u015e\u0001\u0000\u0000\u0000\u015a\u015b\n\u0002\u0000\u0000"+
"\u015b\u015c\u0005\r\u0000\u0000\u015c\u015e\u0003\u000e\u0007\u0000\u015d"+
"\u014a\u0001\u0000\u0000\u0000\u015d\u014e\u0001\u0000\u0000\u0000\u015d"+
"\u0153\u0001\u0000\u0000\u0000\u015d\u015a\u0001\u0000\u0000\u0000\u015e"+
"\u0161\u0001\u0000\u0000\u0000\u015f\u015d\u0001\u0000\u0000\u0000\u015f"+
"\u0160\u0001\u0000\u0000\u0000\u0160-\u0001\u0000\u0000\u0000\u0161\u015f"+
"\u0001\u0000\u0000\u0000\u0162\u0164\u00055\u0000\u0000\u0163\u0165\u0003"+
"0\u0018\u0000\u0164\u0163\u0001\u0000\u0000\u0000\u0164\u0165\u0001\u0000"+
"\u0000\u0000\u0165\u0166\u0001\u0000\u0000\u0000\u0166\u0167\u00056\u0000"+
"\u0000\u0167/\u0001\u0000\u0000\u0000\u0168\u016d\u00032\u0019\u0000\u0169"+
"\u016a\u00059\u0000\u0000\u016a\u016c\u00032\u0019\u0000\u016b\u0169\u0001"+
"\u0000\u0000\u0000\u016c\u016f\u0001\u0000\u0000\u0000\u016d\u016b\u0001"+
"\u0000\u0000\u0000\u016d\u016e\u0001\u0000\u0000\u0000\u016e1\u0001\u0000"+
"\u0000\u0000\u016f\u016d\u0001\u0000\u0000\u0000\u0170\u0171\u0005B\u0000"+
"\u0000\u0171\u0172\u00034\u001a\u0000\u0172\u0173\u0003\f\u0006\u0000"+
"\u01733\u0001\u0000\u0000\u0000\u0174\u0175\u0007\u0004\u0000\u0000\u0175"+
"5\u0001\u0000\u0000\u0000\u0176\u0177\u00053\u0000\u0000\u0177\u017c\u0005"+
"B\u0000\u0000\u0178\u0179\u00059\u0000\u0000\u0179\u017b\u0005B\u0000"+
"\u0000\u017a\u0178\u0001\u0000\u0000\u0000\u017b\u017e\u0001\u0000\u0000"+
"\u0000\u017c\u017a\u0001\u0000\u0000\u0000\u017c\u017d\u0001\u0000\u0000"+
"\u0000\u017d\u017f\u0001\u0000\u0000\u0000\u017e\u017c\u0001\u0000\u0000"+
"\u0000\u017f\u0180\u00054\u0000\u0000\u01807\u0001\u0000\u0000\u0000\u0181"+
"\u0182\u00057\u0000\u0000\u0182\u0183\u0003\u000e\u0007\u0000\u0183\u0184"+
"\u00058\u0000\u0000\u01849\u0001\u0000\u0000\u0000\u0185\u0186\u0007\u0005"+
"\u0000\u0000\u0186;\u0001\u0000\u0000\u0000\u0187\u0188\u0007\u0006\u0000"+
"\u0000\u0188=\u0001\u0000\u0000\u0000+Sacpw\u0087\u008c\u0090\u0095\u0099"+
"\u00a0\u00a5\u00a8\u00ac\u00af\u00b3\u00b6\u00bc\u00bf\u00c1\u00c8\u00d0"+
"\u00d5\u00d9\u00db\u00e4\u00e9\u00ec\u00f4\u00fc\u0104\u0106\u0118\u0128"+
"\u0136\u013b\u0142\u0148\u015d\u015f\u0164\u016d\u017c";
"\u0016\u0005\u0016\u015a\b\u0016\n\u0016\f\u0016\u015d\t\u0016\u0001\u0017"+
"\u0001\u0017\u0003\u0017\u0161\b\u0017\u0001\u0017\u0001\u0017\u0001\u0018"+
"\u0001\u0018\u0001\u0018\u0005\u0018\u0168\b\u0018\n\u0018\f\u0018\u016b"+
"\t\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001"+
"\u001a\u0001\u001b\u0001\u001b\u0001\u001b\u0001\u001b\u0005\u001b\u0177"+
"\b\u001b\n\u001b\f\u001b\u017a\t\u001b\u0001\u001b\u0001\u001b\u0001\u001c"+
"\u0001\u001c\u0001\u001c\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001e"+
"\u0001\u001e\u0001\u001e\u0000\u0003\u0002$,\u001f\u0000\u0002\u0004\u0006"+
"\b\n\f\u000e\u0010\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(*,."+
"02468:<\u0000\u0007\u0001\u0000-2\u0001\u0000=?\u0004\u0000\u0017\u001b"+
"\u001f!%*BB\u0001\u0000\u000f\u0010\u0001\u000012\u0002\u0000\u0017\u001d"+
"\"$\u0002\u0000\u0001\u0003-2\u01ae\u0000>\u0001\u0000\u0000\u0000\u0002"+
"S\u0001\u0000\u0000\u0000\u0004f\u0001\u0000\u0000\u0000\u0006k\u0001"+
"\u0000\u0000\u0000\bw\u0001\u0000\u0000\u0000\ny\u0001\u0000\u0000\u0000"+
"\f{\u0001\u0000\u0000\u0000\u000e}\u0001\u0000\u0000\u0000\u0010\u007f"+
"\u0001\u0000\u0000\u0000\u0012\u0081\u0001\u0000\u0000\u0000\u0014\u009b"+
"\u0001\u0000\u0000\u0000\u0016\u00c1\u0001\u0000\u0000\u0000\u0018\u00c3"+
"\u0001\u0000\u0000\u0000\u001a\u00cb\u0001\u0000\u0000\u0000\u001c\u00db"+
"\u0001\u0000\u0000\u0000\u001e\u00dd\u0001\u0000\u0000\u0000 \u00df\u0001"+
"\u0000\u0000\u0000\"\u00f4\u0001\u0000\u0000\u0000$\u00fc\u0001\u0000"+
"\u0000\u0000&\u0118\u0001\u0000\u0000\u0000(\u0128\u0001\u0000\u0000\u0000"+
"*\u0136\u0001\u0000\u0000\u0000,\u0148\u0001\u0000\u0000\u0000.\u015e"+
"\u0001\u0000\u0000\u00000\u0164\u0001\u0000\u0000\u00002\u016c\u0001\u0000"+
"\u0000\u00004\u0170\u0001\u0000\u0000\u00006\u0172\u0001\u0000\u0000\u0000"+
"8\u017d\u0001\u0000\u0000\u0000:\u0181\u0001\u0000\u0000\u0000<\u0183"+
"\u0001\u0000\u0000\u0000>?\u0003\u0002\u0001\u0000?@\u0005\u0000\u0000"+
"\u0001@\u0001\u0001\u0000\u0000\u0000AB\u0006\u0001\uffff\uffff\u0000"+
"BC\u00053\u0000\u0000CD\u0003\u0002\u0001\u0000DE\u00054\u0000\u0000E"+
"T\u0001\u0000\u0000\u0000FT\u0003,\u0016\u0000GT\u0003\u0012\t\u0000H"+
"T\u0003\n\u0005\u0000IJ\u0005+\u0000\u0000JK\u00053\u0000\u0000KL\u0003"+
"\f\u0006\u0000LM\u00054\u0000\u0000MT\u0001\u0000\u0000\u0000NO\u0005"+
",\u0000\u0000OP\u00053\u0000\u0000PQ\u0003\f\u0006\u0000QR\u00054\u0000"+
"\u0000RT\u0001\u0000\u0000\u0000SA\u0001\u0000\u0000\u0000SF\u0001\u0000"+
"\u0000\u0000SG\u0001\u0000\u0000\u0000SH\u0001\u0000\u0000\u0000SI\u0001"+
"\u0000\u0000\u0000SN\u0001\u0000\u0000\u0000Tc\u0001\u0000\u0000\u0000"+
"UV\n\t\u0000\u0000VW\u0007\u0000\u0000\u0000Wb\u0003\u0002\u0001\nXY\n"+
"\b\u0000\u0000YZ\u0005\u0001\u0000\u0000Zb\u0003\u0002\u0001\t[\\\n\u0007"+
"\u0000\u0000\\]\u0005\u0003\u0000\u0000]b\u0003\u0002\u0001\b^_\n\u0006"+
"\u0000\u0000_`\u0005\u0002\u0000\u0000`b\u0003\u0002\u0001\u0007aU\u0001"+
"\u0000\u0000\u0000aX\u0001\u0000\u0000\u0000a[\u0001\u0000\u0000\u0000"+
"a^\u0001\u0000\u0000\u0000be\u0001\u0000\u0000\u0000ca\u0001\u0000\u0000"+
"\u0000cd\u0001\u0000\u0000\u0000d\u0003\u0001\u0000\u0000\u0000ec\u0001"+
"\u0000\u0000\u0000fg\u0003\u0010\b\u0000gh\u00053\u0000\u0000hi\u0003"+
"\u0006\u0003\u0000ij\u00054\u0000\u0000j\u0005\u0001\u0000\u0000\u0000"+
"kp\u0003\b\u0004\u0000lm\u00059\u0000\u0000mo\u0003\b\u0004\u0000nl\u0001"+
"\u0000\u0000\u0000or\u0001\u0000\u0000\u0000pn\u0001\u0000\u0000\u0000"+
"pq\u0001\u0000\u0000\u0000q\u0007\u0001\u0000\u0000\u0000rp\u0001\u0000"+
"\u0000\u0000sx\u0003\u0002\u0001\u0000tx\u0005\u0016\u0000\u0000ux\u0003"+
"\f\u0006\u0000vx\u0003\u000e\u0007\u0000ws\u0001\u0000\u0000\u0000wt\u0001"+
"\u0000\u0000\u0000wu\u0001\u0000\u0000\u0000wv\u0001\u0000\u0000\u0000"+
"x\t\u0001\u0000\u0000\u0000yz\u0007\u0001\u0000\u0000z\u000b\u0001\u0000"+
"\u0000\u0000{|\u0005A\u0000\u0000|\r\u0001\u0000\u0000\u0000}~\u0005@"+
"\u0000\u0000~\u000f\u0001\u0000\u0000\u0000\u007f\u0080\u0007\u0002\u0000"+
"\u0000\u0080\u0011\u0001\u0000\u0000\u0000\u0081\u0082\u0005\u0005\u0000"+
"\u0000\u0082\u0083\u0003\u0014\n\u0000\u0083\u0084\u0005\u0006\u0000\u0000"+
"\u0084\u0087\u0003 \u0010\u0000\u0085\u0086\u0005\u0007\u0000\u0000\u0086"+
"\u0088\u0003$\u0012\u0000\u0087\u0085\u0001\u0000\u0000\u0000\u0087\u0088"+
"\u0001\u0000\u0000\u0000\u0088\u008c\u0001\u0000\u0000\u0000\u0089\u008a"+
"\u0005\b\u0000\u0000\u008a\u008b\u0005\t\u0000\u0000\u008b\u008d\u0003"+
"\u0018\f\u0000\u008c\u0089\u0001\u0000\u0000\u0000\u008c\u008d\u0001\u0000"+
"\u0000\u0000\u008d\u0090\u0001\u0000\u0000\u0000\u008e\u008f\u0005\n\u0000"+
"\u0000\u008f\u0091\u0003$\u0012\u0000\u0090\u008e\u0001\u0000\u0000\u0000"+
"\u0090\u0091\u0001\u0000\u0000\u0000\u0091\u0095\u0001\u0000\u0000\u0000"+
"\u0092\u0093\u0005\u000b\u0000\u0000\u0093\u0094\u0005\t\u0000\u0000\u0094"+
"\u0096\u0003\u001a\r\u0000\u0095\u0092\u0001\u0000\u0000\u0000\u0095\u0096"+
"\u0001\u0000\u0000\u0000\u0096\u0099\u0001\u0000\u0000\u0000\u0097\u0098"+
"\u0005\f\u0000\u0000\u0098\u009a\u0003\u001e\u000f\u0000\u0099\u0097\u0001"+
"\u0000\u0000\u0000\u0099\u009a\u0001\u0000\u0000\u0000\u009a\u0013\u0001"+
"\u0000\u0000\u0000\u009b\u00a0\u0003\u0016\u000b\u0000\u009c\u009d\u0005"+
"9\u0000\u0000\u009d\u009f\u0003\u0016\u000b\u0000\u009e\u009c\u0001\u0000"+
"\u0000\u0000\u009f\u00a2\u0001\u0000\u0000\u0000\u00a0\u009e\u0001\u0000"+
"\u0000\u0000\u00a0\u00a1\u0001\u0000\u0000\u0000\u00a1\u0015\u0001\u0000"+
"\u0000\u0000\u00a2\u00a0\u0001\u0000\u0000\u0000\u00a3\u00a8\u0003\u0004"+
"\u0002\u0000\u00a4\u00a6\u0005\u000e\u0000\u0000\u00a5\u00a4\u0001\u0000"+
"\u0000\u0000\u00a5\u00a6\u0001\u0000\u0000\u0000\u00a6\u00a7\u0001\u0000"+
"\u0000\u0000\u00a7\u00a9\u0005B\u0000\u0000\u00a8\u00a5\u0001\u0000\u0000"+
"\u0000\u00a8\u00a9\u0001\u0000\u0000\u0000\u00a9\u00c2\u0001\u0000\u0000"+
"\u0000\u00aa\u00af\u0005B\u0000\u0000\u00ab\u00ad\u0005\u000e\u0000\u0000"+
"\u00ac\u00ab\u0001\u0000\u0000\u0000\u00ac\u00ad\u0001\u0000\u0000\u0000"+
"\u00ad\u00ae\u0001\u0000\u0000\u0000\u00ae\u00b0\u0005B\u0000\u0000\u00af"+
"\u00ac\u0001\u0000\u0000\u0000\u00af\u00b0\u0001\u0000\u0000\u0000\u00b0"+
"\u00c2\u0001\u0000\u0000\u0000\u00b1\u00b6\u0005\u0016\u0000\u0000\u00b2"+
"\u00b4\u0005\u000e\u0000\u0000\u00b3\u00b2\u0001\u0000\u0000\u0000\u00b3"+
"\u00b4\u0001\u0000\u0000\u0000\u00b4\u00b5\u0001\u0000\u0000\u0000\u00b5"+
"\u00b7\u0005B\u0000\u0000\u00b6\u00b3\u0001\u0000\u0000\u0000\u00b6\u00b7"+
"\u0001\u0000\u0000\u0000\u00b7\u00c2\u0001\u0000\u0000\u0000\u00b8\u00b9"+
"\u0005B\u0000\u0000\u00b9\u00ba\u0005:\u0000\u0000\u00ba\u00bf\u0005B"+
"\u0000\u0000\u00bb\u00bd\u0005\u000e\u0000\u0000\u00bc\u00bb\u0001\u0000"+
"\u0000\u0000\u00bc\u00bd\u0001\u0000\u0000\u0000\u00bd\u00be\u0001\u0000"+
"\u0000\u0000\u00be\u00c0\u0005B\u0000\u0000\u00bf\u00bc\u0001\u0000\u0000"+
"\u0000\u00bf\u00c0\u0001\u0000\u0000\u0000\u00c0\u00c2\u0001\u0000\u0000"+
"\u0000\u00c1\u00a3\u0001\u0000\u0000\u0000\u00c1\u00aa\u0001\u0000\u0000"+
"\u0000\u00c1\u00b1\u0001\u0000\u0000\u0000\u00c1\u00b8\u0001\u0000\u0000"+
"\u0000\u00c2\u0017\u0001\u0000\u0000\u0000\u00c3\u00c8\u0005B\u0000\u0000"+
"\u00c4\u00c5\u00059\u0000\u0000\u00c5\u00c7\u0005B\u0000\u0000\u00c6\u00c4"+
"\u0001\u0000\u0000\u0000\u00c7\u00ca\u0001\u0000\u0000\u0000\u00c8\u00c6"+
"\u0001\u0000\u0000\u0000\u00c8\u00c9\u0001\u0000\u0000\u0000\u00c9\u0019"+
"\u0001\u0000\u0000\u0000\u00ca\u00c8\u0001\u0000\u0000\u0000\u00cb\u00d0"+
"\u0003\u001c\u000e\u0000\u00cc\u00cd\u00059\u0000\u0000\u00cd\u00cf\u0003"+
"\u001c\u000e\u0000\u00ce\u00cc\u0001\u0000\u0000\u0000\u00cf\u00d2\u0001"+
"\u0000\u0000\u0000\u00d0\u00ce\u0001\u0000\u0000\u0000\u00d0\u00d1\u0001"+
"\u0000\u0000\u0000\u00d1\u001b\u0001\u0000\u0000\u0000\u00d2\u00d0\u0001"+
"\u0000\u0000\u0000\u00d3\u00d5\u0005B\u0000\u0000\u00d4\u00d6\u0007\u0003"+
"\u0000\u0000\u00d5\u00d4\u0001\u0000\u0000\u0000\u00d5\u00d6\u0001\u0000"+
"\u0000\u0000\u00d6\u00dc\u0001\u0000\u0000\u0000\u00d7\u00d9\u0003\u0004"+
"\u0002\u0000\u00d8\u00da\u0007\u0003\u0000\u0000\u00d9\u00d8\u0001\u0000"+
"\u0000\u0000\u00d9\u00da\u0001\u0000\u0000\u0000\u00da\u00dc\u0001\u0000"+
"\u0000\u0000\u00db\u00d3\u0001\u0000\u0000\u0000\u00db\u00d7\u0001\u0000"+
"\u0000\u0000\u00dc\u001d\u0001\u0000\u0000\u0000\u00dd\u00de\u0005?\u0000"+
"\u0000\u00de\u001f\u0001\u0000\u0000\u0000\u00df\u00e4\u0003\"\u0011\u0000"+
"\u00e0\u00e1\u00059\u0000\u0000\u00e1\u00e3\u0003\"\u0011\u0000\u00e2"+
"\u00e0\u0001\u0000\u0000\u0000\u00e3\u00e6\u0001\u0000\u0000\u0000\u00e4"+
"\u00e2\u0001\u0000\u0000\u0000\u00e4\u00e5\u0001\u0000\u0000\u0000\u00e5"+
"!\u0001\u0000\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000\u0000\u00e7\u00ec"+
"\u0005B\u0000\u0000\u00e8\u00ea\u0005\u000e\u0000\u0000\u00e9\u00e8\u0001"+
"\u0000\u0000\u0000\u00e9\u00ea\u0001\u0000\u0000\u0000\u00ea\u00eb\u0001"+
"\u0000\u0000\u0000\u00eb\u00ed\u0005B\u0000\u0000\u00ec\u00e9\u0001\u0000"+
"\u0000\u0000\u00ec\u00ed\u0001\u0000\u0000\u0000\u00ed\u00f5\u0001\u0000"+
"\u0000\u0000\u00ee\u00ef\u00053\u0000\u0000\u00ef\u00f0\u0003\u0012\t"+
"\u0000\u00f0\u00f1\u00054\u0000\u0000\u00f1\u00f2\u0005\u000e\u0000\u0000"+
"\u00f2\u00f3\u0005B\u0000\u0000\u00f3\u00f5\u0001\u0000\u0000\u0000\u00f4"+
"\u00e7\u0001\u0000\u0000\u0000\u00f4\u00ee\u0001\u0000\u0000\u0000\u00f5"+
"#\u0001\u0000\u0000\u0000\u00f6\u00f7\u0006\u0012\uffff\uffff\u0000\u00f7"+
"\u00fd\u0003(\u0014\u0000\u00f8\u00f9\u00053\u0000\u0000\u00f9\u00fa\u0003"+
"$\u0012\u0000\u00fa\u00fb\u00054\u0000\u0000\u00fb\u00fd\u0001\u0000\u0000"+
"\u0000\u00fc\u00f6\u0001\u0000\u0000\u0000\u00fc\u00f8\u0001\u0000\u0000"+
"\u0000\u00fd\u0106\u0001\u0000\u0000\u0000\u00fe\u00ff\n\u0004\u0000\u0000"+
"\u00ff\u0100\u0005\u0001\u0000\u0000\u0100\u0105\u0003$\u0012\u0005\u0101"+
"\u0102\n\u0003\u0000\u0000\u0102\u0103\u0005\u0002\u0000\u0000\u0103\u0105"+
"\u0003$\u0012\u0004\u0104\u00fe\u0001\u0000\u0000\u0000\u0104\u0101\u0001"+
"\u0000\u0000\u0000\u0105\u0108\u0001\u0000\u0000\u0000\u0106\u0104\u0001"+
"\u0000\u0000\u0000\u0106\u0107\u0001\u0000\u0000\u0000\u0107%\u0001\u0000"+
"\u0000\u0000\u0108\u0106\u0001\u0000\u0000\u0000\u0109\u0119\u00051\u0000"+
"\u0000\u010a\u0119\u0005/\u0000\u0000\u010b\u0119\u0005-\u0000\u0000\u010c"+
"\u0119\u00050\u0000\u0000\u010d\u0119\u0005.\u0000\u0000\u010e\u0119\u0005"+
"2\u0000\u0000\u010f\u0119\u0005\u0014\u0000\u0000\u0110\u0111\u0005\u0004"+
"\u0000\u0000\u0111\u0119\u0005\u0014\u0000\u0000\u0112\u0119\u0005\u0011"+
"\u0000\u0000\u0113\u0114\u0005\u0004\u0000\u0000\u0114\u0119\u0005\u0011"+
"\u0000\u0000\u0115\u0119\u0005\u0012\u0000\u0000\u0116\u0117\u0005\u0012"+
"\u0000\u0000\u0117\u0119\u0005\u0004\u0000\u0000\u0118\u0109\u0001\u0000"+
"\u0000\u0000\u0118\u010a\u0001\u0000\u0000\u0000\u0118\u010b\u0001\u0000"+
"\u0000\u0000\u0118\u010c\u0001\u0000\u0000\u0000\u0118\u010d\u0001\u0000"+
"\u0000\u0000\u0118\u010e\u0001\u0000\u0000\u0000\u0118\u010f\u0001\u0000"+
"\u0000\u0000\u0118\u0110\u0001\u0000\u0000\u0000\u0118\u0112\u0001\u0000"+
"\u0000\u0000\u0118\u0113\u0001\u0000\u0000\u0000\u0118\u0115\u0001\u0000"+
"\u0000\u0000\u0118\u0116\u0001\u0000\u0000\u0000\u0119\'\u0001\u0000\u0000"+
"\u0000\u011a\u011b\u0003*\u0015\u0000\u011b\u011c\u0003&\u0013\u0000\u011c"+
"\u011d\u0003*\u0015\u0000\u011d\u0129\u0001\u0000\u0000\u0000\u011e\u011f"+
"\u00053\u0000\u0000\u011f\u0120\u0003(\u0014\u0000\u0120\u0121\u00054"+
"\u0000\u0000\u0121\u0129\u0001\u0000\u0000\u0000\u0122\u0123\u0005B\u0000"+
"\u0000\u0123\u0124\u0005\u0015\u0000\u0000\u0124\u0125\u0003\n\u0005\u0000"+
"\u0125\u0126\u0005\u0001\u0000\u0000\u0126\u0127\u0003\n\u0005\u0000\u0127"+
"\u0129\u0001\u0000\u0000\u0000\u0128\u011a\u0001\u0000\u0000\u0000\u0128"+
"\u011e\u0001\u0000\u0000\u0000\u0128\u0122\u0001\u0000\u0000\u0000\u0129"+
")\u0001\u0000\u0000\u0000\u012a\u0137\u0003\n\u0005\u0000\u012b\u0137"+
"\u0003\f\u0006\u0000\u012c\u0137\u0005B\u0000\u0000\u012d\u012e\u0005"+
"B\u0000\u0000\u012e\u012f\u0005:\u0000\u0000\u012f\u0137\u0005B\u0000"+
"\u0000\u0130\u0137\u0005\u0013\u0000\u0000\u0131\u0132\u00053\u0000\u0000"+
"\u0132\u0133\u0003\u0012\t\u0000\u0133\u0134\u00054\u0000\u0000\u0134"+
"\u0137\u0001\u0000\u0000\u0000\u0135\u0137\u0003\u0004\u0002\u0000\u0136"+
"\u012a\u0001\u0000\u0000\u0000\u0136\u012b\u0001\u0000\u0000\u0000\u0136"+
"\u012c\u0001\u0000\u0000\u0000\u0136\u012d\u0001\u0000\u0000\u0000\u0136"+
"\u0130\u0001\u0000\u0000\u0000\u0136\u0131\u0001\u0000\u0000\u0000\u0136"+
"\u0135\u0001\u0000\u0000\u0000\u0137+\u0001\u0000\u0000\u0000\u0138\u0139"+
"\u0006\u0016\uffff\uffff\u0000\u0139\u013b\u0003.\u0017\u0000\u013a\u013c"+
"\u00038\u001c\u0000\u013b\u013a\u0001\u0000\u0000\u0000\u013b\u013c\u0001"+
"\u0000\u0000\u0000\u013c\u0149\u0001\u0000\u0000\u0000\u013d\u013e\u0003"+
":\u001d\u0000\u013e\u013f\u00053\u0000\u0000\u013f\u0142\u0003,\u0016"+
"\u0000\u0140\u0141\u0005\t\u0000\u0000\u0141\u0143\u00036\u001b\u0000"+
"\u0142\u0140\u0001\u0000\u0000\u0000\u0142\u0143\u0001\u0000\u0000\u0000"+
"\u0143\u0144\u0001\u0000\u0000\u0000\u0144\u0145\u00054\u0000\u0000\u0145"+
"\u0149\u0001\u0000\u0000\u0000\u0146\u0149\u0003\u0004\u0002\u0000\u0147"+
"\u0149\u0005B\u0000\u0000\u0148\u0138\u0001\u0000\u0000\u0000\u0148\u013d"+
"\u0001\u0000\u0000\u0000\u0148\u0146\u0001\u0000\u0000\u0000\u0148\u0147"+
"\u0001\u0000\u0000\u0000\u0149\u015b\u0001\u0000\u0000\u0000\u014a\u014b"+
"\n\u0004\u0000\u0000\u014b\u014c\u00057\u0000\u0000\u014c\u014d\u0003"+
"\u000e\u0007\u0000\u014d\u014e\u00058\u0000\u0000\u014e\u015a\u0001\u0000"+
"\u0000\u0000\u014f\u0150\n\u0003\u0000\u0000\u0150\u0151\u00057\u0000"+
"\u0000\u0151\u0152\u0003\u000e\u0007\u0000\u0152\u0153\u0005;\u0000\u0000"+
"\u0153\u0154\u0003\u000e\u0007\u0000\u0154\u0155\u00058\u0000\u0000\u0155"+
"\u015a\u0001\u0000\u0000\u0000\u0156\u0157\n\u0002\u0000\u0000\u0157\u0158"+
"\u0005\r\u0000\u0000\u0158\u015a\u0003\u000e\u0007\u0000\u0159\u014a\u0001"+
"\u0000\u0000\u0000\u0159\u014f\u0001\u0000\u0000\u0000\u0159\u0156\u0001"+
"\u0000\u0000\u0000\u015a\u015d\u0001\u0000\u0000\u0000\u015b\u0159\u0001"+
"\u0000\u0000\u0000\u015b\u015c\u0001\u0000\u0000\u0000\u015c-\u0001\u0000"+
"\u0000\u0000\u015d\u015b\u0001\u0000\u0000\u0000\u015e\u0160\u00055\u0000"+
"\u0000\u015f\u0161\u00030\u0018\u0000\u0160\u015f\u0001\u0000\u0000\u0000"+
"\u0160\u0161\u0001\u0000\u0000\u0000\u0161\u0162\u0001\u0000\u0000\u0000"+
"\u0162\u0163\u00056\u0000\u0000\u0163/\u0001\u0000\u0000\u0000\u0164\u0169"+
"\u00032\u0019\u0000\u0165\u0166\u00059\u0000\u0000\u0166\u0168\u00032"+
"\u0019\u0000\u0167\u0165\u0001\u0000\u0000\u0000\u0168\u016b\u0001\u0000"+
"\u0000\u0000\u0169\u0167\u0001\u0000\u0000\u0000\u0169\u016a\u0001\u0000"+
"\u0000\u0000\u016a1\u0001\u0000\u0000\u0000\u016b\u0169\u0001\u0000\u0000"+
"\u0000\u016c\u016d\u0005B\u0000\u0000\u016d\u016e\u00034\u001a\u0000\u016e"+
"\u016f\u0003\f\u0006\u0000\u016f3\u0001\u0000\u0000\u0000\u0170\u0171"+
"\u0007\u0004\u0000\u0000\u01715\u0001\u0000\u0000\u0000\u0172\u0173\u0005"+
"3\u0000\u0000\u0173\u0178\u0005B\u0000\u0000\u0174\u0175\u00059\u0000"+
"\u0000\u0175\u0177\u0005B\u0000\u0000\u0176\u0174\u0001\u0000\u0000\u0000"+
"\u0177\u017a\u0001\u0000\u0000\u0000\u0178\u0176\u0001\u0000\u0000\u0000"+
"\u0178\u0179\u0001\u0000\u0000\u0000\u0179\u017b\u0001\u0000\u0000\u0000"+
"\u017a\u0178\u0001\u0000\u0000\u0000\u017b\u017c\u00054\u0000\u0000\u017c"+
"7\u0001\u0000\u0000\u0000\u017d\u017e\u00057\u0000\u0000\u017e\u017f\u0003"+
"\u000e\u0007\u0000\u017f\u0180\u00058\u0000\u0000\u01809\u0001\u0000\u0000"+
"\u0000\u0181\u0182\u0007\u0005\u0000\u0000\u0182;\u0001\u0000\u0000\u0000"+
"\u0183\u0184\u0007\u0006\u0000\u0000\u0184=\u0001\u0000\u0000\u0000+S"+
"acpw\u0087\u008c\u0090\u0095\u0099\u00a0\u00a5\u00a8\u00ac\u00af\u00b3"+
"\u00b6\u00bc\u00bf\u00c1\u00c8\u00d0\u00d5\u00d9\u00db\u00e4\u00e9\u00ec"+
"\u00f4\u00fc\u0104\u0106\u0118\u0128\u0136\u013b\u0142\u0148\u0159\u015b"+
"\u0160\u0169\u0178";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
@@ -17,8 +17,6 @@
package org.apache.hertzbeat.alert.notice.impl;
import java.util.Arrays;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -29,11 +27,14 @@ import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* Send alert information through FeiShu
*/
@@ -47,19 +48,18 @@ final class FlyBookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
*/
private static final String[] TITLE_COLOR = {"red", "yellow", "orange"};
@Override
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
try {
String notificationContent = JsonUtil.toJson(renderContent(noticeTemplate, alert));
// todo priority custom the color
// todo priority custom the color
String cardMessage = createLarkMessage(receiver.getUserId(), notificationContent, (byte) 1);
String webHookUrl = alerterProperties.getFlyBookWebhookUrl() + receiver.getAccessToken();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> flyEntity = new HttpEntity<>(cardMessage, headers);
ResponseEntity<CommonRobotNotifyResp> entity = restTemplate.postForEntity(webHookUrl,
flyEntity, CommonRobotNotifyResp.class);
flyEntity, CommonRobotNotifyResp.class);
if (entity.getStatusCode() == HttpStatus.OK) {
assert entity.getBody() != null;
if (entity.getBody().getCode() == null || entity.getBody().getCode() == 0) {
@@ -87,115 +87,120 @@ final class FlyBookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
*/
private String createLarkMessage(String userId, String notificationContent, byte priority) {
String larkCardMessage = """
{
"msg_type": "interactive",
"card": {
"config": {
"update_multi": true
},
"i18n_elements": {
"zh_cn": [
{
"tag": "column_set",
"flex_mode": "none",
"horizontal_spacing": "default",
"background_style": "default",
"columns": [
{
"tag": "column",
"elements": [
{
"tag": "div",
"text": {
"tag": "plain_text",
"content": "",
"text_size": "normal",
"text_align": "left",
"text_color": "default"
}
}
],
"width": "weighted",
"weight": 1
}
]
},
{
"tag": "column_set",
"flex_mode": "none",
"horizontal_spacing": "default",
"background_style": "default",
"columns": [
{
"tag": "column",
"elements": [
{
"tag": "div",
"text": {
"tag": "plain_text",
"content": %s,
"text_size": "normal",
"text_align": "left",
"text_color": "default"
}
}
],
"width": "weighted",
"weight": 1
}
]
},
%s
{
"tag": "action",
"actions": [
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "登入控制台"
},
"type": "default",
"complex_interaction": true,
"width": "default",
"size": "medium",
"multi_url": {
"url": "%s"
}
}
]
}
]
},
"i18n_header": {
"zh_cn": {
"title": {
"tag": "plain_text",
"content": "HertzBeat 告警"
},
"template": "%s"
}
}
{
"msg_type": "interactive",
"card": {
"schema": "2.0",
"config": {
"update_multi": true,
"locales": [
"en_us",
"zh_cn"
],
"style": {
"text_size": {
"normal_v2": {
"default": "normal",
"pc": "normal",
"mobile": "heading"
}
}
}
},
"body": {
"direction": "vertical",
"padding": "12px 12px 12px 12px",
"elements": [
{
"tag": "markdown",
"content": "%s",
"i18n_content": {
"en_us": ""
},
"text_align": "left",
"text_size": "normal_v2",
"margin": "0px 0px 0px 0px"
},
{
"tag": "hr",
"margin": "0px 0px 0px 0px"
},
{
"tag": "column_set",
"horizontal_align": "left",
"columns": [
{
"tag": "column",
"width": "weighted",
"elements": [
{
"tag": "button",
"text": {
"tag": "plain_text",
"content": "登入控制台",
"i18n_content": {
"en_us": "Login In"
}
},
"type": "default",
"width": "default",
"size": "medium",
"behaviors": [
{
"type": "open_url",
"default_url": "%s",
"pc_url": "",
"ios_url": "",
"android_url": ""
}
]
}
],
"direction": "horizontal",
"vertical_spacing": "8px",
"horizontal_align": "left",
"vertical_align": "top",
"weight": 1
}
],
"margin": "0px 0px 0px 0px"
}
]
},
"header": {
"title": {
"tag": "plain_text",
"content": "HertzBeat 告警",
"i18n_content": {
"en_us": "HertzBeat Alarm"
}
},
"subtitle": {
"tag": "plain_text",
"content": ""
},
"template": "%s",
"padding": "12px 12px 12px 12px"
}
}
}
}
""";
""";
String atUserElement = "";
if (StringUtils.isNotBlank(userId)) {
String atUserId = Arrays.stream(userId.split(","))
.map(id -> "<at id=" + id + "></at>")
.collect(Collectors.joining(" "));
atUserElement = String.format("""
{
"tag": "div",
"text": {
"content": "%s",
"tag": "lark_md"
}
},
""", atUserId);
atUserElement = "\\n" + Arrays.stream(userId.split(","))
.map(id -> "<at id=" + id + "></at>")
.collect(Collectors.joining(" "));
}
return String.format(larkCardMessage, notificationContent, atUserElement, alerterProperties.getConsoleUrl(), TITLE_COLOR[priority]);
if (notificationContent.startsWith("\"") && notificationContent.endsWith("\"")) {
notificationContent = StringUtils.removeStart(notificationContent, "\"");
notificationContent = StringUtils.removeEnd(notificationContent, "\"");
}
return String.format(larkCardMessage,
notificationContent.replace("\"", "\\\"") + atUserElement,
alerterProperties.getConsoleUrl(), TITLE_COLOR[priority]);
}
@Override
@@ -156,7 +156,6 @@ conditionUnit
promql
: metricSelector instantVectorOp?
| aggregationOperator LPAREN promql (BY labelList)? RPAREN
| promql binaryOperator promql
| functionCall
| promql LBRACKET duration RBRACKET
| promql LBRACKET duration COLON duration RBRACKET
@@ -17,6 +17,7 @@
package org.apache.hertzbeat.collector.collect.database;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
@@ -74,6 +75,7 @@ public class JdbcCommonCollect extends AbstractCollect {
// code execution related - may result in remote code execution
"init=", "javaobjectserializer=", "runscript", "serverstatusdiffinterceptor",
"queryinterceptors=", "statementinterceptors=", "exceptioninterceptors=",
"xp_cmdshell", "create function", "dbms_java", "sp_sysexecute", "load_file",
// multiple statement execution - may lead to SQL injection
"allowmultiqueries",
@@ -82,6 +84,96 @@ public class JdbcCommonCollect extends AbstractCollect {
"autodeserialize", "detectcustomcollations",
};
// universal bypass detection mode - applicable to all databases for dangerous command bypass detection
private static final String[] UNIVERSAL_BYPASS_PATTERNS = {
".*create\\s*([/\\\\]|\\\\n|/n|\\n)\\s*trigger.*",
".*create\\s*([/\\\\]|\\\\n|/n|\\n)\\s*function.*",
".*drop\\s*([/\\\\]|\\\\n|/n|\\n)\\s*table.*",
".*drop\\s*([/\\\\]|\\\\n|/n|\\n)\\s*database.*",
".*run\\s*([/\\\\]|\\\\n|/n|\\n)\\s*script.*",
".*alter\\s*([/\\\\]|\\\\n|/n|\\n)\\s*system.*",
".*grant\\s*([/\\\\]|\\\\n|/n|\\n)\\s*all.*",
".*revoke\\s*([/\\\\]|\\\\n|/n|\\n)\\s*all.*",
".*xp\\s*([/\\\\]|\\\\n|/n|\\n)\\s*cmdshell.*",
".*load\\s*([/\\\\]|\\\\n|/n|\\n)\\s*file.*"
};
// database platform specific bypass detection mode
private static final HashMap<String, String[]> PLATFORM_BYPASS_PATTERNS = new HashMap<>();
static {
// H2 database special character bypass mode
PLATFORM_BYPASS_PATTERNS.put("h2", new String[]{
".*(\\\\\\\\|/|\\\\|\\\\n|/n|\\n)\\s*init\\s*=.*",
".*in\\s*([/\\\\]|\\\\n|/n|\\n)\\s*it\\s*=.*",
".*(\\\\\\\\|/|\\\\|\\\\n|/n|\\n)\\s*runscript\\s+from.*",
".*ru\\s*([/\\\\]|\\\\n|/n|\\n)\\s*script\\s+from.*"
});
// MySQL/MariaDB bypass mode
String[] mysqlPatterns = {
".*allow\\s*([/\\\\]|\\\\n|/n|\\n)\\s*load\\s*([/\\\\]|\\\\n|/n|\\n)\\s*local\\s*([/\\\\]|\\\\n|/n|\\n)\\s*infile.*",
".*allow\\s*([/\\\\]|\\\\n|/n|\\n)\\s*multi\\s*([/\\\\]|\\\\n|/n|\\n)\\s*queries.*",
".*query\\s*([/\\\\]|\\\\n|/n|\\n)\\s*interceptors.*",
".*statement\\s*([/\\\\]|\\\\n|/n|\\n)\\s*interceptors.*",
".*exception\\s*([/\\\\]|\\\\n|/n|\\n)\\s*interceptors.*",
".*auto\\s*([/\\\\]|\\\\n|/n|\\n)\\s*deserialize.*"
};
PLATFORM_BYPASS_PATTERNS.put("mysql", mysqlPatterns);
PLATFORM_BYPASS_PATTERNS.put("mariadb", mysqlPatterns);
// PostgreSQL bypass mode
PLATFORM_BYPASS_PATTERNS.put("postgresql", new String[]{
".*socket\\s*([/\\\\]|\\\\n|/n|\\n)\\s*factory.*",
".*logger\\s*([/\\\\]|\\\\n|/n|\\n)\\s*file.*",
".*ssl\\s*([/\\\\]|\\\\n|/n|\\n)\\s*mode.*",
".*logger\\s*([/\\\\]|\\\\n|/n|\\n)\\s*level.*"
});
// SQL Server bypass mode
PLATFORM_BYPASS_PATTERNS.put("sqlserver", new String[]{
".*integrated\\s*([/\\\\]|\\\\n|/n|\\n)\\s*security.*",
".*authentication\\s*([/\\\\]|\\\\n|/n|\\n)\\s*scheme.*",
".*select\\s*([/\\\\]|\\\\n|/n|\\n)\\s*method.*",
".*send\\s*([/\\\\]|\\\\n|/n|\\n)\\s*string\\s*([/\\\\]|\\\\n|/n|\\n)\\s*parameters\\s*([/\\\\]|\\\\n|/n|\\n)\\s*as\\s*([/\\\\]|\\\\n|/n|\\n)\\s*unicode.*",
".*x\\s*([/\\\\]|\\\\n|/n|\\n)\\s*open\\s*([/\\\\]|\\\\n|/n|\\n)\\s*state.*",
".*application\\s*([/\\\\]|\\\\n|/n|\\n)\\s*intent.*"
});
// ClickHouse bypass mode
PLATFORM_BYPASS_PATTERNS.put("clickhouse", new String[]{
".*custom\\s*([/\\\\]|\\\\n|/n|\\n)\\s*http\\s*([/\\\\]|\\\\n|/n|\\n)\\s*params.*",
".*http\\s*([/\\\\]|\\\\n|/n|\\n)\\s*connection\\s*([/\\\\]|\\\\n|/n|\\n)\\s*provider.*",
".*check\\s*([/\\\\]|\\\\n|/n|\\n)\\s*all\\s*([/\\\\]|\\\\n|/n|\\n)\\s*nodes.*",
".*fail\\s*([/\\\\]|\\\\n|/n|\\n)\\s*over.*",
".*use\\s*([/\\\\]|\\\\n|/n|\\n)\\s*objects\\s*([/\\\\]|\\\\n|/n|\\n)\\s*in\\s*([/\\\\]|\\\\n|/n|\\n)\\s*arrays.*"
});
// Oracle bypass mode
PLATFORM_BYPASS_PATTERNS.put("oracle", new String[]{
".*oracle\\s*([/\\\\]|\\\\n|/n|\\n)\\s*jdbc.*",
".*oracle\\s*([/\\\\]|\\\\n|/n|\\n)\\s*net.*",
".*oracle\\.jdbc\\.timezoneinfotable\\s*=.*",
".*oracle\\.net\\.wallet_location\\s*=.*",
".*oracle\\.net\\.ssl_server_dn_match\\s*=\\s*false.*",
".*oracle\\.jdbc\\.enablesqlinjectionattack\\s*=\\s*true.*",
".*oracle\\.jdbc\\.implicitstatementcachesize\\s*=\\s*0.*",
".*oracle\\.jdbc\\.timezoneinfotable\\s*=.*",
".*oracle\\.net\\.wallet_location\\s*=.*",
".*oracle\\.net\\.ssl_server_dn_match\\s*=\\s*false.*",
".*oracle\\.jdbc\\.enablesqlinjectionattack\\s*=\\s*true.*",
".*oracle\\.jdbc\\.implicitstatementcachesize\\s*=\\s*0.*"
});
// DM bypass mode
PLATFORM_BYPASS_PATTERNS.put("dm", new String[]{
".*login\\s*([/\\\\]|\\\\n|/n|\\n)\\s*mode.*",
".*compatible\\s*([/\\\\]|\\\\n|/n|\\n)\\s*mode.*",
".*en\\s*([/\\\\]|\\\\n|/n|\\n)\\s*crypt.*",
".*ci\\s*([/\\\\]|\\\\n|/n|\\n)\\s*pher.*"
});
}
private final GlobalConnectionCache connectionCommonCache = GlobalConnectionCache.getInstance();
@@ -91,8 +183,9 @@ public class JdbcCommonCollect extends AbstractCollect {
throw new IllegalArgumentException("Database collect must has jdbc params");
}
if (StringUtils.hasText(metrics.getJdbc().getUrl())) {
String url = metrics.getJdbc().getUrl().toLowerCase();
for (String keyword : VULNERABLE_KEYWORDS) {
if (metrics.getJdbc().getUrl().contains(keyword)) {
if (url.contains(keyword.toLowerCase())) {
throw new IllegalArgumentException("Jdbc url prohibit contains vulnerable param " + keyword);
}
}
@@ -340,6 +433,27 @@ public class JdbcCommonCollect extends AbstractCollect {
}
}
/**
* Recursively decode the URL to prevent multiple encoding bypasses.
*
* @param url jdbc url
* @return decoded decoded jdbc url
*/
private String recursiveDecode(String url) {
String prev;
String decoded = url;
int max = 5; // Decode it at most 5 times to prevent infinite loops.
do {
prev = decoded;
try {
decoded = java.net.URLDecoder.decode(prev, StandardCharsets.UTF_8);
} catch (Exception e) {
break;
}
} while (!prev.equals(decoded) && --max > 0);
return decoded;
}
/**
* construct jdbc url due the jdbc protocol
*
@@ -355,20 +469,54 @@ public class JdbcCommonCollect extends AbstractCollect {
throw new IllegalArgumentException("JDBC URL length exceeds maximum limit of 2048 characters");
}
// remove special characters
String cleanedUrl = jdbcProtocol.getUrl().replaceAll("[\\x00-\\x1F\\x7F]", "");
String url = cleanedUrl.toLowerCase();
String cleanedUrl = jdbcProtocol.getUrl().replaceAll("[\\x00-\\x1F\\x7F\\xA0]", "");
String url = recursiveDecode(cleanedUrl);
url = url.toLowerCase();
// url format check
if (!url.matches("^jdbc:[a-zA-Z0-9]+:([^\\s;]+)(;[^\\s;]+)*$")) {
throw new IllegalArgumentException("Invalid JDBC URL format");
}
// backlist check
for (String keyword : BLACK_LIST) {
if (url.contains(keyword)) {
if (url.contains(keyword.toLowerCase())) {
throw new IllegalArgumentException("Invalid JDBC URL: contains potentially malicious parameter: " + keyword);
}
}
// url format check
if (!url.matches("^jdbc:[a-zA-Z0-9]+://[^\\s]+$")) {
throw new IllegalArgumentException("Invalid JDBC URL format");
// universal detection
String normalizedUrl = url.replaceAll("[\\x00-\\x1F\\x7F\\xA0]", " ").toLowerCase();
// universal detection of JDBC injection and deserialization attacks
if (normalizedUrl.matches(".*jndi\\s*[:=].*")
|| normalizedUrl.matches(".*ldap\\s*[:=].*")
|| normalizedUrl.matches(".*rmi\\s*[:=].*")
|| normalizedUrl.matches(".*java\\s*[:=].*")
|| normalizedUrl.matches(".*serialization\\s*[:=].*")
|| normalizedUrl.matches(".*deserializ.*\\s*[:=].*")
|| normalizedUrl.matches(".*objectinputstream\\s*[:=].*")
|| normalizedUrl.matches(".*readobject\\s*[:=].*")) {
throw new IllegalArgumentException("Invalid JDBC URL: contains potentially malicious JNDI or deserialization parameter");
}
return cleanedUrl;
// universal detection of bypass
for (String pattern : UNIVERSAL_BYPASS_PATTERNS) {
if (normalizedUrl.matches(pattern)) {
throw new IllegalArgumentException("Invalid JDBC URL: contains potentially malicious bypass pattern");
}
}
// database platform specific bypass detection
if (jdbcProtocol.getPlatform() != null) {
String platform = jdbcProtocol.getPlatform().toLowerCase();
// check for specific bypass modes on the platform
String[] platformPatterns = PLATFORM_BYPASS_PATTERNS.get(platform);
if (platformPatterns != null) {
for (String pattern : platformPatterns) {
if (normalizedUrl.matches(pattern)) {
throw new IllegalArgumentException("Invalid " + platform.toUpperCase() + " JDBC URL: contains potentially malicious bypass pattern");
}
}
}
}
return normalizedUrl;
}
assert jdbcProtocol.getPlatform() != null;
return switch (jdbcProtocol.getPlatform()) {
case "mysql", "mariadb" -> "jdbc:mysql://" + host + ":" + port
+ "/" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase())
@@ -39,7 +39,7 @@ spring:
collector:
info:
version: ${COLLECTOR_VERSION:1.7.1}
version: ${COLLECTOR_VERSION:1.7.2}
ip: ${COLLECTOR_IP:}
dispatch:
entrance:
@@ -121,6 +121,14 @@ public class MonitorsController {
monitorService.export(ids, type, res);
}
@GetMapping("/export/all")
@Operation(summary = "export all monitor config", description = "export all monitor config")
public void exportAll(
@Parameter(description = "Export Type:JSON,EXCEL,YAML") @RequestParam(defaultValue = "JSON") String type,
HttpServletResponse res) throws Exception {
monitorService.exportAll(type, res);
}
@PostMapping("/import")
@Operation(summary = "import monitor config", description = "import monitor config")
public ResponseEntity<Message<Void>> export(MultipartFile file) throws Exception {
@@ -173,6 +173,15 @@ public interface MonitorService {
*/
void export(List<Long> ids, String type, HttpServletResponse res) throws Exception;
/**
* Export All Monitoring Configuration
*
* @param type file type
* @param res response
* @throws Exception This exception will be thrown if the export fails
*/
void exportAll(String type, HttpServletResponse res) throws Exception;
/**
* Import Monitoring Configuration
*
@@ -235,6 +235,18 @@ public class MonitorServiceImpl implements MonitorService {
imExportService.exportConfig(res.getOutputStream(), ids);
}
@Override
public void exportAll(String type, HttpServletResponse res) throws Exception {
// Get all monitor IDs from the database
List<Long> allMonitorIds = monitorDao.findAll()
.stream()
.map(Monitor::getId)
.collect(Collectors.toList());
// Use the existing export method to export all monitors
export(allMonitorIds, type, res);
}
@Override
public void importConfig(MultipartFile file) throws Exception {
var fileName = FileUtil.getFileName(file);
@@ -21,11 +21,13 @@ app: hpe_switch
name:
zh-CN: HPE通用交换机
en-US: HPE Switch
ja-JP: HPEスイッチングハブ
# The description and help of this monitoring type
help:
zh-CN: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP 协议</a> 对 HPE交换机 的通用指标(可用性,系统信息,端口流量等)进行采集监控。<br>您可以点击 “<i>新建 HPE通用交换机</i>” 并进行配置SNMP相关参数添加,或者选择“<i>更多操作</i>”,导入已有配置。
en-US: HertzBeat uses <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP Protocol</a> to monitoring HPE Switch general performance metrics. <br>You can click the "<i>New HPE Switch</i>" button and config snmp params to add monitor or import an existing setup through the "<i>More Actions</i>" menu.
zh-TW: Hertzbeat 使用 <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP 協議</a> 對 HPE交換機 的通用指標(可用性,系統信息,端口流量等)進行采集監控。<br>您可以點擊 “<i>新建 HPE通用交換機</i>” 並進行配置SNMP相關參數添加,或者選擇“<i>更多操作</i>”,導入已有配置。
ja-JP: Hertzbeat は <a class='help_module_content' href='https://hertzbeat.apache.org/docs/advanced/extend-snmp'> SNMP プロトコルを介して</a> HPEスイッチングハブの一般的なメトリックを監視します。<br>「<i>新規 HPEスイッチングハブ</i>」をクリックしてSNMPなどのパラメタを設定した後、新規することができます。
helpLink:
zh-CN: https://hertzbeat.apache.org/zh-cn/docs/help/hpe_switch
en-US: https://hertzbeat.apache.org/docs/help/hpe_switch
@@ -37,6 +39,7 @@ params:
name:
zh-CN: 目标Host
en-US: Target Host
ja-JP: 目標ホスト
# type-param field type(most mapping the html input type)
type: host
# required-true or false
@@ -47,6 +50,7 @@ params:
name:
zh-CN: 端口
en-US: Port
ja-JP: ポート
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
@@ -61,6 +65,7 @@ params:
name:
zh-CN: SNMP 版本
en-US: SNMP Version
ja-JP: SNMPバージョン
# type-param field type(radio mapping the html radio tag)
type: radio
# required-true or false
@@ -79,6 +84,7 @@ params:
name:
zh-CN: SNMP 团体字
en-US: SNMP Community
ja-JP: SNMPコミュニティ
# type-param field type(most mapping the html input type)
type: text
# when type is text, use limit to limit string length
@@ -98,6 +104,7 @@ params:
name:
zh-CN: SNMP username
en-US: SNMP username
ja-JP: SNMPユーザー名
# type-param field type(most mapping the html input type)
type: text
# when type is text, use limit to limit string length
@@ -116,6 +123,7 @@ params:
name:
zh-CN: SNMP contextName
en-US: SNMP contextName
ja-JP: SNMPコンテキスト名
# type-param field type(most mapping the html input type)
type: text
# when type is text, use limit to limit string length
@@ -134,6 +142,7 @@ params:
name:
zh-CN: SNMP authPassword
en-US: SNMP authPassword
ja-JP: SNMP認証パスワード
# type-param field type(most mapping the html input type)
type: text
# when type is text, use limit to limit string length
@@ -152,6 +161,7 @@ params:
name:
zh-CN: authPassword 加密方式
en-US: authPassword Encryption
ja-JP: 認証暗号
# type-param field type(radio mapping the html radio tag)
type: radio
# required-true or false
@@ -172,6 +182,7 @@ params:
name:
zh-CN: SNMP privPassphrase
en-US: SNMP privPassphrase
ja-JP: SNMPパスワードフレーズ
# type-param field type(most mapping the html input type)
type: text
# when type is text, use limit to limit string length
@@ -190,6 +201,7 @@ params:
name:
zh-CN: privPassword 加密方式
en-US: privPassword Encryption
ja-JP: パスワードの暗号
# type-param field type(radio mapping the html radio tag)
type: radio
# required-true or false
@@ -210,6 +222,7 @@ params:
name:
zh-CN: 超时时间(ms)
en-US: Timeout(ms)
ja-JP: タイムアウト(ms)
# type-param field type(most mapping the html input type)
type: number
# when type is number, range is required
@@ -227,6 +240,7 @@ metrics:
i18n:
zh-CN: 系统信息
en-US: System Info
ja-JP: システム情報
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
priority: 0
@@ -238,32 +252,38 @@ metrics:
i18n:
zh-CN: 主机名称
en-US: Host Name
ja-JP: ホスト名
- field: descr
type: 1
i18n:
zh-CN: 描述信息
en-US: Description
ja-JP: 説明
- field: uptime
type: 1
i18n:
zh-CN: 运行时长
en-US: Uptime
ja-JP: アップタイム
- field: location
type: 1
i18n:
zh-CN: 位置
en-US: Location
ja-JP: 位置
- field: contact
type: 1
i18n:
zh-CN: 联系人
en-US: Contact
ja-JP: 連絡先
- field: responseTime
type: 0
unit: ms
i18n:
zh-CN: 响应时间
en-US: Response Time
ja-JP: 応答時間
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: snmp
# the config content when protocol is snmp
@@ -305,6 +325,7 @@ metrics:
i18n:
zh-CN: 接口详情
en-US: Interfaces Detail
ja-JP: ネットワークカード詳細
priority: 1
fields:
- field: index
@@ -312,68 +333,79 @@ metrics:
i18n:
zh-CN: 编号
en-US: Index
ja-JP: 番号
- field: descr
type: 1
label: true
i18n:
zh-CN: 接口名称
en-US: Interface Name
ja-JP: ネットワークカード名
- field: mtu
type: 0
unit: 'byte'
i18n:
zh-CN: MTU
en-US: MTU
ja-JP: MTU
- field: speed
type: 0
unit: 'MB/s'
i18n:
zh-CN: 接口速率
en-US: Interface Speed
ja-JP: ネットワークカード速度
- field: in_octets
type: 0
unit: 'MByte'
i18n:
zh-CN: 入流量
en-US: In Octets
ja-JP: 受信バイト数
- field: in_discards
type: 0
i18n:
zh-CN: 入丢包数
en-US: In Discards
ja-JP: 受信パケットロス数
- field: in_errors
type: 0
i18n:
zh-CN: 入错包数
en-US: In Errors
ja-JP: 受信異常パケット数
- field: out_octets
type: 0
unit: 'MByte'
i18n:
zh-CN: 出流量
en-US: Out Octets
ja-JP: 送信バイト数
- field: out_discards
type: 0
i18n:
zh-CN: 出丢包数
en-US: Out Discards
ja-JP: 送信パケットロス数
- field: out_errors
type: 0
i18n:
zh-CN: 出错包数
en-US: Out Errors
ja-JP: 送信異常パケット数
- field: admin_status
type: 1
i18n:
zh-CN: 配置状态
en-US: Config Status
ja-JP: 設定ステータス
- field: oper_status
type: 1
i18n:
zh-CN: 当前状态
en-US: Current Status
ja-JP: ステータス
# (optional)metrics field alias name, it is used as an alias field to map and convert the collected data and metrics field
# (可选)监控指标别名, 做为中间字段与采集数据字段和指标字段映射转换
aliasFields:
- ifIndex
- ifDescr
@@ -21,6 +21,7 @@ app: http_sd
name:
zh-CN: Http Service Discovery
en-US: Http Service Discovery
ja-JP: Httpサービスディスカバリー
# Input params define for app api(render web ui by the definition)
params:
# field-param field key
@@ -29,6 +30,7 @@ params:
name:
zh-CN: 服务发现地址
en-US: Service Discovery Url
ja-JP: サービスディスカバリー Url
# type-param field type(most mapping the html input type)
type: text
# required-true or false
@@ -39,6 +41,7 @@ params:
name:
zh-CN: 认证方式
en-US: Auth Type
ja-JP: 認証方法
# type-param field type(most mapping the html input type)
type: radio
# required-true or false
@@ -57,6 +60,7 @@ params:
name:
zh-CN: 认证Token
en-US: Access Token
ja-JP: アクセストークン
# type-param field type(most mapping the html input type)
type: text
# dependent parameter values list
@@ -73,6 +77,7 @@ params:
name:
zh-CN: 用户名
en-US: Username
ja-JP: ユーザー名
# type-param field type(most mapping the html input type)
type: text
# when type is text, use limit to limit string length
@@ -92,6 +97,7 @@ params:
name:
zh-CN: 密码
en-US: Password
ja-JP: パスワード
# type-param field type(most mapping the html input type)
type: password
# dependent parameter values list
@@ -109,6 +115,7 @@ metrics:
i18n:
zh-CN: 监控目标
en-US: Monitor Target
ja-JP: 監視対象
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
priority: 0
@@ -120,11 +127,13 @@ metrics:
i18n:
zh-CN: Host
en-US: Host
ja-JP: ホスト
- field: port
type: 1
i18n:
zh-CN: Port
en-US: Port
ja-JP: ポート
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
protocol: http_sd
# the config content when protocol is http_sd
@@ -145,4 +145,18 @@ class MonitorsControllerTest {
.andExpect(jsonPath("$.code").value("0"))
.andExpect(jsonPath("$.msg").value("Import success"));
}
@Test
void exportAll() throws Exception {
String type = "JSON";
// Mock the behavior of monitorService.exportAll
doNothing().when(monitorService).exportAll(Mockito.anyString(), Mockito.any());
// Perform the request and verify the response
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/monitors/export/all")
.param("type", type))
.andExpect(status().isOk())
.andReturn();
}
}
@@ -745,6 +745,33 @@ class MonitorServiceTest {
when(monitorDao.findById(1L)).thenReturn(Optional.of(monitor));
when(paramDao.findParamsByMonitorId(1L)).thenReturn(params);
assertDoesNotThrow(() -> monitorService.copyMonitor(1L));
}
@Test
void exportAll() throws Exception {
// Create some test monitors
Monitor monitor1 = Monitor.builder().id(1L).name("test1").app("app1").build();
Monitor monitor2 = Monitor.builder().id(2L).name("test2").app("app2").build();
List<Monitor> allMonitors = List.of(monitor1, monitor2);
// Mock the behavior of monitorDao.findAll
when(monitorDao.findAll()).thenReturn(allMonitors);
// Create a mock HttpServletResponse
jakarta.servlet.http.HttpServletResponse mockResponse = org.mockito.Mockito.mock(jakarta.servlet.http.HttpServletResponse.class);
// Mock the ImExportService
org.apache.hertzbeat.manager.service.ImExportService mockImExportService = org.mockito.Mockito.mock(org.apache.hertzbeat.manager.service.ImExportService.class);
// Mock the getFileName method
when(mockImExportService.getFileName()).thenReturn("test.json");
// Set the field using reflection
java.lang.reflect.Field field = MonitorServiceImpl.class.getDeclaredField("imExportServiceMap");
field.setAccessible(true);
java.util.Map<String, org.apache.hertzbeat.manager.service.ImExportService> imExportServiceMap = new java.util.HashMap<>();
imExportServiceMap.put("JSON", mockImExportService);
field.set(monitorService, imExportServiceMap);
// Test the exportAll method
assertDoesNotThrow(() -> monitorService.exportAll("JSON", mockResponse));
}
}
-8
View File
@@ -83,14 +83,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+1 -1
View File
@@ -95,7 +95,7 @@
</modules>
<properties>
<hzb.version>1.7.1</hzb.version>
<hzb.version>1.7.2</hzb.version>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
@@ -30,8 +30,8 @@ runs:
- name: Install mvnd
shell: bash
run: |
MVND_VERSION=1.0.2
curl -sL https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip -o mvnd.zip
MVND_VERSION=2.0.0-rc-3
curl -sL https://dlcdn.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip -o mvnd.zip
unzip -q mvnd.zip
mkdir -p $HOME/.local
mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
@@ -63,7 +63,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
@@ -62,7 +62,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
@@ -62,7 +62,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
@@ -65,7 +65,7 @@ services:
- hertzbeat
hertzbeat:
image: apache/hertzbeat:1.7.1
image: apache/hertzbeat:1.7.2
container_name: compose-hertzbeat
hostname: hertzbeat
restart: always
+1 -1
View File
@@ -17,7 +17,7 @@ $gpgKeyId = "********"
# ********************************************************
# Set version and release candidate number
$version = "1.7.1"
$version = "1.7.2"
$rcNumber = "rc1"
# ********************************************************
@@ -959,7 +959,6 @@ export class AlertSettingComponent implements OnInit {
selectedLabels: Set<string> = new Set(); // Selected labels
inputLabelValue = '';
labelInputElement!: ElementRef<HTMLInputElement>;
monitorDataByLabel: any[] = [];
$asTransferItems = (data: unknown): TransferItem[] => data as TransferItem[];
onConnectModalCancel() {
@@ -1000,7 +999,6 @@ export class AlertSettingComponent implements OnInit {
handleDeleteLabel(removedLabel: string): void {
this.selectedLabels.delete(removedLabel);
this.showMonitorByLabel();
}
showLabelInput(): void {
@@ -1014,23 +1012,10 @@ export class AlertSettingComponent implements OnInit {
if (this.inputLabelValue && !this.selectedLabels.has(this.inputLabelValue)) {
this.selectedLabels.add(this.inputLabelValue);
}
this.showMonitorByLabel();
this.inputLabelValue = '';
this.labelInputVisible = false;
}
showMonitorByLabel(): void {
const monitorsByLabel = this.transferData.filter(item => item.labels.some((label: string) => this.selectedLabels.has(label)));
this.monitorDataByLabel = monitorsByLabel.map(item => {
return {
key: item.key,
title: item.title,
description: item.description,
labels: item.labels
};
});
}
onFilterLabelsChange(newLabels: string[], direction: string): void {
if (direction == 'left') {
this.leftFilterLabels = newLabels;
@@ -1188,8 +1173,8 @@ export class AlertSettingComponent implements OnInit {
.replace(/^\s*&&\s*/, '')
.replace(/\s*&&\s*$/, '')
// Remove monitor label binding expressions
.replace(/&&\s*\(?(equals\(__labels__,\s*"[^"]+"\)(\s*or\s*equals\(__labels__,\s*"[^"]+"\))*)\)?/, '')
.replace(/\(?(equals\(__labels__,\s*"[^"]+"\)(\s*or\s*equals\(__labels__,\s*"[^"]+"\))*)\)?\s*&&\s*/, '')
.replace(/&&\s*\(?(contains\(__labels__,\s*"[^"]+"\)(\s*or\s*contains\(__labels__,\s*"[^"]+"\))*)\)?/, '')
.replace(/\(?(contains\(__labels__,\s*"[^"]+"\)(\s*or\s*contains\(__labels__,\s*"[^"]+"\))*)\)?\s*&&\s*/, '')
);
}
@@ -1301,7 +1286,7 @@ export class AlertSettingComponent implements OnInit {
// Parse label from expression
private parseLabelFromExpr(expr: string) {
const labelPattern = /equals\(__labels__,\s*"([^"]+)"\)/g;
const labelPattern = /contains\(__labels__,\s*"([^"]+)"\)/g;
let match;
this.selectedLabels.clear();
while ((match = labelPattern.exec(expr)) !== null) {
@@ -1424,4 +1409,15 @@ export class AlertSettingComponent implements OnInit {
onTextareaDragLeave(event: DragEvent): void {
(event.target as HTMLElement).classList.remove('drag-over');
}
get monitorDataByLabel(): any[] {
return this.transferData
.filter(item => item.labels.some((label: string) => this.selectedLabels.has(label)))
.map(item => ({
key: item.key,
title: item.title,
description: item.description,
labels: item.labels
}));
}
}
@@ -77,6 +77,12 @@
{{ 'monitor.export' | i18n }}
</button>
</li>
<li nz-menu-item>
<button nz-button (click)="onExportAllMonitors()">
<i nz-icon nzType="export" nzTheme="outline"></i>
{{ 'monitor.export-all' | i18n }}
</button>
</li>
<li nz-menu-item>
<nz-upload nzAction="/monitors/import" [nzLimit]="1" [nzShowUploadList]="false" (nzChange)="onImportMonitors($event)">
<button nz-button>
@@ -287,7 +293,12 @@
>
<ng-container *nzModalContent>
<div class="export-type-container">
<div class="export-type-card" (click)="exportMonitors('JSON')" [class.loading]="exportJsonButtonLoading">
<div
class="export-type-card"
(click)="exportMonitors('JSON')"
[class.loading]="exportJsonButtonLoading"
*ngIf="checkedMonitorIds.size > 0"
>
<div class="export-type-icon">
<i nz-icon nzType="code" nzTheme="outline"></i>
</div>
@@ -296,7 +307,12 @@
<p>{{ 'monitor.export.use-type' | i18n : { type: 'JSON' } }}</p>
</div>
</div>
<div class="export-type-card" (click)="exportMonitors('EXCEL')" [class.loading]="exportExcelButtonLoading">
<div
class="export-type-card"
(click)="exportMonitors('EXCEL')"
[class.loading]="exportExcelButtonLoading"
*ngIf="checkedMonitorIds.size > 0"
>
<div class="export-type-icon">
<i nz-icon nzType="file-excel" nzTheme="outline"></i>
</div>
@@ -305,6 +321,34 @@
<p>{{ 'monitor.export.use-type' | i18n : { type: 'EXCEL' } }}</p>
</div>
</div>
<div
class="export-type-card"
(click)="exportAllMonitors('JSON')"
[class.loading]="exportJsonButtonLoading"
*ngIf="checkedMonitorIds.size === 0"
>
<div class="export-type-icon">
<i nz-icon nzType="code" nzTheme="outline"></i>
</div>
<div class="export-type-info">
<h3>JSON</h3>
<p>{{ 'monitor.export-all.use-type' | i18n : { type: 'JSON' } }}</p>
</div>
</div>
<div
class="export-type-card"
(click)="exportAllMonitors('EXCEL')"
[class.loading]="exportExcelButtonLoading"
*ngIf="checkedMonitorIds.size === 0"
>
<div class="export-type-icon">
<i nz-icon nzType="file-excel" nzTheme="outline"></i>
</div>
<div class="export-type-info">
<h3>EXCEL</h3>
<p>{{ 'monitor.export-all.use-type' | i18n : { type: 'EXCEL' } }}</p>
</div>
</div>
</div>
</ng-container>
</nz-modal>
@@ -264,6 +264,10 @@ export class MonitorListComponent implements OnInit, OnDestroy {
this.isSwitchExportTypeModalVisible = true;
}
onExportAllMonitors() {
this.isSwitchExportTypeModalVisible = true;
}
onImportMonitors(info: NzUploadChangeParam): void {
console.log(info.type);
if (info.type === 'start') {
@@ -362,6 +366,46 @@ export class MonitorListComponent implements OnInit, OnDestroy {
);
}
exportAllMonitors(type: string) {
switch (type) {
case 'JSON':
this.exportJsonButtonLoading = true;
break;
case 'EXCEL':
this.exportExcelButtonLoading = true;
break;
}
const exportAllMonitors$ = this.monitorSvc
.exportAllMonitors(type)
.pipe(
finalize(() => {
this.exportExcelButtonLoading = false;
this.exportJsonButtonLoading = false;
exportAllMonitors$.unsubscribe();
})
)
.subscribe(
response => {
const message = response.body!;
if (message.type == 'application/json') {
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.export-fail'), '');
} else {
const blob = new Blob([message], { type: response.headers.get('Content-Type')! });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.download = response.headers.get('Content-Disposition')!.split(';')[1].split('filename=')[1];
a.href = url;
a.click();
window.URL.revokeObjectURL(url);
this.isSwitchExportTypeModalVisible = false;
}
},
error => {
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.export-fail'), error.msg);
}
);
}
onCancelManageMonitors() {
if (this.checkedMonitorIds == null || this.checkedMonitorIds.size === 0) {
this.notifySvc.warning(this.i18nSvc.fanyi('common.notify.no-select-cancel'), '');
@@ -30,6 +30,7 @@ const monitors_uri = '/monitors';
const detect_monitor_uri = '/monitor/detect';
const manage_monitors_uri = '/monitors/manage';
const export_monitors_uri = '/monitors/export';
const export_all_monitors_uri = '/monitors/export/all';
const summary_uri = '/summary';
const warehouse_storage_status_uri = '/warehouse/storage/status';
const grafana_dashboard_uri = '/grafana/dashboard';
@@ -74,6 +75,16 @@ export class MonitorService {
});
}
public exportAllMonitors(type: string): Observable<HttpResponse<Blob>> {
let httpParams = new HttpParams();
httpParams = httpParams.append('type', type);
return this.http.get(export_all_monitors_uri, {
params: httpParams,
observe: 'response',
responseType: 'blob'
});
}
public cancelManageMonitors(monitorIds: Set<number>): Observable<Message<any>> {
let httpParams = new HttpParams();
monitorIds.forEach(monitorId => {
+1 -1
View File
@@ -18,5 +18,5 @@
*/
export const CONSTANTS = {
VERSION: 'v1.7.1'
VERSION: 'v1.7.2'
};
+10 -7
View File
@@ -70,11 +70,11 @@
"alert.help.inhibit.link": "https://hertzbeat.apache.org/docs/help/alert_inhibit",
"alert.help.integration": "Unified management of alerts from different third-party platforms, integrating and receiving alert messages from third-party monitoring and observability systems, and performing actions such as grouping, aggregation, inhibition, silencing, and notification distribution.",
"alert.help.integration.link": "https://hertzbeat.apache.org",
"alert.help.notice": "Notification is used to config the receiver of alarm message and receiving method. The alarm message will be sent to the receiver by specified way(support email, discord, webhook etc). <a href='https://hertzbeat.apache.org/zh-cn/docs/help/alert_webhook'>Click here to see configuration steps.</a>.<br><i>Notice Template</i> is message content structure template. The built-in template is used by default or you can customize the template to customize the message notification structure.<br><span class='help_module_span'>Note⚠️: After configuring the <i>Receiver</i>, you also need to config the<i>Notice Policy</i>to specify which messages are sent to which receivers.</span><a href='https://hertzbeat.apache.org/docs/help/alert_email'> Click here to see potential issues</a>.",
"alert.help.notice": "Notification is used to config the receiver of alarm message and receiving method. The alarm message will be sent to the receiver by specified way(support email, discord, webhook etc). <a href='https://hertzbeat.apache.org/zh-cn/docs/help/alert_webhook'>Click here to see configuration steps.</a>.<br>\"<i>Notice Template</i>\" is message content structure template. The built-in template is used by default or you can customize the template to customize the message notification structure.<br><span class='help_module_span'>Note⚠️: After configuring the \"<i>Receiver</i>\", you also need to config the\"<i>Notice Policy</i>\"to specify which messages are sent to which receivers.</span><a href='https://hertzbeat.apache.org/docs/help/alert_email'> Click here to see potential issues</a>.",
"alert.help.notice.link": "https://hertzbeat.apache.org/docs/help/alert_email",
"alert.help.setting": "Threshold Rules are used for metrics alarm threshold rule management. Click the \"<i>New Threshold</i>\" to configure the alarm threshold for monitoring metrics. Hertzbeat will trigger alarms based on the threshold and metrics data.<br>Note⚠️: The alarm message that has been triggered can be checked in [Alter Center], and you can also set the notification method and personnel in [Notification].",
"alert.help.setting.link": "https://hertzbeat.apache.org/docs/help/alert_threshold",
"alert.help.silence": "Alarm Silence management is used when you dont want to be disturbed during system maintenance or on nights weekend. <br> Click \"<i>New Silence Strategy</i>\" and configure the time period to block messages so you would not get disturbed during breaks.",
"alert.help.silence": "Alarm Silence management is used when you don't want to be disturbed during system maintenance or on nights weekend. <br> Click \"<i>New Silence Strategy</i>\" and configure the time period to block messages so you would not get disturbed during breaks.",
"alert.help.silence.link": "https://hertzbeat.apache.org/docs",
"alert.inhibit.delete": "Delete Inhibit Rule",
"alert.inhibit.edit": "Edit Inhibit Rule",
@@ -701,10 +701,12 @@
"monitor.edit-monitor": "Edit Monitor",
"monitor.edit.failed": "Update Monitor Failed",
"monitor.edit.success": "Update Monitor Success",
"monitor.enable": "Resume Monitor",
"monitor.export": "Export Monitor",
"monitor.export.switch-type": "Please select the export file format!",
"monitor.export.use-type": "Export monitors in {{type}} file format",
"monitor.enable": "Enable",
"monitor.export": "Export Selected",
"monitor.export-all": "Export All",
"monitor.export.switch-type": "Please select the export file format",
"monitor.export.use-type": "Export selected monitors in {{type}} format",
"monitor.export-all.use-type": "Export all monitors in {{type}} format",
"monitor.grafana.enabled.label": "Enable Grafana",
"monitor.grafana.enabled.tip": "is enabled, the monitoring data will be displayed in Grafana",
"monitor.grafana.upload.label": "Upload Grafana Template",
@@ -912,5 +914,6 @@
"ai.bot.greeting": "Hello! I am an AI assistant. How can I help you?",
"ai.bot.input.placeholder": "Please enter a question...",
"ai.bot.send": "Send",
"ai.bot.connect-fail": "Sorry, there was an issue connecting to the AI assistant. Please try again later."
"ai.bot.connect-fail": "Sorry, there was an issue connecting to the AI assistant. Please try again later.",
"monitor.help": "Monitoring and management page, you can check the metric data and manage monitoring tasks here. The status of normal service is"
}
+4 -2
View File
@@ -702,9 +702,11 @@
"monitor.edit.failed": "修改监控失败",
"monitor.edit.success": "修改监控成功",
"monitor.enable": "恢复监控",
"monitor.export": "导出监控",
"monitor.export": "导出所选",
"monitor.export-all": "导出全部",
"monitor.export.switch-type": "请选择导出文件格式!",
"monitor.export.use-type": "以 {{type}} 文件格式导出监控",
"monitor.export.use-type": "以 {{type}} 文件格式导出所选监控",
"monitor.export-all.use-type": "以 {{type}} 文件格式导出全部监控",
"monitor.grafana.enabled.label": "启用Grafana",
"monitor.grafana.enabled.tip": "是否启用Grafana",
"monitor.grafana.upload.label": "上传Grafana模板",