Commit 3cf16a33 authored by wenmo's avatar wenmo

解决日志冲突与数据源注册异常

parent 4031a9d5
......@@ -29,6 +29,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
......@@ -78,17 +84,38 @@
<version>1.2.14</version>
<scope>provided</scope>
</dependency>-->
<dependency>
<!--<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
......@@ -123,17 +150,21 @@
<groupId>com.dlink</groupId>
<artifactId>dlink-metadata-base</artifactId>
</dependency>
<dependency>
<!--<dependency>
<groupId>com.dlink</groupId>
<artifactId>dlink-metadata-mysql</artifactId>
<scope>provided</scope>
</dependency>
</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
<mainClass>com.dlink.Dlink</mainClass>
</configuration>
<executions>
<execution>
<goals>
......@@ -142,6 +173,33 @@
</execution>
</executions>
</plugin>
<!--<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
&lt;!&ndash;<outputDirectory>${project.build.directory}/app/bin</outputDirectory>&ndash;&gt;
&lt;!&ndash;这些配置将写入到MANIFEST.MF文件中&ndash;&gt;
<archive>
&lt;!&ndash;指定程序入口&ndash;&gt;
<manifest>
&lt;!&ndash;<addClasspath>true</addClasspath>&ndash;&gt;
<classpathPrefix>../lib/</classpathPrefix>
<mainClass>com.dlink.Dlink</mainClass>
</manifest>
&lt;!&ndash; (配置文件外置目录) &ndash;&gt;
<manifestEntries>
<Class-Path>../config/</Class-Path>
</manifestEntries>
</archive>
<excludes>
<exclude>**/*.xml</exclude>
<exclude>**/*.conf</exclude>
<exclude>**/*.properties</exclude>
<exclude>**/*/*.json</exclude>
</excludes>
</configuration>
</plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
......
package com.dlink;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
......@@ -13,6 +12,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@SpringBootApplication
public class Dlink {
public static void main(String[] args) {
SpringApplication.run(Dlink.class, args);
}
......
......@@ -3,10 +3,13 @@ package com.dlink.controller;
import com.dlink.assertion.Asserts;
import com.dlink.common.result.ProTableResult;
import com.dlink.common.result.Result;
import com.dlink.constant.CommonConstant;
import com.dlink.model.DataBase;
import com.dlink.service.DataBaseService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
......@@ -25,6 +28,7 @@ import java.util.List;
public class DataBaseController {
@Autowired
private DataBaseService databaseService;
private static Logger logger = LoggerFactory.getLogger(DataBaseController.class);
/**
* 新增或者更新
......@@ -91,7 +95,13 @@ public class DataBaseController {
*/
@PostMapping("/testConnect")
public Result testConnect(@RequestBody DataBase database) {
return Result.succeed(databaseService.checkHeartBeat(database),"获取成功");
String msg = databaseService.testConnect(database);
boolean isHealthy = Asserts.isEquals(CommonConstant.HEALTHY,msg);
if(isHealthy){
return Result.succeed("数据源连接测试成功!");
}else{
return Result.failed(msg);
}
}
/**
......@@ -117,6 +127,7 @@ public class DataBaseController {
Asserts.checkNotNull(dataBase,"该数据源不存在!");
databaseService.checkHeartBeat(dataBase);
databaseService.updateById(dataBase);
logger.warn("埋点日志");
return Result.succeed(dataBase,"状态刷新完成");
}
......
......@@ -14,6 +14,8 @@ import java.util.List;
*/
public interface DataBaseService extends ISuperService<DataBase> {
String testConnect(DataBase dataBase);
boolean checkHeartBeat(DataBase dataBase);
boolean saveOrUpdateDataBase(DataBase dataBase);
......
......@@ -2,6 +2,7 @@ package com.dlink.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dlink.assertion.Asserts;
import com.dlink.constant.CommonConstant;
import com.dlink.db.service.impl.SuperServiceImpl;
import com.dlink.mapper.DataBaseMapper;
import com.dlink.metadata.driver.Driver;
......@@ -23,9 +24,14 @@ import java.util.List;
*/
@Service
public class DataBaseServiceImpl extends SuperServiceImpl<DataBaseMapper, DataBase> implements DataBaseService {
@Override
public String testConnect(DataBase dataBase) {
return Driver.build(dataBase.getDriverConfig()).test();
}
@Override
public boolean checkHeartBeat(DataBase dataBase) {
boolean isHealthy = Driver.build(dataBase.getDriverConfig()).test();
boolean isHealthy = Asserts.isEquals(CommonConstant.HEALTHY,Driver.build(dataBase.getDriverConfig()).test());
dataBase.setStatus(isHealthy);
dataBase.setHeartbeatTime(LocalDateTime.now());
if(isHealthy){
......
......@@ -49,3 +49,4 @@ mybatis-plus.global-config.db-config.logic-not-delete-value=0
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.cache-enabled=false
logging.config= classpath:log4j2.xml
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--status用于设置log4j2框架内部的日志信息输出,设置成OFF将禁止log4j2内部日志输出,毕竟这个日志对我们没有什么作用,如果设置成trace,你会看到log4j2内部各种详细输出;monitorInterval是监控间隔,例如下面的设置是指:log4j2每隔600秒自动监控该配置文件是否有变化,如果有变化,则根据文件内容新的配置生成日志-->
<configuration status="OFF" monitorInterval="600">
<Properties>
<property name="LOG_PATH">./logs/</property>
<property name="LOG_FILE">dlink</property>
</Properties>
<!--定义添加器-->
<appenders>
<!--Console是输出控制台的标签,target可以控制往控制台输出日志的颜色,例如SYSTEM_OUT就是蓝色的,SYSTEM_ERR就是红色的-->
<Console name="Console" target="SYSTEM_OUT">
<!--控制台只输出level及以上级别的信息,onMatch为true代表符合level标准的才输出,onMismatch为true代表不符合level标准的就不输出-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="ACCEPT"/>
<!--这个是输出日志的格式,如果对里面的参数不理解,可以去看我的这篇文章,网址是:“https://blog.csdn.net/qq_42449963/article/details/104617356”-->
<PatternLayout pattern="[dlink] %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</Console>
<!--这种存储文件的方式更加合理,可以设置多长时间把文件归档一次,也可以设置多大文件归档一次,如果都把所有的日志存在一个文件里面,文件会受不了的,解释一下参数信息:fileName后面如果后面不跟/,例如dev/logs/app.log,那就会把日志文件放在project工程下面,不是所属的项目下面如果后面跟/,例如/dev/logs/app.log,那就会把日志文件直接放在项目所在盘符的根目录下,例如项目在E盘存放,那就会把日志文件直接放在E盘的根目录下,如果后面直接加盘符,那就会存在特定的位置,例如F:/dev/logs/app.log,那就会直接放在F盘中特定的位置,上面都是经过测验的,fileName后面的app.log文件相当于是一个缓存文件,我们会把日志信息先放在app.log中,当达到我们设置的要求之后会把app.log中的日志信息转移到filePattern指定的日志文件中,转移的内容就会从app.log日志文件中清除,没有转移的内容还存放在app.log中,等到下一次符合要求的时候在进行一次转移-->
<!--$${date:yyyy-MM}用在文件上面,输出的是目录的名字,例如2020-03,%d{MM-dd-yyyy}输入的就是月日年,例如03-02-2020,%i按照轮询输出,毕竟一天可能有符合要求的多个日志文件生成,所以需要在后面加一个类似于后缀的东西,当天的第一个日志文件可能是-1.log.gz,第二个文件就是-2.log.gz-->
<RollingFile name="RollingFile" fileName="${LOG_PATH}/${LOG_FILE}.log" filePattern="${LOG_PATH}/$${date:yyyy-MM}/${LOG_FILE}-%d{yyyy-MM-dd}-%i.log">
<!--%thread:线程名;%-5level:级别从左显示5个字符宽度;%msg:在代码中需要输出的日志消息;%class{36}:估计显示的是完整类名-->
<PatternLayout pattern="[dlink] %d{yyyy-MM-dd HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<!--<SizeBasedTriggeringPolicy size="300MB"/>-->
<Policies>
<!--TimeBasedTriggeringPolicy基于时间的触发策略,integer属性和上面<RollingFile>标签中的filePattern的值有关,例如:filePattern=”xxx%d{yyyy-MM-dd}xx” interval=”1” 表示将1天一个日志文件;filePattern=”xxx%d{yyyy-MM-dd-HH}xxx” interval=”1”表示一个小时一个日志文件,也就是说interval的单位取决于filePattern中的最小时间单位;modulate是(boolean)以0点钟为边界进行偏移计算,应该就是假设你中午启动项目,晚上0点也是一天了,而不是经过24小时才算一天-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!--当app.log文件大小到达100MB的时候,就归档一次日志文件,也就是把app.log中的那前面100MB文件取出来,放到上面<RollingFile >中的filePattern后面的路径中-->
<SizeBasedTriggeringPolicy size="100MB"/>
</Policies>
</RollingFile>
</appenders>
<loggers>
<!--level="info"代表只能打印出info及其以上的信息;Console是上面Console标签的名字,往这一写,就可以往控制台上输出内容了,RollingFile是上面RollingFile标签的名字,往这一写,就会往设定的文件中输出内容了;当程序运行的时候就会被创建日志输出文件,不过里面没有任何日志内容,是否往里面输入日志,是通过下面的appender-ref标签控制的-->
<root level="info">
<appender-ref ref="Console"/>
<!--一般不使用这个,只是让你知道有这个输出日志文件的方式而已-->
<!--<appender-ref ref="File"/>-->
<appender-ref ref="RollingFile"/>
</root>
</loggers>
<!--打印sql日志使用的,additivity是Logger是否继承root中的appender-ref定义的输出位置的标志,默认情况下Logger会继承,若是additivity设为false,则Logger只会在自己标签中的appender-ref里输出,不会在root中的指定的appender-ref里输出,若是additivity设为true,则还会把sql日志输出在root中指定的appender-ref中;level="debug"的原因是Mybatis中SQL语句的日志级别被设为DEBUG,SQL执行结果的日志级别为TRACE,只要将日志级别调整为DEBUG就可以输出SQL语句,而不会输出SQL执行结果-->
<logger name="com.dlink.mapper" level="debug" additivity="false">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFile"/>
</logger>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
<property name="LOG_HOME" value="./logs" />
<!--控制台日志, 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/dlink.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!--<appender name="dailyRollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>/usr/local/log/app.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
&lt;!&ndash; daily rollover &ndash;&gt;
<FileNamePattern>logback.%d{yyyy-MM-dd}.log</FileNamePattern>
&lt;!&ndash; keep 30 days' worth of history &ndash;&gt;
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg %n</Pattern>
</encoder>
</appender>-->
<!--myibatis log configure-->
<!--<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="ERROR"/>
<logger name="java.sql.Statement" level="ERROR"/>
<logger name="java.sql.PreparedStatement" level="ERROR"/>-->
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE"/>
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--logImpl指定MyBatis所用的日志工具,未指定时将自动查找,查找顺序是SLF4J、Apache Commons Logging、Log4j2、Log4j、JDK logging,如果一个都未找到,日志功能就会被禁用,我们可以设置mybatis使用的日志工具,value可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING-->
<!--如果是springboot+log4j2,那就是下面的-->
<!--<setting name="logImpl" value="LOG4J2" />-->
<!--如果加上了slf4j,那就需要改成SLF4J-->
<setting name="logImpl" value="SLF4J" />
</settings>
</configuration>
......@@ -48,6 +48,11 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!--<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>-->
<dependency>
<groupId>com.dlink</groupId>
<artifactId>dlink-common</artifactId>
......
......@@ -10,5 +10,9 @@ public interface CommonConstant {
/**
* 项目版本号(banner使用)
*/
String PROJECT_VERSION = "0.1.0";
String PROJECT_VERSION = "0.3.0";
/**
* 实例健康
*/
String HEALTHY = "1";
}
......@@ -3,8 +3,7 @@
# 要运行的jar包路径,加不加引号都行。 注意:等号两边 不能 有空格,否则会提示command找不到
JAR_NAME="./dlink-admin-*.jar"
#java -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:$JAVA_HOME/jre/lib:./lib -classpath ."/lib/*.jar" -jar dlink-admin-*.jar
SETTING="-Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:$JAVA_HOME/jre/lib:./lib"
SETTING="-Dloader.path=./lib"
# 如果输入格式不对,给出提示!
tips() {
echo ""
......
package com.dlink.metadata.driver;
import com.dlink.assertion.Asserts;
import com.dlink.constant.CommonConstant;
import com.dlink.model.Column;
import com.dlink.model.Schema;
import com.dlink.model.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.DriverManager;
......@@ -24,23 +27,23 @@ import java.util.List;
**/
public abstract class AbstractJdbcDriver extends AbstractDriver {
private static Logger logger = LoggerFactory.getLogger(AbstractJdbcDriver.class);
protected Connection conn;
abstract String getDriverClass();
@Override
public boolean test() {
public String test() {
Asserts.checkNotNull(config, "无效的数据源配置");
try {
Class.forName(getDriverClass());
DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword()).close();
} catch (SQLException e) {
// logger.error("Jdbc链接测试失败!错误信息为:" + e.getMessage(), e);
return false;
} catch (Exception e) {
return false;
logger.error("Jdbc链接测试失败!错误信息为:" + e.getMessage(), e);
return e.getMessage();
}
return true;
return CommonConstant.HEALTHY;
}
@Override
......@@ -65,7 +68,7 @@ public abstract class AbstractJdbcDriver extends AbstractDriver {
}
}
public void close(PreparedStatement preparedStatement,ResultSet results){
public void close(PreparedStatement preparedStatement, ResultSet results) {
try {
if (Asserts.isNotNull(results)) {
results.close();
......@@ -97,7 +100,7 @@ public abstract class AbstractJdbcDriver extends AbstractDriver {
} catch (Exception e) {
e.printStackTrace();
} finally {
close(preparedStatement,results);
close(preparedStatement, results);
}
return schemas;
}
......@@ -125,7 +128,7 @@ public abstract class AbstractJdbcDriver extends AbstractDriver {
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(preparedStatement,results);
close(preparedStatement, results);
}
return tableList;
}
......@@ -135,7 +138,7 @@ public abstract class AbstractJdbcDriver extends AbstractDriver {
List<Column> columns = new ArrayList<>();
PreparedStatement preparedStatement = null;
ResultSet results = null;
String tableFieldsSql = getDBQuery().columnsSql(schemaName,tableName);
String tableFieldsSql = getDBQuery().columnsSql(schemaName, tableName);
tableFieldsSql = String.format(tableFieldsSql, tableName);
try {
preparedStatement = conn.prepareStatement(tableFieldsSql);
......@@ -160,8 +163,8 @@ public abstract class AbstractJdbcDriver extends AbstractDriver {
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
close(preparedStatement,results);
} finally {
close(preparedStatement, results);
}
return columns;
}
......@@ -263,7 +266,7 @@ public abstract class AbstractJdbcDriver extends AbstractDriver {
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(preparedStatement,results);
close(preparedStatement, results);
}
return datas;
}
......
......@@ -49,7 +49,7 @@ public interface Driver {
String getName();
boolean test();
String test();
Driver connect();
......
......@@ -36,7 +36,7 @@ public class MysqlTest {
config.setUsername("dca");
config.setPassword("dca");
config.setUrl("jdbc:mysql://10.1.51.25:3306/dca?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true");
boolean test = Driver.build(config).test();
String test = Driver.build(config).test();
System.out.println(test);
System.out.println("end...");
}
......
......@@ -36,7 +36,7 @@ public class OracleTest {
config.setUsername("cdr");
config.setPassword("cdr");
config.setUrl("jdbc:oracle:thin:@10.1.51.25:1521:orcl");
boolean test = Driver.build(config).test();
String test = Driver.build(config).test();
System.out.println(test);
System.out.println("end...");
}
......
......@@ -29,7 +29,7 @@ export default {
'pages.welcome.link': '欢迎加入',
'pages.welcome.star': '欢迎 Star ',
'pages.welcome.advancedLayout': 'Github',
'pages.welcome.alertMessage': '实时计算平台 Dlink & Apache Flink 即将发布,目前为体验版,版本号为 0.3.0。',
'pages.welcome.alertMessage': '实时计算平台 Dlink & Apache Flink 即将发布,目前为体验版,版本号为 0.3.1。',
'pages.admin.subPage.title': ' 这个页面只有 admin 权限才能查看',
'pages.admin.subPage.alertMessage': 'umi ui 现已发布,欢迎使用 npm run ui 启动体验。',
'pages.searchTable.createForm.newRule': '新建规则',
......
......@@ -11,9 +11,9 @@ export async function createOrModifyDatabase(databse: DataBaseItem) {
export async function testDatabaseConnect(databse: DataBaseItem) {
const hide = message.loading('正在测试连接');
try {
const {datas} = await postAll('/api/database/testConnect',databse);
const {code,msg} = await postAll('/api/database/testConnect',databse);
hide();
datas?message.success("数据源连接测试成功!"):message.error("数据源连接测试失败,请检查连接配置。");
code==0?message.success(msg):message.error(msg);
} catch (error) {
hide();
message.error('请求失败,请重试');
......
......@@ -20,7 +20,7 @@ export default (): React.ReactNode => {
<Alert
message={intl.formatMessage({
id: 'pages.welcome.alertMessage',
defaultMessage: '实时计算平台 Dlink & Apache Flink 即将发布,目前为体验版,版本号为 0.3.0。',
defaultMessage: '实时计算平台 Dlink & Apache Flink 即将发布,目前为体验版,版本号为 0.3.1。',
})}
type="success"
showIcon
......@@ -276,6 +276,19 @@ export default (): React.ReactNode => {
</ul>
</Paragraph>
</Timeline.Item>
<Timeline.Item><Text code>0.3.1</Text> <Text type="secondary">2021-08-2?</Text>
<p> </p>
<Paragraph>
<ul>
<li>
<Link>解决日志无法正确输出的bug</Link>
</li>
<li>
<Link>解决数据源注册可能失败的bug</Link>
</li>
</ul>
</Paragraph>
</Timeline.Item>
</Timeline>
</Card>
</PageContainer>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment