Jelajahi Sumber

[home]开源官网初始化

tomsun28 4 tahun lalu
induk
melakukan
c47706be17
100 mengubah file dengan 6078 tambahan dan 0 penghapusan
  1. 34 0
      home/README.md
  2. 3 0
      home/babel.config.js
  3. 298 0
      home/blog/2022-01-30-hertz-beat.md
  4. 81 0
      home/docs/advanced/custom-datasource.md
  5. 35 0
      home/docs/advanced/custom-processor.md
  6. 20 0
      home/docs/advanced/custom-subject-creator.md
  7. 20 0
      home/docs/advanced/custom-subject.md
  8. 22 0
      home/docs/advanced/extend-point.md
  9. 65 0
      home/docs/contributing.md
  10. 13 0
      home/docs/design.md
  11. 14 0
      home/docs/integrate/sample-bootstrap.md
  12. 296 0
      home/docs/integrate/sample-javalin.md
  13. 17 0
      home/docs/integrate/sample-ktor.md
  14. 423 0
      home/docs/integrate/sample-micronaut.md
  15. 16 0
      home/docs/integrate/sample-quarkus.md
  16. 15 0
      home/docs/integrate/sample-spring-webflux.md
  17. 19 0
      home/docs/integrate/sample-tom.md
  18. 57 0
      home/docs/introduce.md
  19. 21 0
      home/docs/sponsor.md
  20. 35 0
      home/docs/start/docker-deploy.md
  21. 30 0
      home/docs/start/mysql-init.md
  22. 35 0
      home/docs/start/package-deploy.md
  23. 44 0
      home/docs/start/quickstart.md
  24. 50 0
      home/docs/start/tdengine-init.md
  25. 315 0
      home/docusaurus.config.js
  26. 274 0
      home/i18n/en/code.json
  27. 298 0
      home/i18n/en/docusaurus-plugin-content-blog/2022-01-30-hertz-beat.md
  28. 22 0
      home/i18n/en/docusaurus-plugin-content-docs/current.json
  29. 81 0
      home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-datasource.md
  30. 35 0
      home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-processor.md
  31. 20 0
      home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-subject-creator.md
  32. 20 0
      home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-subject.md
  33. 22 0
      home/i18n/en/docusaurus-plugin-content-docs/current/advanced/extend-point.md
  34. 65 0
      home/i18n/en/docusaurus-plugin-content-docs/current/contributing.md
  35. 13 0
      home/i18n/en/docusaurus-plugin-content-docs/current/design.md
  36. 14 0
      home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-bootstrap.md
  37. 296 0
      home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-javalin.md
  38. 17 0
      home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-ktor.md
  39. 423 0
      home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-micronaut.md
  40. 16 0
      home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-quarkus.md
  41. 15 0
      home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-spring-webflux.md
  42. 21 0
      home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-tom.md
  43. 74 0
      home/i18n/en/docusaurus-plugin-content-docs/current/introduce.md
  44. 21 0
      home/i18n/en/docusaurus-plugin-content-docs/current/sponsor.md
  45. 48 0
      home/i18n/en/docusaurus-plugin-content-docs/current/start/annotation-datasource.md
  46. 51 0
      home/i18n/en/docusaurus-plugin-content-docs/current/start/default-auth.md
  47. 64 0
      home/i18n/en/docusaurus-plugin-content-docs/current/start/default-datasource.md
  48. 45 0
      home/i18n/en/docusaurus-plugin-content-docs/current/start/default-exception.md
  49. 27 0
      home/i18n/en/docusaurus-plugin-content-docs/current/start/path-match.md
  50. 93 0
      home/i18n/en/docusaurus-plugin-content-docs/current/start/quickstart.md
  51. 432 0
      home/i18n/en/docusaurus-plugin-content-docs/current/start/step-by-step.md
  52. 62 0
      home/i18n/en/docusaurus-theme-classic/footer.json
  53. 54 0
      home/i18n/en/docusaurus-theme-classic/navbar.json
  54. 49 0
      home/package.json
  55. 63 0
      home/sidebars.json
  56. 11 0
      home/src/CdnTransfer.js
  57. 171 0
      home/src/constants.js
  58. 14 0
      home/src/css/buttons.css
  59. 280 0
      home/src/css/custom.css
  60. 21 0
      home/src/css/docsearch.css
  61. 38 0
      home/src/css/hero.css
  62. 92 0
      home/src/css/navbar.css
  63. 19 0
      home/src/pages/components/Feature.js
  64. 15 0
      home/src/pages/components/Feature.module.css
  65. 34 0
      home/src/pages/components/Highlight.js
  66. 96 0
      home/src/pages/components/LogoCarousel.js
  67. 82 0
      home/src/pages/components/LogoCarousel.module.css
  68. 23 0
      home/src/pages/components/Robot.js
  69. 122 0
      home/src/pages/components/Robot.module.css
  70. 16 0
      home/src/pages/components/Section.js
  71. 19 0
      home/src/pages/components/Section.module.css
  72. 61 0
      home/src/pages/components/highlight.module.css
  73. 79 0
      home/src/pages/index.js
  74. 101 0
      home/src/pages/styles.module.css
  75. 17 0
      home/src/sw.js
  76. 54 0
      home/src/theme/card.jsx
  77. 0 0
      home/static/.nojekyll
  78. 0 0
      home/static/img/docs/PathRoleMatcher.svg
  79. TEMPAT SAMPAH
      home/static/img/docs/basicAuthPostmanUse.png
  80. TEMPAT SAMPAH
      home/static/img/docs/benchmark_cn.png
  81. TEMPAT SAMPAH
      home/static/img/docs/benchmark_en.png
  82. TEMPAT SAMPAH
      home/static/img/docs/bot.jpg
  83. TEMPAT SAMPAH
      home/static/img/docs/digestAuthUse.png
  84. TEMPAT SAMPAH
      home/static/img/docs/digestFlow.png
  85. TEMPAT SAMPAH
      home/static/img/docs/flow-cn.png
  86. TEMPAT SAMPAH
      home/static/img/docs/flow-en.png
  87. 0 0
      home/static/img/docs/hertzbeat-stru.svg
  88. TEMPAT SAMPAH
      home/static/img/docs/javalin/test1.PNG
  89. TEMPAT SAMPAH
      home/static/img/docs/javalin/test2.PNG
  90. TEMPAT SAMPAH
      home/static/img/docs/javalin/test3.PNG
  91. TEMPAT SAMPAH
      home/static/img/docs/javalin/test4.PNG
  92. TEMPAT SAMPAH
      home/static/img/docs/javalin/test5.PNG
  93. TEMPAT SAMPAH
      home/static/img/docs/javalin/test6.PNG
  94. TEMPAT SAMPAH
      home/static/img/docs/javalin/test7.PNG
  95. TEMPAT SAMPAH
      home/static/img/docs/javalin/test8.PNG
  96. TEMPAT SAMPAH
      home/static/img/docs/jwtPostmanUse.png
  97. TEMPAT SAMPAH
      home/static/img/docs/micronaut/error.png
  98. TEMPAT SAMPAH
      home/static/img/docs/micronaut/success.png
  99. TEMPAT SAMPAH
      home/static/img/docs/pay.png
  100. 0 0
      home/static/img/docs/sureness-core.svg

+ 34 - 0
home/README.md

@@ -0,0 +1,34 @@
+# HertzBeat Website
+
+This website is built using [Docusaurus 2](https://docusaurus.io/).    
+Learn From [webdriverio](https://webdriver.io/)                
+
+## Installation
+
+```console
+yarn install
+```
+
+## Local Development
+
+```console
+yarn start
+```
+
+This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
+
+## Build
+
+```console
+yarn build
+```
+
+This command generates static content into the `build` directory and can be served using any static contents hosting service.
+
+## Deployment
+
+```console
+GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
+```
+
+If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.

+ 3 - 0
home/babel.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+    presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
+}

+ 298 - 0
home/blog/2022-01-30-hertz-beat.md

@@ -0,0 +1,298 @@
+---
+title: TANCLOUD开源监控系统HertzBeat-赫兹节拍发布
+author: tom  
+author_title: Tancloud   
+author_url: https://github.com/tomsun28  
+author_image_url: https://avatars.githubusercontent.com/u/24788200?s=400&v=4  
+tags: [integrate]  
+---
+
+
+[javalin-sureness sample repository](https://github.com/tomsun28/sureness/tree/master/samples/javalin-sureness)
+
+Using Sureness to secure Javalin REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC)
+
+<!--truncate-->
+
+## What You Will Learn
+
+* Creating a simple REST API using Javalin
+* Learn how to integrate Sureness into a Javalin application
+* Learn how to issue a JWT
+* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API
+* Test API authorization - use different users to verify that they can access the REST API
+
+
+The tutorial assumes that you know what  JWT, Basic Auth, Digest Auth, RBAC are. If you
+do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction.
+
+## Setting Up Dependencies
+
+First, you will need to create a maven project and add Javalin, Sureness dependencies coordinate
+
+````
+        <dependency>
+            <groupId>io.javalin</groupId>
+            <artifactId>javalin</artifactId>
+            <version>{{site.javalinversion}}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usthe.sureness</groupId>
+            <artifactId>sureness-core</artifactId>
+            <version>1.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.30</version>
+        </dependency>
+````
+
+
+## Setting Up Javalin and Create REST API
+
+We need to create a simple Javalin app and provide some  REST API for test.
+
+```
+        // init javalin
+        Javalin app = Javalin.create().start(8088);
+```
+
+```
+        // create simple rest api
+        // simple rest api
+        app.routes(() ->
+            path("api", () -> {
+                path("v3", () -> {
+                    get("host", ctx -> ctx.result("get /api/v3/host success"));
+                    put("book", ctx -> ctx.result("put /api/v3/book success"));
+                });
+                path("v2", () -> {
+                    path("host", () -> {
+                        get(ctx -> ctx.result("get /api/v2/host success"));
+                        post(ctx -> ctx.result("post /api/v2/host success"));
+                        put(ctx -> ctx.result("put /api/v2/host success"));
+                        delete(ctx -> ctx.result("delete /api/v2/host success"));
+                    });
+                });
+                path("v1", () -> {
+                    path("source1", () -> {
+                        get(ctx -> ctx.result("get /api/v1/source1 success"));
+                        post(ctx -> ctx.result("post /api/v1/source1 success"));
+                        put(ctx -> ctx.result("put /api/v1/source1 success"));
+                        delete(ctx -> ctx.result("delete /api/v1/source1 success"));
+                    });
+                });
+            }));
+```
+
+
+## Setting Up Sureness
+
+#### 1. Use the Default Configuration to Configure Sureness
+
+The default configuration -`DefaultSurenessConfig` uses the document datasource `sureness.yml` as the auth datasource.  
+It supports JWT, Basic Auth, Digest Auth authentication.
+```
+    public static void main(String[] args) {
+        // init sureness default config
+        new DefaultSurenessConfig();
+    }
+```
+
+####  2. Config Document Datasource - `sureness.yml`
+
+Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`.
+
+Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`.  eg:
+
+````yaml
+## -- sureness.yml document dataSource-- ##
+
+# load api resource which need be protected, config role who can access these resource.
+# resources that are not configured are also authenticated and protected by default, but not authorized
+# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3
+# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===post===[role1]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v1/source2===get===[]
+  - /api/v2/host===post===[role2,role3]
+  - /api/v2/host===get===[role2,role3]
+  - /api/v2/host===delete===[role2,role3]
+  - /api/v2/host===put===[role2,role3]
+  - /api/v3/*===*===[role1,role2,role3]
+
+# load api resource which do not need be protected, means them need be excluded.
+# these api resource can be access by everyone
+excludedResource:
+  - /api/v3/host===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+
+# account info
+# there are three account: admin, root, tom
+# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500
+# eg: root has role1, unencrypted password is 23456
+# eg: tom has role3, unencrypted password is 32113
+account:
+  - appId: admin
+    # if add salt, the password is encrypted password - the result: MD5(password+salt)
+    # digest auth not support encrypted password
+    # if no salt, the password is unencrypted password
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1,role2]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+````
+
+
+
+####  3. Add an Interceptor Intercepting All Requests
+
+The essence of sureness is to intercept all rest requests for authenticating and authorizing.     The interceptor can be a filter or interceptor, it intercepts all request to check them. In Javalin, we use `app.before()`.
+
+```
+        // intercept all rest requests for authenticating and authorizing
+        app.before(ctx -> {
+            SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(ctx.req);
+            // when auth error , the exception throw, you should use app.exception() catch it and define return
+            if (subject != null) {
+                SurenessContextHolder.bindSubject(subject);
+            }
+        });
+
+        app.after(ctx ->  SurenessContextHolder.unbindSubject());
+
+```
+
+#### 4. Last, Implement Auth Exception Handling Process
+
+Sureness uses exception handling process:
+
+- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information.
+- If auth failure, method - `checkIn()` will throw different types of auth exceptions.
+
+We need to continue the subsequent process based on these exceptions.(eg: return the request response)
+
+Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something:
+
+````
+        // when auth error , the exception throw, you should use app.exception() catch it and define return
+        app.exception(UnknownAccountException.class, (e, ctx) -> {
+            log.debug("this request user account not exist");
+            ctx.status(401).result(e.getMessage());
+        }).exception(IncorrectCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential is incorrect");
+            ctx.status(401).result(e.getMessage());
+        }).exception(ExpiredCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential expired");
+            ctx.status(401).result(e.getMessage());
+        }).exception(NeedDigestInfoException.class, (e, ctx) -> {
+            log.debug("you should try once again with digest auth information");
+            ctx.status(401).header("WWW-Authenticate", e.getAuthenticate());
+        }).exception(UnauthorizedException.class, (e, ctx) -> {
+            log.debug("this account can not access this resource");
+            ctx.status(403).result(e.getMessage());
+        }).exception(Exception.class, (e, ctx) -> {
+            log.error("other exception happen: ", e);
+            ctx.status(500).result(e.getMessage());
+        });
+
+````
+
+
+## Provide an Issue JWT Api
+
+Now we provide a REST API to issue JWT. We can use this JWT to test JWT auth.
+
+````
+       // issue jwt rest api
+        app.get("/auth/token", ctx -> {
+            SubjectSum subjectSum = SurenessContextHolder.getBindSubject();
+            if (subjectSum == null) {
+                ctx.result("Please auth!");
+            } else {
+                String principal = (String) subjectSum.getPrincipal();
+                List<String> roles = (List<String>) subjectSum.getRoles();
+                // issue jwt
+                String jwt = JsonWebTokenUtil.issueJwt(UUID.randomUUID().toString(), principal,
+                        "token-server", 3600L, roles);
+                ctx.result(jwt);
+            }
+        });
+````
+
+**All done, we can test now!**
+
+## Test
+
+Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support?   
+This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api.
+
+Let's test it. (we use postman and chrome to test.)
+
+### Test Authentication
+
+####  1. Basic Auth Test
+
+Use postman Basic auth, as shown below:
+
+* success - input username: admin, password: admin
+
+![success](/img/docs/javalin/test1.PNG)
+
+
+* fail - input username: admin, password: 12345
+
+![fail](/img/docs/javalin/test2.PNG)
+
+####  2. Digest Auth Test
+
+Note: If password has been encrypted,  Digest auth not support.(So the account admin not support Digest auth).  
+Use chrome to Digest auth, as shown below:
+
+![success](/img/docs/javalin/test3.PNG)
+
+![success](/img/docs/javalin/test4.PNG)
+
+####  3. JWT Auth Test
+
+First, we should access **[GET /auth/token]** api to get a JWT to use, as shown below:
+
+![success](/img/docs/javalin/test5.PNG)
+
+Then, use the JWT as Bearer Token to access REST API, as shown below:
+
+![success](/img/docs/javalin/test6.PNG)
+
+
+### Test Authorization
+
+* success - user **tom** has role **role3**, the api **[DELETE - /api/v2/host]** support **role3** access, so **tom** can access api **[DELETE - /api/v2/host]** success, as shown below:
+
+![success](/img/docs/javalin/test7.PNG)
+
+
+* fail - user **tom** only has role **role3**, the api **[GET - /api/v1/source1]** only support **role2** access, not support **role3**,  so **tom** can not access api **[GET - /api/v1/source1]**, as shown below:
+
+![fail](/img/docs/javalin/test8.PNG)
+
+
+## Conclusion
+
+Javalin is a framework dedicated to simplicity and ease of use, and so is Sureness.  
+We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc.   
+Finally, thank you again for reading.
+
+[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness)

+ 81 - 0
home/docs/advanced/custom-datasource.md

@@ -0,0 +1,81 @@
+---
+id: custom-datasource  
+title: 自定义数据源   
+sidebar_label: 自定义数据源     
+---
+
+## 自定义数据源
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `PathTreeProvider`: 资源的数据源接口,实现从数据库,文本等加载数据,加载到对应的资源权限匹配器`DefaultPathRoleMatcher`中
+- `SurenessAccountProvider`: 用户的账户密钥信息接口,实现从数据库,文本等加载数据,加载到需要账户数据的`processor`中
+
+
+首先我们先来认识下sureness提供的两个用户信息和资源权限信息的接口,用户可以实现这些接口自定义从不同的数据源给sureness提供数据。
+当我们把项目从配置文件模式切换成数据库模式时,也只是简单替换了这些接口的实现类而已。
+
+一. `PathTreeProvider` 资源权限配置信息的数据源接口,我们可以实现从数据库,文本等加载接口想要的资源权限配置数据
+
+````
+public interface PathTreeProvider {
+
+    Set<String> providePathData();
+
+    Set<String> provideExcludedResource();
+}
+
+````  
+
+此接口主要是需要实现上面这两个方法,providePathData是加载资源权限配置信息,也就是我们配置文件模式下sureness.yml的resourceRole信息列,
+provideExcludedResource是加载哪些资源可以被过滤不认证鉴权,也就是sureness.yml下的excludedResource信息列,如下。
+
+````
+resourceRole:
+  - /api/v2/host===post===[role2,role3,role4]
+  - /api/v2/host===get===[role2,role3,role4]
+  - /api/v2/host===delete===[role2,role3,role4]
+  - /api/v2/host===put===[role2,role3,role4]
+  - /api/mi/**===put===[role2,role3,role4]
+  - /api/v1/getSource1===get===[role1,role2]
+  - /api/v2/getSource2/*/*===get===[role2]
+
+excludedResource:
+  - /api/v1/source3===get
+  - /api/v3/host===get
+  - /**/*.css===get
+  - /**/*.ico===get
+  - /**/*.png===get
+````
+
+而当我们使用数据库模式时,实现这些信息从数据库关联读取就ok了,规范返回 eg: /api/v2/host===post===[role2,role3,role4] 格式的数据列,
+具体的数据库实现类参考类 - [DatabasePathTreeProvider](https://github.com/tomsun28/sureness/blob/master/sample-tom/src/main/java/com/usthe/sureness/sample/tom/sureness/provider/DatabasePathTreeProvider.java)
+
+二. `SurenessAccountProvider`这第二个相关的接口就是用户的账户密钥信息提供接口,我们需要实现从数据库或者文本等其他数据源那里去加载我们想要的用户的账户信息数据,
+这些数据提供需要账户数据的processor进行用户的认证。
+
+````
+public interface SurenessAccountProvider {
+    SurenessAccount loadAccount(String appId);
+}
+````
+此接口主要需要实现上面这个loadAccount方法,通过用户的唯一标识appid来从数据库或者redis缓存中查找到用户的账户信息返回即可。
+用户账户信息类SurenessAccount如下:
+
+````
+public class DefaultAccount implements SurenessAccount {
+
+    private String appId;
+    private String password;
+    private String salt;
+    private List<String> ownRoles;
+    private boolean disabledAccount;
+    private boolean excessiveAttempts;
+}
+```` 
+
+比较简单,主要是需要提供用户的密码相关信息即可,供sureness认证时密钥判断正确与否。  
+这个具体的数据库接口实现可参考类 - [DatabaseAccountProvider](https://github.com/tomsun28/sureness/blob/master/sample-tom/src/main/java/com/usthe/sureness/sample/tom/sureness/provider/DatabaseAccountProvider.java)
+
+
+具体扩展实践请参考 [Springboot项目集成-数据库方案](/docs/integrate/sample-tom)     

+ 35 - 0
home/docs/advanced/custom-processor.md

@@ -0,0 +1,35 @@
+---
+id: custom-processor  
+title: 自定义processor     
+sidebar_label: 自定义processor     
+---
+
+processor就是对请求的用户账户信息subject真正的认证鉴权处理器,我们需要实现BaseProcessor接口,来实现我们自定义的认证鉴权方式。  
+sureness已经内置基于账户密码认证方式处理PasswordSubject的PasswordProcessor,基于jwt认证方式处理JwtSubject的JwtProcessor等。
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `Processor`: `Subject`处理接口,根据Subject信息,进行认证鉴权
+
+```
+public abstract class BaseProcessor implements Processor{
+
+    public abstract boolean canSupportSubjectClass(Class<?> var);
+
+    public abstract Subject authenticated (Subject var) throws SurenessAuthenticationException;
+
+    public abstract void authorized(Subject var) throws SurenessAuthorizationException;
+}
+
+```
+
+上面就是BaseProcessor的一些重要接口方法,自定义processor需要我们去实现这些方法。
+
+- `canSupportSubjectClass` 判断是否支持入参的此Subject类类型,比如 JwtProcessor只支持JwtSubject, PasswordProcessor只支持PasswordSubject
+- `authenticated` 对subject进行认证,根据传入的subject信息和系统内信息,进行请求用户的账户认证
+- `authorized` 对subject进行鉴权,鉴权判断此用户是否拥有其访问api的访问权限
+
+sureness使用异常流程模型,以上的认证失败或鉴权失败都会抛出不同类型的异常,用户在最外部捕获判断实现接下来的流程。
+
+sureness默认异常类型参考 [默认异常类型](/docs/start/default-exception)    
+具体扩展实践请参考 [Springboot项目集成-数据库方案](/docs/integrate/sample-tom)    

+ 20 - 0
home/docs/advanced/custom-subject-creator.md

@@ -0,0 +1,20 @@
+---
+id: custom-subject-creator  
+title: 自定义subject creator   
+sidebar_label: 自定义subject creator    
+---
+
+自定义`subject creator`是我们使用频率最高的扩展,当请求体对象并不是`servlet`或者`jax-rs`标准`api`时,
+或者我们从request请求的不同地方获取账户信息时,我们就需要自定义`subject creator`,
+使其通过请求对象获取我们需要的请求信息(请求路径,请求方法,认证信息等), 从而创建出对应的`subject`.
+
+sureness已经内置能创建出基于账户密码的PasswordSubject的BasicSubjectServletCreator,
+和创建出jwt类型JwtSubject的JwtSubjectServletCreator等,当然我们可以自定义自己需要的subjectCreator来创建subject
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `SubjectCreate`: 创建`Subject`接口,根据请求内容创建不同类型的`Subject`对象
+
+实现`SubjectCreate`接口方法,根据request请求的内容创建出对应需要的的`subject`
+
+具体扩展实践请参考 [sample-spring-webflux项目集成案例](/docs/integrate/sample-spring-webflux)      

+ 20 - 0
home/docs/advanced/custom-subject.md

@@ -0,0 +1,20 @@
+---
+id: custom-subject  
+title: 自定义subject  
+sidebar_label: 自定义subject  
+---
+
+subject包含的就是我们request请求所带的用户信息,sureness已经内置基于账户密码的PasswordSubject,
+基于jwt的JwtSubject等,当然我们可以自定义自己需要的subject来扩充自己的用户信息
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `Subject`: 认证鉴权对象接口,提供访问对象的账户密钥,请求资源,角色等信息
+
+自定义subject需要走以下流程:
+
+1. 实现`Subject`接口,添加自定义的`subject`内容
+2. 实现`SubjectCreate`接口方法,创建出自定义的`subject` 参考[自定义Subject Creator](/docs/advanced/custom-subject-creator)
+3. 实现`Processor`接口,支持处理自定义的`subject` 参考[自定义Processor](/docs/advanced/custom-processor)
+
+具体扩展实践请参考 [使用sureness30分钟项目集成案例](/docs/integrate/sample-tom)     

+ 22 - 0
home/docs/advanced/extend-point.md

@@ -0,0 +1,22 @@
+---
+id: extend-point  
+title: 进阶扩展  
+sidebar_label: 扩展点    
+---
+
+`sureness`支持自定义`subject`,自定义注册`subjectCreator`,自定义`processor`处理器,自定义数据源加载器等。
+
+进阶自定义扩展之前我们先来了解下sureness的大致流程:
+
+![flow](/img/docs/flow-cn.png)
+
+如上面的流程所讲,Subject被SubjectCreate根据request请求体所创造,不同的认证鉴权处理器Processor来处理所支持的Subject。
+
+
+sureness提供了下面这些常用接口作为扩展点:
+
+- `Subject`: 认证鉴权对象接口,提供访问对象的账户密钥,请求资源,角色等信息
+- `SubjectCreate`: 创建`Subject`接口,根据请求内容创建不同类型的`Subject`对象
+- `Processor`: `Subject`处理接口,根据Subject信息,进行认证鉴权
+- `PathTreeProvider`: 资源的数据源接口,实现从数据库,文本等加载数据
+- `SurenessAccountProvider`: 用户的账户密钥信息接口,实现从数据库,文本等加载数据    

+ 65 - 0
home/docs/contributing.md

@@ -0,0 +1,65 @@
+---
+id: contributing  
+title: 参与贡献    
+sidebar_label: 参与贡献    
+---
+
+Contributing to Sureness
+=======================================
+Very welcome to Contribute this project, go further and better with sureness.
+Firstly, thanks for your interest in contributing! I hope that this will be a pleasant first experience for you, and that you will return to continue contributing.
+
+Components of Repository:
+- [sureness's kernel code--sureness-core](https://github.com/usthe/sureness/tree/master/core)
+- [sureness integration springboot sample(configuration file scheme)--sample-bootstrap](https://github.com/usthe/sureness/tree/master/sample-bootstrap)
+- [sureness integration springboot sample(database scheme)-sample-tom](https://github.com/usthe/sureness/tree/master/sample-tom)
+- [sample projects using sureness in each framework(javalin,ktor,quarkus)--samples](https://github.com/usthe/sureness/tree/master/samples)
+
+## How to contribute?
+
+Most of the contributions that we receive are code contributions, but you can
+also contribute to the documentation or simply report solid bugs
+for us to fix.
+
+For new contributors, please take a look at issues or pull requests with a tag called below.
+
+[Good first issue](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)    
+[Help wanted](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)     
+[Good first pull request](https://github.com/usthe/sureness/issues?q=label%3A%22good+first+pull+request%22+)
+
+## Join discussion
+
+[Github Discussion](https://github.com/usthe/sureness/discussions)      
+[Gitter Channel](https://gitter.im/usthe/sureness)
+
+----   
+----   
+
+参与贡献
+=======================================
+
+非常欢迎参与项目贡献,我们致力于维护一个互相帮助的快乐社区。
+
+仓库的组成部分:
+- [sureness的核心代码--sureness-core](https://github.com/usthe/sureness/tree/master/core)
+- [使用sureness集成springboot搭建权限项目(配置文件方案)--sample-bootstrap](https://github.com/usthe/sureness/tree/master/sample-bootstrap)
+- [使用sureness集成springboot搭建权限项目(数据库方案)--sample-tom](https://github.com/usthe/sureness/tree/master/sample-tom)
+- [各个框架使用sureness的样例项目(javalin,ktor,quarkus)--samples](https://github.com/usthe/sureness/tree/master/samples)
+
+
+## 如何贡献?
+
+我们不仅仅接收代码的贡献提交,您也可以通过提交文档的更新或者BUG的报告来参与社区贡献。
+
+如果是新的贡献者,请首先了解参考如下样例的提交Issues,提交Pull Requests如果工作。
+
+[Good first issue](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)    
+[Help wanted](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)     
+[Good first pull request](https://github.com/usthe/sureness/issues?q=label%3A%22good+first+pull+request%22+)
+
+## 加入交流
+
+[Github Discussion](https://github.com/usthe/sureness/discussions)         
+[Gitter Channel](https://gitter.im/usthe/sureness)    
+QQ交流群:390083213    
+微信公众号:sureness

+ 13 - 0
home/docs/design.md

@@ -0,0 +1,13 @@
+---
+id: design  
+title: 设计文档     
+sidebar_label: 设计文档     
+---
+
+### 高性能匹配
+
+![pathRoleMatcher](/img/docs/PathRoleMatcher.svg)
+
+### 处理流程
+
+![sureness-core](/img/docs/sureness-core.svg)  

+ 14 - 0
home/docs/integrate/sample-bootstrap.md

@@ -0,0 +1,14 @@
+---
+id: sample-bootstrap  
+title: Springboot项目集成-配置文件方案    
+sidebar_label: Springboot项目集成-配置文件方案    
+---
+
+[Springboot项目集成-配置文件方案代码仓库地址](https://github.com/tomsun28/sureness/tree/master/sample-bootstrap)
+
+- 基于`springboot`
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`sureness-config`
+- 使用默认的`JWT, Basic Auth, Digest Auth`方式认证鉴权
+- 保护入口: `SurenessFilterExample`
+- 推荐使用`postman`测试,测试样例为`sample-bootstrap-postman.json`,导入`postman`即可  

+ 296 - 0
home/docs/integrate/sample-javalin.md

@@ -0,0 +1,296 @@
+---
+id: sample-javalin  
+title: Javalin项目集成    
+sidebar_label: Javalin项目集成    
+---
+
+
+# Using Sureness to protect the security of Javalin REST API
+
+[javalin-sureness sample repository](https://github.com/tomsun28/sureness/tree/master/samples/javalin-sureness)
+
+Using Sureness to secure Javalin REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC)
+
+
+## What You Will Learn
+
+* Creating a simple REST API using Javalin
+* Learn how to integrate Sureness into a Javalin application
+* Learn how to issue a JWT
+* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API
+* Test API authorization - use different users to verify that they can access the REST API
+
+
+The tutorial assumes that you know what  JWT, Basic Auth, Digest Auth, RBAC are. If you
+do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction.
+
+## Setting Up Dependencies
+
+First, you will need to create a maven project and add Javalin, Sureness dependencies coordinate
+
+````
+        <dependency>
+            <groupId>io.javalin</groupId>
+            <artifactId>javalin</artifactId>
+            <version>{{site.javalinversion}}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usthe.sureness</groupId>
+            <artifactId>sureness-core</artifactId>
+            <version>1.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.30</version>
+        </dependency>
+````
+
+
+## Setting Up Javalin and Create REST API
+
+We need to create a simple Javalin app and provide some  REST API for test.
+
+```
+        // init javalin
+        Javalin app = Javalin.create().start(8088);
+```
+
+```
+        // create simple rest api
+        // simple rest api
+        app.routes(() ->
+            path("api", () -> {
+                path("v3", () -> {
+                    get("host", ctx -> ctx.result("get /api/v3/host success"));
+                    put("book", ctx -> ctx.result("put /api/v3/book success"));
+                });
+                path("v2", () -> {
+                    path("host", () -> {
+                        get(ctx -> ctx.result("get /api/v2/host success"));
+                        post(ctx -> ctx.result("post /api/v2/host success"));
+                        put(ctx -> ctx.result("put /api/v2/host success"));
+                        delete(ctx -> ctx.result("delete /api/v2/host success"));
+                    });
+                });
+                path("v1", () -> {
+                    path("source1", () -> {
+                        get(ctx -> ctx.result("get /api/v1/source1 success"));
+                        post(ctx -> ctx.result("post /api/v1/source1 success"));
+                        put(ctx -> ctx.result("put /api/v1/source1 success"));
+                        delete(ctx -> ctx.result("delete /api/v1/source1 success"));
+                    });
+                });
+            }));
+```
+
+
+## Setting Up Sureness
+
+#### 1. Use the Default Configuration to Configure Sureness
+
+The default configuration -`DefaultSurenessConfig` uses the document datasource `sureness.yml` as the auth datasource.  
+It supports JWT, Basic Auth, Digest Auth authentication.
+```
+    public static void main(String[] args) {
+        // init sureness default config
+        new DefaultSurenessConfig();
+    }
+```
+
+####  2. Config Document Datasource - `sureness.yml`
+
+Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`.
+
+Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`.  eg:
+
+````yaml
+## -- sureness.yml document dataSource-- ##
+
+# load api resource which need be protected, config role who can access these resource.
+# resources that are not configured are also authenticated and protected by default, but not authorized
+# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3
+# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===post===[role1]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v1/source2===get===[]
+  - /api/v2/host===post===[role2,role3]
+  - /api/v2/host===get===[role2,role3]
+  - /api/v2/host===delete===[role2,role3]
+  - /api/v2/host===put===[role2,role3]
+  - /api/v3/*===*===[role1,role2,role3]
+
+# load api resource which do not need be protected, means them need be excluded.
+# these api resource can be access by everyone
+excludedResource:
+  - /api/v3/host===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+
+# account info
+# there are three account: admin, root, tom
+# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500
+# eg: root has role1, unencrypted password is 23456
+# eg: tom has role3, unencrypted password is 32113
+account:
+  - appId: admin
+    # if add salt, the password is encrypted password - the result: MD5(password+salt)
+    # digest auth not support encrypted password
+    # if no salt, the password is unencrypted password
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1,role2]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+````
+
+
+
+####  3. Add an Interceptor Intercepting All Requests
+
+The essence of sureness is to intercept all rest requests for authenticating and authorizing.     The interceptor can be a filter or interceptor, it intercepts all request to check them. In Javalin, we use `app.before()`.
+
+```
+        // intercept all rest requests for authenticating and authorizing
+        app.before(ctx -> {
+            SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(ctx.req);
+            // when auth error , the exception throw, you should use app.exception() catch it and define return
+            if (subject != null) {
+                SurenessContextHolder.bindSubject(subject);
+            }
+        });
+
+        app.after(ctx ->  SurenessContextHolder.unbindSubject());
+
+```
+
+#### 4. Last, Implement Auth Exception Handling Process
+
+Sureness uses exception handling process:
+
+- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information.
+- If auth failure, method - `checkIn()` will throw different types of auth exceptions.
+
+We need to continue the subsequent process based on these exceptions.(eg: return the request response)
+
+Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something:
+
+````
+        // when auth error , the exception throw, you should use app.exception() catch it and define return
+        app.exception(UnknownAccountException.class, (e, ctx) -> {
+            log.debug("this request user account not exist");
+            ctx.status(401).result(e.getMessage());
+        }).exception(IncorrectCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential is incorrect");
+            ctx.status(401).result(e.getMessage());
+        }).exception(ExpiredCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential expired");
+            ctx.status(401).result(e.getMessage());
+        }).exception(NeedDigestInfoException.class, (e, ctx) -> {
+            log.debug("you should try once again with digest auth information");
+            ctx.status(401).header("WWW-Authenticate", e.getAuthenticate());
+        }).exception(UnauthorizedException.class, (e, ctx) -> {
+            log.debug("this account can not access this resource");
+            ctx.status(403).result(e.getMessage());
+        }).exception(Exception.class, (e, ctx) -> {
+            log.error("other exception happen: ", e);
+            ctx.status(500).result(e.getMessage());
+        });
+
+````
+
+
+## Provide an Issue JWT Api
+
+Now we provide a REST API to issue JWT. We can use this JWT to test JWT auth.
+
+````
+       // issue jwt rest api
+        app.get("/auth/token", ctx -> {
+            SubjectSum subjectSum = SurenessContextHolder.getBindSubject();
+            if (subjectSum == null) {
+                ctx.result("Please auth!");
+            } else {
+                String principal = (String) subjectSum.getPrincipal();
+                List<String> roles = (List<String>) subjectSum.getRoles();
+                // issue jwt
+                String jwt = JsonWebTokenUtil.issueJwt(UUID.randomUUID().toString(), principal,
+                        "token-server", 3600L, roles);
+                ctx.result(jwt);
+            }
+        });
+````
+
+**All done, we can test now!**
+
+## Test
+
+Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support?   
+This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api.
+
+Let's test it. (we use postman and chrome to test.)
+
+### Test Authentication
+
+####  1. Basic Auth Test
+
+Use postman Basic auth, as shown below:
+
+* success - input username: admin, password: admin
+
+![success](/img/docs/javalin/test1.PNG)
+
+
+* fail - input username: admin, password: 12345
+
+![fail](/img/docs/javalin/test2.PNG)
+
+####  2. Digest Auth Test
+
+Note: If password has been encrypted,  Digest auth not support.(So the account admin not support Digest auth).  
+Use chrome to Digest auth, as shown below:
+
+![success](/img/docs/javalin/test3.PNG)
+
+![success](/img/docs/javalin/test4.PNG)
+
+####  3. JWT Auth Test
+
+First, we should access **[GET /auth/token]** api to get a JWT to use, as shown below:
+
+![success](/img/docs/javalin/test5.PNG)
+
+Then, use the JWT as Bearer Token to access REST API, as shown below:
+
+![success](/img/docs/javalin/test6.PNG)
+
+
+### Test Authorization
+
+* success - user **tom** has role **role3**, the api **[DELETE - /api/v2/host]** support **role3** access, so **tom** can access api **[DELETE - /api/v2/host]** success, as shown below:
+
+![success](/img/docs/javalin/test7.PNG)
+
+
+* fail - user **tom** only has role **role3**, the api **[GET - /api/v1/source1]** only support **role2** access, not support **role3**,  so **tom** can not access api **[GET - /api/v1/source1]**, as shown below:
+
+![fail](/img/docs/javalin/test8.PNG)
+
+
+## Conclusion
+
+Javalin is a framework dedicated to simplicity and ease of use, and so is Sureness.  
+We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc.   
+Finally, thank you again for reading.
+
+[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness)

+ 17 - 0
home/docs/integrate/sample-ktor.md

@@ -0,0 +1,17 @@
+---
+id: sample-ktor  
+title: Ktor项目集成    
+sidebar_label: Ktor项目集成     
+---
+
+[ktor-sureness例子项目仓库地址](https://github.com/tomsun28/sureness/tree/master/samples/ktor-sureness)
+
+- 基于`ktor, servlet`
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`sureness-config`
+- 使用默认的`JWT, Basic Auth, Digest Auth`方式认证鉴权
+- 例子中包含`REST API`
+- 保护入口: `Application.kt`
+- 推荐使用`postman`测试
+
+

+ 423 - 0
home/docs/integrate/sample-micronaut.md

@@ -0,0 +1,423 @@
+---
+id: sample-micronaut  
+title: Micronaut项目集成    
+sidebar_label: Micronaut项目集成  
+---
+
+Using Sureness to secure micronaut REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC)
+
+
+## What You Will Learn
+
+* Creating a simple REST API using micronaut
+* Learn how to integrate Sureness into a micronaut application
+* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API
+* Test API authorization - use different users to verify that they can access the REST API
+
+
+The tutorial assumes that you know what  JWT, Basic Auth, Digest Auth, RBAC are. If you
+do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction.
+
+## Setting Up Dependencies
+
+First, you will need to create a maven project and add micronautn, Sureness dependencies coordinate
+
+````
+
+    <properties>
+        <release.version>8</release.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <packaging>jar</packaging>
+        <jdk.version>1.8</jdk.version>
+        <micronaut.version>2.4.3</micronaut.version>
+        <micronaut-maven-plugin.version>1.1.8</micronaut-maven-plugin.version>
+        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
+        <exec.mainClass>com.usthe.sureness.micronaut.Application</exec.mainClass>
+        <micronaut.runtime>netty</micronaut.runtime>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>io.micronaut</groupId>
+                <artifactId>micronaut-bom</artifactId>
+                <version>${micronaut.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.30</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usthe.sureness</groupId>
+            <artifactId>sureness-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-inject</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-validation</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut.test</groupId>
+            <artifactId>micronaut-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-http-client</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-http-server-netty</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-runtime</artifactId>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+
+        <plugins>
+            <plugin>
+                <groupId>io.micronaut.build</groupId>
+                <artifactId>micronaut-maven-plugin</artifactId>
+                <version>${micronaut-maven-plugin.version}</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven-compiler-plugin.version}</version>
+                <configuration>
+                    <annotationProcessorPaths combine.children="append">
+                        <path>
+                            <groupId>io.micronaut</groupId>
+                            <artifactId>micronaut-inject-java</artifactId>
+                            <version>${micronaut.version}</version>
+                        </path>
+                        <path>
+                            <groupId>io.micronaut</groupId>
+                            <artifactId>micronaut-validation</artifactId>
+                            <version>${micronaut.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                    <compilerArgs>
+                        <arg>-Amicronaut.processing.group=com.usthe.sureness</arg>
+                        <arg>-Amicronaut.processing.module=micronaut-sureness</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+
+     
+````
+
+
+- [User Guide](https://docs.micronaut.io/2.4.3/guide/index.html)
+- [API Reference](https://docs.micronaut.io/2.4.3/api/index.html)
+- [Configuration Reference](https://docs.micronaut.io/2.4.3/guide/configurationreference.html)
+- [Micronaut Guides](https://guides.micronaut.io/index.html)
+
+We need to create a simple micronautn app and provide some  REST API for test.
+
+
+
+## Setting Up Sureness
+
+#### 1.Run Micronaut Application
+
+```
+  
+import io.micronaut.runtime.Micronaut;
+
+
+public class Application{
+
+    public static void main(String[] args) {
+        Micronaut.run(Application.class, args);
+
+    }
+}
+
+```
+
+####  2. Config Document Datasource - `sureness.yml`
+
+Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`.
+
+Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`.  eg:
+
+````yaml
+## -- sureness.yml document dataSource-- ##
+
+# load api resource which need be protected, config role who can access these resource.
+# resources that are not configured are also authenticated and protected by default, but not authorized
+# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3
+# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===post===[role1]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v1/source2===get===[]
+  - /api/v2/host===post===[role2,role3]
+  - /api/v2/host===get===[role2,role3]
+  - /api/v2/host===delete===[role2,role3]
+  - /api/v2/host===put===[role2,role3]
+  - /api/v3/*===*===[role1,role2,role3]
+
+# load api resource which do not need be protected, means them need be excluded.
+# these api resource can be access by everyone
+excludedResource:
+  - /api/v3/host===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+
+# account info
+# there are three account: admin, root, tom
+# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500
+# eg: root has role1, unencrypted password is 23456
+# eg: tom has role3, unencrypted password is 32113
+account:
+  - appId: admin
+    # if add salt, the password is encrypted password - the result: MD5(password+salt)
+    # digest auth not support encrypted password
+    # if no salt, the password is unencrypted password
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1,role2]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+````
+
+
+
+####  3. Add an Interceptor Intercepting All Requests
+
+The essence of sureness is to intercept all rest requests for authenticating and authorizing.     The interceptor can be a filter or interceptor, it intercepts all request to check them. In Micronaut, we use Filter
+```java
+@Filter("/**")
+public class MicronautSurenessFilterExample  implements HttpServerFilter {
+
+    private static final Logger logger = LoggerFactory.getLogger(MicronautSurenessFilterExample.class);
+
+    @Inject
+    private SurenessSecurityManager securityManager ;
+
+
+    @Override
+    public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request,
+                                                             ServerFilterChain chain) {
+        Integer statusCode = null;
+        String errorMsg = null;
+        try {
+            SubjectSum subject =securityManager.checkIn(request);
+            if (subject != null) {
+                SurenessContextHolder.bindSubject(subject);
+            }
+        } catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
+            logger.debug("this request is illegal");
+            statusCode = HttpStatus.BAD_REQUEST.getCode();
+            errorMsg = e4.getMessage();
+        } catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
+            logger.debug("the account is disabled");
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e2.getMessage();
+        } catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
+            logger.debug("this account credential is incorrect or expired");
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e3.getMessage();
+        } catch (UnauthorizedException e5) {
+            logger.debug("this account can not access this resource");
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e5.getMessage();
+        } catch (RuntimeException e) {
+            logger.error("other exception happen: ", e);
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e.getMessage();
+        }
+        if (statusCode != null && errorMsg != null) {
+            String finalErrorMsg = errorMsg;
+            Integer finalStatusCode = statusCode;
+            logger.info(statusCode+"--->"+errorMsg);
+            try {
+                URI location = new URI("/auth/error");
+                request = request.mutate().headers(httpHeaders -> {
+                    httpHeaders.add("statusCode", String.valueOf(finalStatusCode));
+                    httpHeaders.add("errorMsg", finalErrorMsg);
+                }).uri(location);
+            }catch (URISyntaxException e){
+                logger.error("uri error");
+            }
+        }
+        return chain.proceed(request);
+    }
+
+
+
+    @Override
+    public int getOrder() {
+        return ServerFilterPhase.SECURITY.order();
+    }
+
+
+}
+```
+SurenessSecurityManager configuration
+
+```java
+import io.micronaut.context.annotation.Factory;
+
+@Factory
+public class SurenessConfiguration {
+    private static final Logger logger = LoggerFactory.getLogger(SurenessConfiguration.class);
+
+    @Factory
+    public SurenessSecurityManager init() {
+        SurenessAccountProvider accountProvider = new DocumentAccountProvider();
+        List<Processor> processorList = new LinkedList<>();
+        NoneProcessor noneProcessor = new NoneProcessor();
+        processorList.add(noneProcessor);
+        PasswordProcessor passwordProcessor = new PasswordProcessor();
+        passwordProcessor.setAccountProvider(accountProvider);
+        processorList.add(passwordProcessor);
+        DefaultProcessorManager processorManager = new DefaultProcessorManager(processorList);
+        if (logger.isDebugEnabled()) {
+            logger.debug("DefaultProcessorManager init");
+        }
+        PathTreeProvider pathTreeProvider = new DocumentPathTreeProvider();
+        DefaultPathRoleMatcher pathRoleMatcher = new DefaultPathRoleMatcher();
+        pathRoleMatcher.setPathTreeProvider(pathTreeProvider);
+        pathRoleMatcher.buildTree();
+        if (logger.isDebugEnabled()) {
+            logger.debug("DefaultPathRoleMatcher init");
+        }
+
+        // SubjectFactory init
+        SubjectFactory subjectFactory = new SurenessSubjectFactory();
+        List<SubjectCreate> subjectCreates = Arrays.asList(
+                new NoneSubjectReactiveCreator(),
+                new BasicSubjectReactiveCreator());
+        subjectFactory.registerSubjectCreator(subjectCreates);
+        if (logger.isDebugEnabled()) {
+            logger.debug("SurenessSubjectFactory init");
+        }
+
+        // surenessSecurityManager init
+        SurenessSecurityManager securityManager = SurenessSecurityManager.getInstance();
+        securityManager.setPathRoleMatcher(pathRoleMatcher);
+        securityManager.setSubjectFactory(subjectFactory);
+        securityManager.setProcessorManager(processorManager);
+        if (logger.isDebugEnabled()) {
+            logger.debug("SurenessSecurityManager init");
+        }
+        return securityManager;
+    }
+
+}
+
+
+```
+
+#### 4. Last, Implement Auth Exception Handling Process
+
+Sureness uses exception handling process:
+
+- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information.
+- If auth failure, method - `checkIn()` will throw different types of auth exceptions.
+
+We need to continue the subsequent process based on these exceptions.(eg: return the request response)
+
+Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something:
+
+````
+// when auth error , add error msg to HttpRequest
+   if (statusCode != null && errorMsg != null) {
+            String finalErrorMsg = errorMsg;
+            Integer finalStatusCode = statusCode;
+            logger.info(statusCode+"--->"+errorMsg);
+            try {
+                URI location = new URI("/auth/error");
+                request = request.mutate().headers(httpHeaders -> {
+                    httpHeaders.add("statusCode", String.valueOf(finalStatusCode));
+                    httpHeaders.add("errorMsg", finalErrorMsg);
+                }).uri(location);
+            }catch (URISyntaxException e){
+                logger.error("uri error");
+            }
+        } 
+
+````
+
+
+**All done, we can test now!**
+
+## Test
+
+Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support?   
+This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api.
+
+Let's test it. (we use postman and chrome to test.)
+
+### Test Authentication
+
+####  1. Basic Auth Test
+
+Use postman Basic auth, as shown below:
+
+* success - input username: admin, password: admin
+
+![success](/img/docs/micronaut/success.png)
+
+
+* fail - input username: admin, password: admin1234
+
+![fail](/img/docs/micronaut/error.png)
+
+
+## Conclusion
+
+micronaut is a framework dedicated to simplicity and ease of use, and so is Sureness.  
+We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc.   
+Finally, thank you again for reading.
+
+[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness)

+ 16 - 0
home/docs/integrate/sample-quarkus.md

@@ -0,0 +1,16 @@
+---
+id: sample-quarkus  
+title: Quarkus项目集成     
+sidebar_label: Quarkus项目集成     
+---
+
+[quarkus-sureness例子项目仓库地址](https://github.com/tomsun28/sureness/tree/master/samples/quarkus-sureness)
+
+- 基于`quarkus, jax-rs`
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`sureness-config`
+- 使用默认的`JWT, Basic Auth, Digest Auth`方式认证鉴权
+- 例子中包含`REST API`
+- 保护入口: `SurenessFilterExample`
+- 推荐使用`postman`测试  
+

+ 15 - 0
home/docs/integrate/sample-spring-webflux.md

@@ -0,0 +1,15 @@
+---
+id: sample-spring-webflux  
+title: Spring-Webflux项目集成    
+sidebar_label: Spring-Webflux项目集成    
+---
+
+[spring-webflux-sureness例子项目仓库地址](https://github.com/tomsun28/sureness/tree/master/samples/spring-webflux-sureness)
+
+- 基于`spring-webflux`
+- 自定义 `subject creator (BasicSubjectReactiveCreator, JwtSubjectReactiveCreator, NoneSubjectReactiveCreator)` 适配 `ServerHttpRequest` 请求体
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`JWT, Basic Auth`方式认证鉴权
+- 例子中包含`REST API`
+- 保护入口: `SurenessFilterExample`
+- 推荐使用`postman`测试

+ 19 - 0
home/docs/integrate/sample-tom.md

@@ -0,0 +1,19 @@
+---
+id: sample-tom  
+title: Springboot项目集成-数据库方案    
+sidebar_label: Springboot项目集成-数据库方案    
+---
+
+- 基于`springboot,jpa...`
+- 自定义数据源,使用从数据库加载账户信息,资源角色,过滤资源等信息,这样便于动态调整(见`AccountProvider ResourceProvider`)
+- 除了使用了默认的`JWT, Basic Auth`方式认证鉴权,新增自定义认证鉴权(自定义`subject subjectCreator processor...`)
+- 推荐使用`postman`测试,测试样例为`sample-tom-postman.json`,导入`postman`即可
+
+样例中包含2种自定义认证鉴权方式:
+
+1. 自定义了一个单独的`subjectCreator` 见 `CustomPasswdSubjectCreator`     
+   演示功能就是自定义的从不同地方获取请求体的账户密码,来创建默认的`PasswordSubject`,走默认的账户密码认证流程
+
+2. 自定义了一整套流程(包含`subject subjectCreator processor`) 见 `CustomTokenSubject CustomTokenSubjectCreator CustomTokenProcessor`  
+   演示功能就是自定义一个简单的`token`作为`subject`对象,对其自定义创建获取方式-`creator`和自定义认证鉴权处理流程-`processor`.  
+   此自定义流程也演示了一个简单的`token`刷新流程  

+ 57 - 0
home/docs/introduce.md

@@ -0,0 +1,57 @@
+---
+id: introduce  
+title: HertzBeat赫兹节拍     
+sidebar_label: 介绍
+slug: /
+---
+
+> 易用友好的高性能监控告警系统。
+
+![tan-cloud](https://img.shields.io/badge/网站监控-4EB1BA.svg)
+![tan-cloud](https://img.shields.io/badge/PING连通性监控-blue.svg)
+![tan-cloud](https://img.shields.io/badge/端口可用性监控-green.svg)
+![tan-cloud](https://img.shields.io/badge/数据库监控-yellow.svg)
+![tan-cloud](https://img.shields.io/badge/自定义监控-orange.svg)
+![tan-cloud](https://img.shields.io/badge/阈值告警-red.svg)
+![tan-cloud](https://img.shields.io/badge/告警转发通知-blueviolet.svg)
+
+## 📫 前言   
+
+> 毕业后投入很多业余时间也做了一些开源项目,[Sureness](https://github.com/dromara/sureness) [Bootshiro](https://gitee.com/tomsun28/bootshiro) [Issues-translate-action](https://github.com/usthe/issues-translate-action) ,
+> 当时上班有空就回答网友问题,下班回家写开源代码,远程帮人看问题,还总感觉时间不够用,当时想如果不去上班能做自己热爱的该多好。  
+> 年轻就要折腾,何况还是自己很想做的。于是乎,21年底我放弃激励裸辞开始全职开源了(这里感谢老婆大人的全力支持),也是第一次全职创业。
+> 自己在APM领域做了多年,当然这次创业加开源的方向也就是老本行APM监控系统,我们开发一个支持多种监控指标(更多监控类型指标正在适配中),拥有自定义监控,支持阈值告警通知等功能,面向开发者友好的开源监控项目-HertzBeat赫兹节拍。
+> 想到很多开发者和团队拥有云上资源,可能只需要使用监控服务而并不想部署监控系统,我们也提供了可以直接登陆使用的SAAS云监控版本-[TanCloud探云](https://console.tancloud.cn)。   
+> 希望大家多多支持点赞,非常感谢。 
+
+## 🎡 <font color="green">介绍</font>
+
+> [HertzBeat赫兹节拍](https://github.com/dromara/sureness) 是由[TanCloud](https://tancloud.cn)开源的一个支持网站,API,PING,端口,数据库等监控类型,拥有易用友好的可视化操作界面的开源监控告警项目。  
+> 当然,我们也提供了对应的[SAAS云监控版本](https://console.tancloud.cn),中小团队和个人无需再为了监控自己的网站资源,而去部署一套繁琐的监控系统,[登陆即可免费开始](https://console.tancloud.cn)监控之旅。  
+> HertzBeat 支持自定义监控,只用通过配置YML文件我们就可以自定义需要的监控类型和指标,来满足常见的个性化需求。
+> HertzBeat 模块化,`manager, collector, scheduler, warehouse, alerter` 各个模块解耦合,方便理解与定制开发。    
+> HertzBeat 支持更自由化的告警配置(计算表达式),支持告警通知,告警模版    
+> 欢迎登陆 HertzBeat 的 [云环境TanCloud]((https://console.tancloud.cn)) 试用发现更多。   
+> 我们正在快速迭代中,欢迎参与加入共建项目开源生态。
+
+> `HertzBeat`的多类型支持,易扩展,低耦合,希望能帮助开发者和中小团队快速搭建自有监控系统。
+
+
+## 🥐 模块  
+
+- **[manager](manager)** 提供监控管理,系统管理基础服务
+> 提供对监控的管理,监控应用配置的管理,系统用户租户后台管理等。
+- **[collector](collector)** 提供监控数据采集服务
+> 使用通用协议远程采集获取对端指标数据。
+- **[scheduler](scheduler)** 提供监控任务调度服务
+> 采集任务管理,一次性任务和周期性任务的调度分发。
+- **[warehouse](warehouse)** 提供监控数据仓储服务
+> 采集指标结果数据管理,数据落盘,查询,计算统计。
+- **[alerter](alerter)** 提供告警服务
+> 告警计算触发,监控状态联动,告警配置,告警通知。
+- **[web-app](web-app)** 提供可视化控制台页面
+> 监控告警系统可视化控制台前端(angular+ts+zorro)  
+
+![hertzBeat](/img/docs/hertzbeat-stru.svg)    
+
+

+ 21 - 0
home/docs/sponsor.md

@@ -0,0 +1,21 @@
+---
+id: sponsor  
+title: 赞助    
+sidebar_label: 赞助     
+---
+
+
+**sureness对个人或企业完全免费,如果您喜欢这个项目并且愿意提供帮助,请作者喝杯咖啡吧**      
+
+### 微信支付宝  
+
+![wechat-alipay](/img/docs/pay.png)      
+
+
+### paypal
+
+[https://paypal.me/tomsun28](https://paypal.me/tomsun28)    
+
+
+
+

+ 35 - 0
home/docs/start/docker-deploy.md

@@ -0,0 +1,35 @@
+---
+id: docker-deploy  
+title: 通过Docker方式安装HertzBeat    
+sidebar_label: Docker方式部署    
+---
+
+### 🐕 开始使用
+
+- 如果您不想部署而是直接使用,我们提供SAAS监控云-[TanCloud探云](https://console.tancloud.cn),即刻[登陆注册](https://console.tancloud.cn)免费使用。  
+- 如果您是想将HertzBeat部署到内网环境搭建监控系统,请参考下面的部署文档进行操作。 
+
+### 🐵 依赖服务部署   
+
+> HertzBeat最少依赖于 关系型数据库[MYSQL8+](https://www.mysql.com/) 和 时序性数据库[TDengine2+](https://www.taosdata.com/getting-started)
+
+##### 安装MYSQL  
+1. docker安装MYSQl  
+`docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql`   
+2. 创建名称为hertzBeat的数据库  
+3. 执行位于项目仓库script目录下的数据库脚本 schema.sql    
+
+##### 安装TDengine   
+1. docker安装TDengine   
+`docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine`     
+2. 创建名称为hertzBeat的数据库
+
+### 🍞 HertzBeat安装   
+> HertzBeat支持通过源码安装启动,Docker容器运行和安装包方式安装部署。  
+
+#### Docker方式快速安装
+`docker run -d -p 1157:1157 --name hertzbeat tancloud/hertzbeat:latest`  
+#### 通过安装包安装   
+todo  
+
+**HAVE FUN**

+ 30 - 0
home/docs/start/mysql-init.md

@@ -0,0 +1,30 @@
+---
+id: mysql-init  
+title: 依赖服务MYSQL安装初始化        
+sidebar_label: MYSQL安装初始化    
+---
+MYSQL是一款值得信赖的关系型数据库,HertzBeat使用其存储监控信息,告警信息,配置信息等结构化关系数据。  
+
+> 如果您已有MYSQL环境,可直接跳到SQL脚本执行那一步。  
+
+### 通过Docker方式安装MYSQL   
+1. 下载安装Docker环境   
+   Docker 工具自身的下载请参考 [Docker官网文档](https://docs.docker.com/get-docker/)。
+   安装完毕后终端查看Docker版本是否正常输出。  
+   ```
+   $ docker -v
+   Docker version 20.10.12, build e91ed57
+   ```
+2. Docker安装MYSQl  
+   ```
+   $ docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:latest
+   526aa188da767ae94b244226a2b2eec2b5f17dd8eff594533d9ec0cd0f3a1ccd
+   ```
+   使用```$ docker ps```查看数据库是否启动成功
+
+### SQL脚本执行   
+1. 进入MYSQL或使用客户端连接MYSQL服务
+2. 创建名称为hertzbeat的数据库
+3. 执行位于项目仓库script目录下的数据库建表初始化脚本 schema.sql
+4. 查看hertzbeat数据库是否成功建表
+

+ 35 - 0
home/docs/start/package-deploy.md

@@ -0,0 +1,35 @@
+---
+id: package-deploy  
+title: 通过安装包安装HertzBeat    
+sidebar_label: 安装包方式部署    
+---
+
+### 🐕 开始使用
+
+- 如果您不想部署而是直接使用,我们提供SAAS监控云-[TanCloud探云](https://console.tancloud.cn),即刻[登陆注册](https://console.tancloud.cn)免费使用。  
+- 如果您是想将HertzBeat部署到内网环境搭建监控系统,请参考下面的部署文档进行操作。 
+
+### 🐵 依赖服务部署   
+
+> HertzBeat最少依赖于 关系型数据库[MYSQL8+](https://www.mysql.com/) 和 时序性数据库[TDengine2+](https://www.taosdata.com/getting-started)
+
+##### 安装MYSQL  
+1. docker安装MYSQl  
+`docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql`   
+2. 创建名称为hertzBeat的数据库  
+3. 执行位于项目仓库script目录下的数据库脚本 schema.sql    
+
+##### 安装TDengine   
+1. docker安装TDengine   
+`docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine`     
+2. 创建名称为hertzBeat的数据库
+
+### 🍞 HertzBeat安装   
+> HertzBeat支持通过源码安装启动,Docker容器运行和安装包方式安装部署。  
+
+#### Docker方式快速安装
+`docker run -d -p 1157:1157 --name hertzbeat tancloud/hertzbeat:latest`  
+#### 通过安装包安装   
+todo  
+
+**HAVE FUN**

+ 44 - 0
home/docs/start/quickstart.md

@@ -0,0 +1,44 @@
+---
+id: quickstart  
+title: 快速开始    
+sidebar_label: 快速开始    
+---
+
+### 🐕 开始使用
+
+- 如果您不想部署而是直接使用,我们提供SAAS监控云-[TanCloud探云](https://console.tancloud.cn),即刻[登陆注册](https://console.tancloud.cn)免费使用。  
+- 如果您是想将HertzBeat部署到内网环境搭建监控系统,请参考下面的部署文档进行操作。 
+
+### 🐵 依赖服务部署   
+
+> HertzBeat最少依赖于 关系型数据库[MYSQL8+](https://www.mysql.com/) 和 时序性数据库[TDengine2+](https://www.taosdata.com/getting-started)
+
+##### 安装MYSQL  
+1. docker安装MYSQl  
+`docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql`   
+2. 创建名称为hertzbeat的数据库  
+3. 执行位于项目仓库script目录下的数据库脚本 schema.sql    
+
+详细步骤参考 [依赖服务MYSQL安装初始化](mysql-init.md)    
+
+##### 安装TDengine   
+1. docker安装TDengine   
+`docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine`     
+2. 创建名称为hertzbeat的数据库
+
+详细步骤参考 [依赖服务TDengine安装初始化](tdengine-init.md)   
+
+### 🍞 HertzBeat安装   
+> HertzBeat支持通过源码安装启动,Docker容器运行和安装包方式安装部署。  
+
+#### Docker方式快速安装
+`docker run -d -p 1157:1157 --name hertzbeat tancloud/hertzbeat:latest`  
+
+详细步骤参考 [通过Docker方式安装HertzBeat](docker-deploy.md) 
+
+#### 通过安装包安装   
+todo  
+
+详细步骤参考 [通过安装包安装HertzBeat](package-deploy.md) 
+
+**HAVE FUN**

+ 50 - 0
home/docs/start/tdengine-init.md

@@ -0,0 +1,50 @@
+---
+id: tdengine-init  
+title: 依赖服务TDengine安装初始化        
+sidebar_label: TDengine安装初始化    
+---
+TDengine是一款国产的开源物联网时序型数据库,我们使用其替换了InfluxDb,来存储采集到的监控指标数据。
+
+> 如果您已有TDengine环境,可直接跳到创建数据库实例那一步。
+
+### 通过Docker方式安装TDengine 
+> 可参考官方网站[安装教程](https://www.taosdata.com/docs/cn/v2.0/getting-started/docker)  
+1. 下载安装Docker环境   
+   Docker 工具自身的下载请参考 [Docker官网文档](https://docs.docker.com/get-docker/)。
+   安装完毕后终端查看Docker版本是否正常输出。
+   ```
+   $ docker -v
+   Docker version 20.10.12, build e91ed57
+   ```
+2. Docker安装TDengine
+   ```
+   $ docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine
+   526aa188da767ae94b244226a2b2eec2b5f17dd8eff594533d9ec0cd0f3a1ccd
+   ```
+   使用```$ docker ps```查看数据库是否启动成功
+
+### 创建数据库实例    
+1. 进入数据库Docker容器  
+   ```
+   $ docker exec -it tdengine /bin/bash
+   root@tdengine-server:~/TDengine-server-2.4.0.4#
+   ```
+2. 创建名称为hertzbeat的数据库
+   进入容器后,执行 taos shell 客户端程序。
+   ```
+   root@tdengine-server:~/TDengine-server-2.4.0.4# taos
+   Welcome to the TDengine shell from Linux, Client Version:2.4.0.4
+   Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.
+   taos>
+   ```
+   执行创建数据库命令
+   ```
+   taos> show databases;
+   taos> CREATE DATABASE hertzbeat KEEP 90 DAYS 10 BLOCKS 6 UPDATE 1;
+   ```
+   上述语句将创建一个名为 hertzbeat 的库,这个库的数据将保留90天(超过90天将被自动删除),每 10 天一个数据文件,内存块数为 6,允许更新数据
+3. 查看hertzbeat数据库是否成功创建
+   ```
+   taos> show databases;
+   taos> use hertzbeat;
+   ```

+ 315 - 0
home/docusaurus.config.js

@@ -0,0 +1,315 @@
+const path = require('path')
+
+const organizationName = 'usthe' // Usually your GitHub org/user name.
+const projectName = 'sureness' // Usually your repo name.
+const branch = 'master'
+const repoUrl = `https://github.com/dromara/${projectName}`
+const cdnUrl = '/'
+
+module.exports = {
+    title: 'TANCLOUD探云',
+    tagline: '易用友好的高性能监控云',
+    url: 'https://tancloud.cn',
+    baseUrl: '/',
+    onBrokenLinks: 'throw',
+    onBrokenMarkdownLinks: 'throw',
+    favicon: cdnUrl + 'img/tancloud-logo.svg',
+    organizationName,
+    projectName,
+    customFields: {
+        repoUrl,
+        cdnUrl
+    },
+    i18n: {
+        defaultLocale: 'zh-cn',
+        locales: ['zh-cn', 'en'],
+    },
+    themeConfig: {
+        image: cdnUrl + 'img/tancloud-logo.svg',
+        liveCodeBlock: {
+            playgroundPosition: 'bottom',
+        },
+        colorMode: {
+            defaultMode: 'light',
+            disableSwitch: false,
+            respectPrefersColorScheme: false,
+            switchConfig: {
+                darkIcon: '🌜',
+                lightIcon: '☀️',
+                // React inline style object
+                // see https://reactjs.org/docs/dom-elements.html#style
+                darkIconStyle: {
+                    marginLeft: '2px',
+                },
+                lightIconStyle: {
+                    marginLeft: '1px',
+                },
+            },
+        },
+        prism: {
+            theme: require('prism-react-renderer/themes/github'),
+            darkTheme: require('prism-react-renderer/themes/dracula'),
+            additionalLanguages: ['java'],
+        },
+        algolia: {
+            apiKey: 'c7c84bfcc1495156f5730309d821ba8c',
+            indexName: 'sureness',
+            // appId: 'GNVT7Z0UI2',
+            contextualSearch: true,
+        },
+        announcementBar: {
+            id: "github-star",
+            content:
+                '<font style="font-size: medium; font-weight: bolder">如果您喜欢TANCLOUD的开源产品HertzBeat,</font> <a target="_blank" style="font-size: medium; font-weight: bolder" rel="noopener noreferrer" href="https://github.com/dromara/sureness">欢迎在 GitHub </a> <font style="font-size: medium; font-weight: bolder"> 或 </font><a target="_blank" style="font-size: medium; font-weight: bolder" rel="noopener noreferrer" href="https://gitee.com/dromara/sureness">Gitee 给我们点赞! </a>⭐️',
+            backgroundColor: '#7228B5',
+            textColor: '#fafbfc',
+            isCloseable: true,
+        },
+        navbar: {
+            title: 'HertzBeat',
+            logo: {
+                alt: '易用友好的高性能监控云',
+                src: cdnUrl + 'img/tancloud-logo.svg',
+            },
+            items: [
+                {
+                    label: '首页',
+                    position: 'left',
+                    to: '/',
+                },
+                {
+                    label: '文档',
+                    position: 'left',
+                    to: 'docs/',
+                },
+                {
+                    label: '博客',
+                    position: 'left',
+                    to: 'blog',
+                },
+                {
+                    label: '讨论交流',
+                    position: 'left',
+                    to: 'docs/',
+                },
+                {
+                    label: '私有化部署',
+                    position: 'left',
+                    to: 'docs/',
+                },
+                {
+                    label: '反馈建议',
+                    position: 'left',
+                    items: [
+                        {
+                            label: 'Github Discussion',
+                            href: 'https://github.com/dromara/sureness/discussions',
+                        },
+                        {
+                            label: 'Gitter Channel',
+                            href: 'https://gitter.im/usthe/sureness',
+                        },
+                        {
+                            label: 'QQ Group - 390083213',
+                            href: 'https://qm.qq.com/cgi-bin/qm/qr?k=3IpzQjFOztJe464_eMBmDHfT0YTWK5Qa&jump_from=webapi',
+                        },
+                    ],
+                },
+                {
+                    label: '其它',
+                    position: 'left',
+                    items: [
+                        {
+                            label: 'Design',
+                            to: 'docs/design',
+                        },
+                        {
+                            label: 'Contributing',
+                            to: 'docs/contributing',
+                        },
+                        {
+                            label: 'Sponsor',
+                            to: 'docs/sponsor',
+                        },
+                    ],
+                },
+                {
+                    label: '登陆/注册',
+                    href: 'https://console.tancloud.cn',
+                    position: 'right',
+                    className: 'header-console-link'
+                },
+            ],
+        },
+        footer: {
+            style: 'light',
+            links: [
+                {
+                    title: '产品介绍',
+                    items: [
+                        {
+                            label: 'Github',
+                            href: 'https://github.com/dromara/sureness',
+                        },
+                        {
+                            label: 'Gitee',
+                            href: 'https://gitee.com/dromara/sureness',
+                        },
+                        {
+                            label: 'High Performance',
+                            href: 'https://github.com/tomsun28/sureness-shiro-spring-security-benchmark',
+                        },
+                        {
+                            label: 'Dashboard',
+                            href: 'https://github.com/dromara/sureness/projects/1',
+                        },
+                    ],
+                },
+                {
+                    title: '探云科技',
+                    items: [
+                        {
+                            label: 'Dromara',
+                            href: 'https://dromara.org',
+                        },
+                        {
+                            label: 'Github Discussion',
+                            href: 'https://github.com/dromara/sureness/discussions',
+                        },
+                        {
+                            label: 'Gitter Channel',
+                            href: 'https://gitter.im/usthe/sureness',
+                        },
+                        {
+                            label: 'QQ Group - 390083213',
+                            href: 'https://qm.qq.com/cgi-bin/qm/qr?k=3IpzQjFOztJe464_eMBmDHfT0YTWK5Qa&jump_from=webapi',
+                        },
+                    ],
+                },
+                {
+                    title: '相关资源',
+                    items: [
+                        {
+                            label: 'Tom Blog',
+                            to: 'https://blog.usthe.com',
+                        },
+                        {
+                            label: 'USTHE',
+                            href: 'https://github.com/usthe',
+                        },
+                        {
+                            label: 'Tom',
+                            href: 'https://github.com/tomsun28',
+                        },
+                    ],
+                },
+            ],
+            logo: {
+                alt: 'TANCLOUD探云-易用友好的高性能监控云',
+                src: cdnUrl + 'img/tancloud-brand.svg',
+                href: 'https://tancloud.cn',
+            },
+            copyright: `Apache License 2.0 | Copyright © ${new Date().getFullYear()}`,
+        },
+    },
+    presets: [
+        [
+            '@docusaurus/preset-classic', {
+            docs: {
+                sidebarPath: require.resolve('./sidebars.json'),
+                // Please change this to your repo.
+                // editUrl:'https://github.com/dromara/sureness/edit/master/home/',
+                editUrl: `${repoUrl}/edit/${branch}/home/`,
+                editLocalizedFiles: true,
+                remarkPlugins: [
+                    [require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
+                ],
+            },
+            blog: {
+                showReadingTime: true,
+                postsPerPage: 3,
+                feedOptions: {
+                    type: 'all',
+                    copyright: `Copyright © ${new Date().getFullYear()} TANCLOUD, Inc.`,
+                },
+                // Please change this to your repo.
+                editUrl: `${repoUrl}/edit/${branch}/home/`,
+                editLocalizedFiles: true,
+            },
+            theme: {
+                customCss: require.resolve('./src/css/custom.css'),
+            },
+        },
+        ],
+    ],
+    plugins: [
+        [
+            '@docusaurus/plugin-client-redirects',
+            {
+                fromExtensions: ['html'],
+            }
+        ],
+        '@docusaurus/plugin-ideal-image',
+        [
+            '@docusaurus/plugin-pwa',
+            {
+                debug: false,
+                offlineModeActivationStrategies: ['appInstalled', 'queryString'],
+                // swRegister: false,
+                swCustom: path.resolve(__dirname, 'src/sw.js'),
+                pwaHead: [
+                    {
+                        tagName: 'link',
+                        rel: 'icon',
+                        href: cdnUrl + 'img/tancloud-logo.svg',
+                    },
+                    {
+                        tagName: 'link',
+                        rel: 'manifest',
+                        href: cdnUrl + 'manifest.json',
+                    },
+                    {
+                        tagName: 'meta',
+                        name: 'theme-color',
+                        content: 'rgb(234, 90, 7)',
+                    },
+                    {
+                        tagName: 'meta',
+                        name: 'apple-mobile-web-app-capable',
+                        content: 'yes',
+                    },
+                    {
+                        tagName: 'meta',
+                        name: 'apple-mobile-web-app-status-bar-style',
+                        content: '#000',
+                    },
+                    {
+                        tagName: 'link',
+                        rel: 'apple-touch-icon',
+                        href: cdnUrl + 'img/tancloud-logo.svg',
+                    },
+                    {
+                        tagName: 'link',
+                        rel: 'mask-icon',
+                        href: cdnUrl + 'img/tancloud-logo.svg',
+                        color: 'rgb(234, 90, 7)',
+                    },
+                    {
+                        tagName: 'meta',
+                        name: 'msapplication-TileImage',
+                        content: cdnUrl + 'img/tancloud-logo.svg',
+                    },
+                    {
+                        tagName: 'meta',
+                        name: 'msapplication-TileColor',
+                        content: '#000',
+                    },
+                ],
+            },
+        ],
+    ],
+    themes: ['@docusaurus/theme-live-codeblock'],
+    scripts: [
+        'https://cdn.jsdelivr.net/gh/buttons/buttons.github.io/buttons.js'
+    ]
+}

+ 274 - 0
home/i18n/en/code.json

@@ -0,0 +1,274 @@
+{
+  "theme.NotFound.title": {
+    "message": "Page Not Found",
+    "description": "The title of the 404 page"
+  },
+  "theme.NotFound.p1": {
+    "message": "We could not find what you were looking for.",
+    "description": "The first paragraph of the 404 page"
+  },
+  "theme.NotFound.p2": {
+    "message": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.",
+    "description": "The 2nd paragraph of the 404 page"
+  },
+  "theme.AnnouncementBar.closeButtonAriaLabel": {
+    "message": "Close",
+    "description": "The ARIA label for close button of announcement bar"
+  },
+  "theme.blog.paginator.navAriaLabel": {
+    "message": "Blog list page navigation",
+    "description": "The ARIA label for the blog pagination"
+  },
+  "theme.blog.paginator.newerEntries": {
+    "message": "Newer Entries",
+    "description": "The label used to navigate to the newer blog posts page (previous page)"
+  },
+  "theme.blog.paginator.olderEntries": {
+    "message": "Older Entries",
+    "description": "The label used to navigate to the older blog posts page (next page)"
+  },
+  "theme.blog.post.readingTime.plurals": {
+    "message": "One min read|{readingTime} min read",
+    "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
+  },
+  "theme.tags.tagsListLabel": {
+    "message": "Tags:",
+    "description": "The label alongside a tag list"
+  },
+  "theme.blog.post.readMore": {
+    "message": "Read More",
+    "description": "The label used in blog post item excerpts to link to full blog posts"
+  },
+  "theme.blog.post.paginator.navAriaLabel": {
+    "message": "Blog post page navigation",
+    "description": "The ARIA label for the blog posts pagination"
+  },
+  "theme.blog.post.paginator.newerPost": {
+    "message": "Newer Post",
+    "description": "The blog post button label to navigate to the newer/previous post"
+  },
+  "theme.blog.post.paginator.olderPost": {
+    "message": "Older Post",
+    "description": "The blog post button label to navigate to the older/next post"
+  },
+  "theme.tags.tagsPageTitle": {
+    "message": "Tags",
+    "description": "The title of the tag list page"
+  },
+  "theme.blog.post.plurals": {
+    "message": "One post|{count} posts",
+    "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
+  },
+  "theme.blog.tagTitle": {
+    "message": "{nPosts} tagged with \"{tagName}\"",
+    "description": "The title of the page for a blog tag"
+  },
+  "theme.tags.tagsPageLink": {
+    "message": "View All Tags",
+    "description": "The label of the link targeting the tag list page"
+  },
+  "theme.CodeBlock.copyButtonAriaLabel": {
+    "message": "Copy code to clipboard",
+    "description": "The ARIA label for copy code blocks button"
+  },
+  "theme.CodeBlock.copied": {
+    "message": "Copied",
+    "description": "The copied button label on code blocks"
+  },
+  "theme.CodeBlock.copy": {
+    "message": "Copy",
+    "description": "The copy button label on code blocks"
+  },
+  "theme.docs.sidebar.expandButtonTitle": {
+    "message": "Expand sidebar",
+    "description": "The ARIA label and title attribute for expand button of doc sidebar"
+  },
+  "theme.docs.sidebar.expandButtonAriaLabel": {
+    "message": "Expand sidebar",
+    "description": "The ARIA label and title attribute for expand button of doc sidebar"
+  },
+  "theme.docs.paginator.navAriaLabel": {
+    "message": "Docs pages navigation",
+    "description": "The ARIA label for the docs pagination"
+  },
+  "theme.docs.paginator.previous": {
+    "message": "Previous",
+    "description": "The label used to navigate to the previous doc"
+  },
+  "theme.docs.paginator.next": {
+    "message": "Next",
+    "description": "The label used to navigate to the next doc"
+  },
+  "theme.docs.sidebar.collapseButtonTitle": {
+    "message": "Collapse sidebar",
+    "description": "The title attribute for collapse button of doc sidebar"
+  },
+  "theme.docs.sidebar.collapseButtonAriaLabel": {
+    "message": "Collapse sidebar",
+    "description": "The title attribute for collapse button of doc sidebar"
+  },
+  "theme.docs.sidebar.responsiveCloseButtonLabel": {
+    "message": "Close menu",
+    "description": "The ARIA label for close button of mobile doc sidebar"
+  },
+  "theme.docs.sidebar.responsiveOpenButtonLabel": {
+    "message": "Open menu",
+    "description": "The ARIA label for open button of mobile doc sidebar"
+  },
+  "theme.docs.versions.unreleasedVersionLabel": {
+    "message": "This is unreleased documentation for {siteTitle} {versionLabel} version.",
+    "description": "The label used to tell the user that he's browsing an unreleased doc version"
+  },
+  "theme.docs.versions.unmaintainedVersionLabel": {
+    "message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.",
+    "description": "The label used to tell the user that he's browsing an unmaintained doc version"
+  },
+  "theme.docs.versions.latestVersionSuggestionLabel": {
+    "message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).",
+    "description": "The label userd to tell the user that he's browsing an unmaintained doc version"
+  },
+  "theme.docs.versions.latestVersionLinkLabel": {
+    "message": "latest version",
+    "description": "The label used for the latest version suggestion link label"
+  },
+  "theme.common.editThisPage": {
+    "message": "帮助我们修改文档",
+    "description": "The link label to edit the current page"
+  },
+  "theme.common.headingLinkTitle": {
+    "message": "Direct link to heading",
+    "description": "Title for link to heading"
+  },
+  "theme.lastUpdated.atDate": {
+    "message": " on {date}",
+    "description": "The words used to describe on which date a page has been last updated"
+  },
+  "theme.lastUpdated.byUser": {
+    "message": " by {user}",
+    "description": "The words used to describe by who the page has been last updated"
+  },
+  "theme.lastUpdated.lastUpdatedAtBy": {
+    "message": "Last updated{atDate}{byUser}",
+    "description": "The sentence used to display when a page has been last updated, and by who"
+  },
+  "theme.common.skipToMainContent": {
+    "message": "Skip to main content",
+    "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation"
+  },
+  "theme.SearchPage.documentsFound.plurals": {
+    "message": "One document found|{count} documents found",
+    "description": "Pluralized label for \"{count} documents found\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
+  },
+  "theme.SearchPage.existingResultsTitle": {
+    "message": "Search results for \"{query}\"",
+    "description": "The search page title for non-empty query"
+  },
+  "theme.SearchPage.emptyResultsTitle": {
+    "message": "Search the documentation",
+    "description": "The search page title for empty query"
+  },
+  "theme.SearchPage.inputPlaceholder": {
+    "message": "Type your search here",
+    "description": "The placeholder for search page input"
+  },
+  "theme.SearchPage.inputLabel": {
+    "message": "Search",
+    "description": "The ARIA label for search page input"
+  },
+  "theme.SearchPage.algoliaLabel": {
+    "message": "Search by Algolia",
+    "description": "The ARIA label for Algolia mention"
+  },
+  "theme.SearchPage.noResultsText": {
+    "message": "No results were found",
+    "description": "The paragraph for empty search result"
+  },
+  "theme.SearchPage.fetchingNewResults": {
+    "message": "Fetching new results...",
+    "description": "The paragraph for fetching new search results"
+  },
+  "theme.SearchBar.label": {
+    "message": "Search",
+    "description": "The ARIA label and placeholder for search button"
+  },
+  "Get Started": {
+    "message": "快速开始"
+  },
+  "theme.PwaReloadPopup.info": {
+    "message": "New version available",
+    "description": "The text for PWA reload popup"
+  },
+  "theme.PwaReloadPopup.refreshButtonText": {
+    "message": "Refresh",
+    "description": "The text for PWA reload button"
+  },
+  "theme.PwaReloadPopup.closeButtonAriaLabel": {
+    "message": "Close",
+    "description": "The ARIA label for close button of PWA reload popup"
+  },
+  "theme.Playground.result": {
+    "message": "Result",
+    "description": "The result label of the live codeblocks"
+  },
+  "theme.Playground.liveEditor": {
+    "message": "Live Editor",
+    "description": "The live editor label of the live codeblocks"
+  },
+  "Support for Jvm Modern Frameworks": {
+    "message": "适用大部分Jvm Web框架"
+  },
+  "Focus on Protection of API": {
+    "message": "面向API的高性能认证鉴权框架"
+  },
+  "Extendable": {
+    "message": "可扩展"
+  },
+  "Compatible": {
+    "message": "原生兼容"
+  },
+  "Multi And Fast": {
+    "message": "多支持与高性能"
+  },
+  "Multi Support Samples": {
+    "message": "大量集成样例"
+  },
+  "Benchmark Compare": {
+    "message": "基准性能测试"
+  },
+  "Why Is High Performance": {
+    "message": "为什么高性能"
+  },
+  "Get Started With Sureness within Minutes": {
+    "message": "几分钟入门Sureness"
+  },
+  "Focus on the protection of {restApi}. Based on {rbac}.{br}Provides {authentication} and {authorization}, etc.{br}Extension custom interface is {simple} and really {useful}.": {
+    "message": "基于 {rbac} (用户-角色-资源)主要关注于对 {restApi} 的安全 (认证与鉴权) 保护。{br} 自定义Subject,Subject Creator,Processor和自定义数据源等良好的{interface}, {sample}和{doc}助急速理解扩展使用。"
+  },
+  "Support {WebSockets}, HTTP containers ({Servlet} and {JaxRs}).{br}No framework dependency.{native} supports {SpringBoot}, {SpringWebFlux}, {Javalin}, {Quarkus},{Ktor}, {Solon}, {Jfinal}, {Micronaut} etc.": {
+    "message": "支持 {WebSockets} ,主流 HTTP 容器 {Servlet} 和 {JaxRs}。{br} 无特定Web框架依赖, 原生支持 {SpringBoot}, {SpringWebFlux}, {Javalin}, {Quarkus}, {Ktor}, {Solon}, {Jfinal}, {Micronaut} 等。"
+  },
+  "Supports {jwt}, {basic}, {digest} etc.{br}{dynamic} modification of {perm}.{br}{high} with {tree}.": {
+    "message": "原生拥有 {jwt}, {basic}, {digest} 等认证方式。{br} 支持{dynamicCn}(动态配置每个 API 谁有权访问)。{br} 基于{treeCn}拥有的{highCn}。"
+  },
+  "Sureness allows you to security any server written with jvm modern frameworks such as {Spring}, {SpringBoot}, {SpringWebFlux}, {Javalin}, {Quarkus}, {Micronaut}, {Solon}, {Jfinal} or {Ktor} as well as frameworks for Kotlin.": {
+    "message": "您可以通过使用Sureness来保护大部分JVM系语言的现代化框架搭建的后台服务,比如:Java 语言的 {Spring}, {SpringBoot}, {SpringWebFlux}, {Javalin}, {Quarkus}, {Micronaut}, {Solon}, {Jfinal} 和 Kotlin 语言的 {Ktor}."
+  },
+  "The essence of Sureness is to use {interceptor}(like servlet filter or Spring interceptor)  to intercept all rest requests for authenticating and authorizing.{br}So no matter any framework, as long as it has a interceptor, it can integrate with sureness. Sureness uses {handling}, {checkIn} will return {SubjectSum}(user information) when auth success, or throw different types of auth exceptions when auth error.": {
+    "message": "Sureness的本质是使用{interceptor}(如 servlet filter or Spring interceptor)拦截所有API请求对其认证鉴权判断。{br} 所以对于任何Web框架,只要其支持拦截器{interceptor}, 就能轻松和Sureness集成。Sureness使用异常处理流程即{handling}, {checkIn} 方法会在认证鉴权成功后返回带有用户信息的 {SubjectSum} 对象,但认证鉴权失败时抛出不同类型的异常,用户根据这些异常来继续后面的流程。"
+  },
+  "{Benchmark} test shows Sureness to lose 0.026ms performance compared to frameless application, Shiro lose 0.088ms, Spring Security lose 0.116ms.{br}In contrast, Sureness basically does not consume performance, and the performance (TPS loss) is {times3} that of Shiro and {times4} that of Spring Security.{br}{increases}.": {
+    "message": "基准测试显示 Sureness 对比无权限框架应用损耗 0.026ms 性能,Shiro 损耗 0.088ms,Spring Security 损耗 0.116ms,相比之下 Sureness 性能(参考TPS损耗)是 Shiro 的 {times3cn},Spring Security 的 {times4cn}。{br}{increaseCn}。"
+  },
+  "In a large number of requests, we found that the {linear} of the {filter} is a performance bottleneck. {br}So we used a {tree} instead of {ant}.{br}Practice has proved that it is very {effective}.": {
+    "message": "在大量的请求和过滤链匹配下,我们发现主要的性能瓶颈在{linearCn}这。{br} 所以我们使用了{treeCn}来代替其它安全框架常用的{antCn}。{br} 实践效果证明这有效,提升了{effCn}。"
+  },
+  "We provide many tutorials and samples, you can refer to them to builda complete permission project within 10 minutes.{br}Have Fun!": {
+    "message": "我们提供了很多教程和集成样例,您可以参考其中在10分钟之内使用 Sureness 做一个完整功能的认证鉴权项目。{br} Have Fun!"
+  },
+  "Friend Links": {
+    "message": "友情链接"
+  },
+  "Media Partners": {
+    "message": "合作媒体"
+  }
+}

+ 298 - 0
home/i18n/en/docusaurus-plugin-content-blog/2022-01-30-hertz-beat.md

@@ -0,0 +1,298 @@
+---
+title: TANCLOUD开源监控系统HertzBeat发布
+author: tom  
+author_title: Tancloud   
+author_url: https://github.com/tomsun28  
+author_image_url: https://avatars.githubusercontent.com/u/24788200?s=400&v=4  
+tags: [integrate]  
+---
+
+
+[javalin-sureness sample repository](https://github.com/tomsun28/sureness/tree/master/samples/javalin-sureness)
+
+Using Sureness to secure Javalin REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC)
+
+<!--truncate-->
+
+## What You Will Learn
+
+* Creating a simple REST API using Javalin
+* Learn how to integrate Sureness into a Javalin application
+* Learn how to issue a JWT
+* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API
+* Test API authorization - use different users to verify that they can access the REST API
+
+
+The tutorial assumes that you know what  JWT, Basic Auth, Digest Auth, RBAC are. If you
+do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction.
+
+## Setting Up Dependencies
+
+First, you will need to create a maven project and add Javalin, Sureness dependencies coordinate
+
+````
+        <dependency>
+            <groupId>io.javalin</groupId>
+            <artifactId>javalin</artifactId>
+            <version>{{site.javalinversion}}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usthe.sureness</groupId>
+            <artifactId>sureness-core</artifactId>
+            <version>1.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.30</version>
+        </dependency>
+````
+
+
+## Setting Up Javalin and Create REST API
+
+We need to create a simple Javalin app and provide some  REST API for test.
+
+```
+        // init javalin
+        Javalin app = Javalin.create().start(8088);
+```
+
+```
+        // create simple rest api
+        // simple rest api
+        app.routes(() ->
+            path("api", () -> {
+                path("v3", () -> {
+                    get("host", ctx -> ctx.result("get /api/v3/host success"));
+                    put("book", ctx -> ctx.result("put /api/v3/book success"));
+                });
+                path("v2", () -> {
+                    path("host", () -> {
+                        get(ctx -> ctx.result("get /api/v2/host success"));
+                        post(ctx -> ctx.result("post /api/v2/host success"));
+                        put(ctx -> ctx.result("put /api/v2/host success"));
+                        delete(ctx -> ctx.result("delete /api/v2/host success"));
+                    });
+                });
+                path("v1", () -> {
+                    path("source1", () -> {
+                        get(ctx -> ctx.result("get /api/v1/source1 success"));
+                        post(ctx -> ctx.result("post /api/v1/source1 success"));
+                        put(ctx -> ctx.result("put /api/v1/source1 success"));
+                        delete(ctx -> ctx.result("delete /api/v1/source1 success"));
+                    });
+                });
+            }));
+```
+
+
+## Setting Up Sureness
+
+#### 1. Use the Default Configuration to Configure Sureness
+
+The default configuration -`DefaultSurenessConfig` uses the document datasource `sureness.yml` as the auth datasource.  
+It supports JWT, Basic Auth, Digest Auth authentication.
+```
+    public static void main(String[] args) {
+        // init sureness default config
+        new DefaultSurenessConfig();
+    }
+```
+
+####  2. Config Document Datasource - `sureness.yml`
+
+Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`.
+
+Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`.  eg:
+
+````yaml
+## -- sureness.yml document dataSource-- ##
+
+# load api resource which need be protected, config role who can access these resource.
+# resources that are not configured are also authenticated and protected by default, but not authorized
+# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3
+# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===post===[role1]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v1/source2===get===[]
+  - /api/v2/host===post===[role2,role3]
+  - /api/v2/host===get===[role2,role3]
+  - /api/v2/host===delete===[role2,role3]
+  - /api/v2/host===put===[role2,role3]
+  - /api/v3/*===*===[role1,role2,role3]
+
+# load api resource which do not need be protected, means them need be excluded.
+# these api resource can be access by everyone
+excludedResource:
+  - /api/v3/host===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+
+# account info
+# there are three account: admin, root, tom
+# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500
+# eg: root has role1, unencrypted password is 23456
+# eg: tom has role3, unencrypted password is 32113
+account:
+  - appId: admin
+    # if add salt, the password is encrypted password - the result: MD5(password+salt)
+    # digest auth not support encrypted password
+    # if no salt, the password is unencrypted password
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1,role2]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+````
+
+
+
+####  3. Add an Interceptor Intercepting All Requests
+
+The essence of sureness is to intercept all rest requests for authenticating and authorizing.     The interceptor can be a filter or interceptor, it intercepts all request to check them. In Javalin, we use `app.before()`.
+
+```
+        // intercept all rest requests for authenticating and authorizing
+        app.before(ctx -> {
+            SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(ctx.req);
+            // when auth error , the exception throw, you should use app.exception() catch it and define return
+            if (subject != null) {
+                SurenessContextHolder.bindSubject(subject);
+            }
+        });
+
+        app.after(ctx ->  SurenessContextHolder.unbindSubject());
+
+```
+
+#### 4. Last, Implement Auth Exception Handling Process
+
+Sureness uses exception handling process:
+
+- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information.
+- If auth failure, method - `checkIn()` will throw different types of auth exceptions.
+
+We need to continue the subsequent process based on these exceptions.(eg: return the request response)
+
+Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something:
+
+````
+        // when auth error , the exception throw, you should use app.exception() catch it and define return
+        app.exception(UnknownAccountException.class, (e, ctx) -> {
+            log.debug("this request user account not exist");
+            ctx.status(401).result(e.getMessage());
+        }).exception(IncorrectCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential is incorrect");
+            ctx.status(401).result(e.getMessage());
+        }).exception(ExpiredCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential expired");
+            ctx.status(401).result(e.getMessage());
+        }).exception(NeedDigestInfoException.class, (e, ctx) -> {
+            log.debug("you should try once again with digest auth information");
+            ctx.status(401).header("WWW-Authenticate", e.getAuthenticate());
+        }).exception(UnauthorizedException.class, (e, ctx) -> {
+            log.debug("this account can not access this resource");
+            ctx.status(403).result(e.getMessage());
+        }).exception(Exception.class, (e, ctx) -> {
+            log.error("other exception happen: ", e);
+            ctx.status(500).result(e.getMessage());
+        });
+
+````
+
+
+## Provide an Issue JWT Api
+
+Now we provide a REST API to issue JWT. We can use this JWT to test JWT auth.
+
+````
+       // issue jwt rest api
+        app.get("/auth/token", ctx -> {
+            SubjectSum subjectSum = SurenessContextHolder.getBindSubject();
+            if (subjectSum == null) {
+                ctx.result("Please auth!");
+            } else {
+                String principal = (String) subjectSum.getPrincipal();
+                List<String> roles = (List<String>) subjectSum.getRoles();
+                // issue jwt
+                String jwt = JsonWebTokenUtil.issueJwt(UUID.randomUUID().toString(), principal,
+                        "token-server", 3600L, roles);
+                ctx.result(jwt);
+            }
+        });
+````
+
+**All done, we can test now!**
+
+## Test
+
+Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support?   
+This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api.
+
+Let's test it. (we use postman and chrome to test.)
+
+### Test Authentication
+
+####  1. Basic Auth Test
+
+Use postman Basic auth, as shown below:
+
+* success - input username: admin, password: admin
+
+![success](/img/docs/javalin/test1.PNG)
+
+
+* fail - input username: admin, password: 12345
+
+![fail](/img/docs/javalin/test2.PNG)
+
+####  2. Digest Auth Test
+
+Note: If password has been encrypted,  Digest auth not support.(So the account admin not support Digest auth).  
+Use chrome to Digest auth, as shown below:
+
+![success](/img/docs/javalin/test3.PNG)
+
+![success](/img/docs/javalin/test4.PNG)
+
+####  3. JWT Auth Test
+
+First, we should access **[GET /auth/token]** api to get a JWT to use, as shown below:
+
+![success](/img/docs/javalin/test5.PNG)
+
+Then, use the JWT as Bearer Token to access REST API, as shown below:
+
+![success](/img/docs/javalin/test6.PNG)
+
+
+### Test Authorization
+
+* success - user **tom** has role **role3**, the api **[DELETE - /api/v2/host]** support **role3** access, so **tom** can access api **[DELETE - /api/v2/host]** success, as shown below:
+
+![success](/img/docs/javalin/test7.PNG)
+
+
+* fail - user **tom** only has role **role3**, the api **[GET - /api/v1/source1]** only support **role2** access, not support **role3**,  so **tom** can not access api **[GET - /api/v1/source1]**, as shown below:
+
+![fail](/img/docs/javalin/test8.PNG)
+
+
+## Conclusion
+
+Javalin is a framework dedicated to simplicity and ease of use, and so is Sureness.  
+We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc.   
+Finally, thank you again for reading.
+
+[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness)

+ 22 - 0
home/i18n/en/docusaurus-plugin-content-docs/current.json

@@ -0,0 +1,22 @@
+{
+  "version.label": {
+    "message": "下一篇",
+    "description": "The label for version current"
+  },
+  "sidebar.docs.category.Guides": {
+    "message": "入门",
+    "description": "The label for category Guides in sidebar docs"
+  },
+  "sidebar.docs.category.Advanced": {
+    "message": "进阶扩展",
+    "description": "The label for category Advanced in sidebar docs"
+  },
+  "sidebar.docs.category.Integrate": {
+    "message": "集成案例",
+    "description": "The label for category Integrate in sidebar docs"
+  },
+  "sidebar.docs.category.Others": {
+    "message": "其它",
+    "description": "The label for category Others in sidebar docs"
+  }
+}

+ 81 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-datasource.md

@@ -0,0 +1,81 @@
+---
+id: custom-datasource  
+title: 自定义数据源   
+sidebar_label: 自定义数据源     
+---
+
+## 自定义数据源
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `PathTreeProvider`: 资源的数据源接口,实现从数据库,文本等加载数据,加载到对应的资源权限匹配器`DefaultPathRoleMatcher`中
+- `SurenessAccountProvider`: 用户的账户密钥信息接口,实现从数据库,文本等加载数据,加载到需要账户数据的`processor`中
+
+
+首先我们先来认识下sureness提供的两个用户信息和资源权限信息的接口,用户可以实现这些接口自定义从不同的数据源给sureness提供数据。
+当我们把项目从配置文件模式切换成数据库模式时,也只是简单替换了这些接口的实现类而已。
+
+一. `PathTreeProvider` 资源权限配置信息的数据源接口,我们可以实现从数据库,文本等加载接口想要的资源权限配置数据
+
+````
+public interface PathTreeProvider {
+
+    Set<String> providePathData();
+
+    Set<String> provideExcludedResource();
+}
+
+````  
+
+此接口主要是需要实现上面这两个方法,providePathData是加载资源权限配置信息,也就是我们配置文件模式下sureness.yml的resourceRole信息列,
+provideExcludedResource是加载哪些资源可以被过滤不认证鉴权,也就是sureness.yml下的excludedResource信息列,如下。
+
+````
+resourceRole:
+  - /api/v2/host===post===[role2,role3,role4]
+  - /api/v2/host===get===[role2,role3,role4]
+  - /api/v2/host===delete===[role2,role3,role4]
+  - /api/v2/host===put===[role2,role3,role4]
+  - /api/mi/**===put===[role2,role3,role4]
+  - /api/v1/getSource1===get===[role1,role2]
+  - /api/v2/getSource2/*/*===get===[role2]
+
+excludedResource:
+  - /api/v1/source3===get
+  - /api/v3/host===get
+  - /**/*.css===get
+  - /**/*.ico===get
+  - /**/*.png===get
+````
+
+而当我们使用数据库模式时,实现这些信息从数据库关联读取就ok了,规范返回 eg: /api/v2/host===post===[role2,role3,role4] 格式的数据列,
+具体的数据库实现类参考类 - [DatabasePathTreeProvider](https://github.com/tomsun28/sureness/blob/master/sample-tom/src/main/java/com/usthe/sureness/sample/tom/sureness/provider/DatabasePathTreeProvider.java)
+
+二. `SurenessAccountProvider`这第二个相关的接口就是用户的账户密钥信息提供接口,我们需要实现从数据库或者文本等其他数据源那里去加载我们想要的用户的账户信息数据,
+这些数据提供需要账户数据的processor进行用户的认证。
+
+````
+public interface SurenessAccountProvider {
+    SurenessAccount loadAccount(String appId);
+}
+````
+此接口主要需要实现上面这个loadAccount方法,通过用户的唯一标识appid来从数据库或者redis缓存中查找到用户的账户信息返回即可。
+用户账户信息类SurenessAccount如下:
+
+````
+public class DefaultAccount implements SurenessAccount {
+
+    private String appId;
+    private String password;
+    private String salt;
+    private List<String> ownRoles;
+    private boolean disabledAccount;
+    private boolean excessiveAttempts;
+}
+```` 
+
+比较简单,主要是需要提供用户的密码相关信息即可,供sureness认证时密钥判断正确与否。  
+这个具体的数据库接口实现可参考类 - [DatabaseAccountProvider](https://github.com/tomsun28/sureness/blob/master/sample-tom/src/main/java/com/usthe/sureness/sample/tom/sureness/provider/DatabaseAccountProvider.java)
+
+
+具体扩展实践请参考 [Springboot项目集成-数据库方案](/docs/integrate/sample-tom)     

+ 35 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-processor.md

@@ -0,0 +1,35 @@
+---
+id: custom-processor  
+title: 自定义processor     
+sidebar_label: 自定义processor     
+---
+
+processor就是对请求的用户账户信息subject真正的认证鉴权处理器,我们需要实现BaseProcessor接口,来实现我们自定义的认证鉴权方式。  
+sureness已经内置基于账户密码认证方式处理PasswordSubject的PasswordProcessor,基于jwt认证方式处理JwtSubject的JwtProcessor等。
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `Processor`: `Subject`处理接口,根据Subject信息,进行认证鉴权
+
+```
+public abstract class BaseProcessor implements Processor{
+
+    public abstract boolean canSupportSubjectClass(Class<?> var);
+
+    public abstract Subject authenticated (Subject var) throws SurenessAuthenticationException;
+
+    public abstract void authorized(Subject var) throws SurenessAuthorizationException;
+}
+
+```
+
+上面就是BaseProcessor的一些重要接口方法,自定义processor需要我们去实现这些方法。
+
+- `canSupportSubjectClass` 判断是否支持入参的此Subject类类型,比如 JwtProcessor只支持JwtSubject, PasswordProcessor只支持PasswordSubject
+- `authenticated` 对subject进行认证,根据传入的subject信息和系统内信息,进行请求用户的账户认证
+- `authorized` 对subject进行鉴权,鉴权判断此用户是否拥有其访问api的访问权限
+
+sureness使用异常流程模型,以上的认证失败或鉴权失败都会抛出不同类型的异常,用户在最外部捕获判断实现接下来的流程。
+
+sureness默认异常类型参考 [默认异常类型](/docs/start/default-exception)    
+具体扩展实践请参考 [Springboot项目集成-数据库方案](/docs/integrate/sample-tom)    

+ 20 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-subject-creator.md

@@ -0,0 +1,20 @@
+---
+id: custom-subject-creator  
+title: 自定义subject creator   
+sidebar_label: 自定义subject creator    
+---
+
+自定义`subject creator`是我们使用频率最高的扩展,当请求体对象并不是`servlet`或者`jax-rs`标准`api`时,
+或者我们从request请求的不同地方获取账户信息时,我们就需要自定义`subject creator`,
+使其通过请求对象获取我们需要的请求信息(请求路径,请求方法,认证信息等), 从而创建出对应的`subject`.
+
+sureness已经内置能创建出基于账户密码的PasswordSubject的BasicSubjectServletCreator,
+和创建出jwt类型JwtSubject的JwtSubjectServletCreator等,当然我们可以自定义自己需要的subjectCreator来创建subject
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `SubjectCreate`: 创建`Subject`接口,根据请求内容创建不同类型的`Subject`对象
+
+实现`SubjectCreate`接口方法,根据request请求的内容创建出对应需要的的`subject`
+
+具体扩展实践请参考 [sample-spring-webflux项目集成案例](/docs/integrate/sample-spring-webflux)      

+ 20 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/advanced/custom-subject.md

@@ -0,0 +1,20 @@
+---
+id: custom-subject  
+title: 自定义subject  
+sidebar_label: 自定义subject  
+---
+
+subject包含的就是我们request请求所带的用户信息,sureness已经内置基于账户密码的PasswordSubject,
+基于jwt的JwtSubject等,当然我们可以自定义自己需要的subject来扩充自己的用户信息
+
+自定义前建议了解`sureness`处理流程和提供的扩展接口,详见 [进阶扩展](/docs/advanced/extend-point)
+
+- `Subject`: 认证鉴权对象接口,提供访问对象的账户密钥,请求资源,角色等信息
+
+自定义subject需要走以下流程:
+
+1. 实现`Subject`接口,添加自定义的`subject`内容
+2. 实现`SubjectCreate`接口方法,创建出自定义的`subject` 参考[自定义Subject Creator](/docs/advanced/custom-subject-creator)
+3. 实现`Processor`接口,支持处理自定义的`subject` 参考[自定义Processor](/docs/advanced/custom-processor)
+
+具体扩展实践请参考 [使用sureness30分钟项目集成案例](/docs/integrate/sample-tom)     

+ 22 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/advanced/extend-point.md

@@ -0,0 +1,22 @@
+---
+id: extend-point  
+title: 进阶扩展  
+sidebar_label: 扩展点    
+---
+
+`sureness`支持自定义`subject`,自定义注册`subjectCreator`,自定义`processor`处理器,自定义数据源加载器等。
+
+进阶自定义扩展之前我们先来了解下sureness的大致流程:
+
+![flow](/img/docs/flow-cn.png)
+
+如上面的流程所讲,Subject被SubjectCreate根据request请求体所创造,不同的认证鉴权处理器Processor来处理所支持的Subject。
+
+
+sureness提供了下面这些常用接口作为扩展点:
+
+- `Subject`: 认证鉴权对象接口,提供访问对象的账户密钥,请求资源,角色等信息
+- `SubjectCreate`: 创建`Subject`接口,根据请求内容创建不同类型的`Subject`对象
+- `Processor`: `Subject`处理接口,根据Subject信息,进行认证鉴权
+- `PathTreeProvider`: 资源的数据源接口,实现从数据库,文本等加载数据
+- `SurenessAccountProvider`: 用户的账户密钥信息接口,实现从数据库,文本等加载数据    

+ 65 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/contributing.md

@@ -0,0 +1,65 @@
+---
+id: contributing  
+title: 参与贡献    
+sidebar_label: 参与贡献    
+---
+
+Contributing to Sureness
+=======================================
+Very welcome to Contribute this project, go further and better with sureness.
+Firstly, thanks for your interest in contributing! I hope that this will be a pleasant first experience for you, and that you will return to continue contributing.
+
+Components of Repository:
+- [sureness's kernel code--sureness-core](https://github.com/usthe/sureness/tree/master/core)
+- [sureness integration springboot sample(configuration file scheme)--sample-bootstrap](https://github.com/usthe/sureness/tree/master/sample-bootstrap)
+- [sureness integration springboot sample(database scheme)-sample-tom](https://github.com/usthe/sureness/tree/master/sample-tom)
+- [sample projects using sureness in each framework(javalin,ktor,quarkus)--samples](https://github.com/usthe/sureness/tree/master/samples)
+
+## How to contribute?
+
+Most of the contributions that we receive are code contributions, but you can
+also contribute to the documentation or simply report solid bugs
+for us to fix.
+
+For new contributors, please take a look at issues or pull requests with a tag called below.
+
+[Good first issue](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)    
+[Help wanted](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)     
+[Good first pull request](https://github.com/usthe/sureness/issues?q=label%3A%22good+first+pull+request%22+)
+
+## Join discussion
+
+[Github Discussion](https://github.com/usthe/sureness/discussions)      
+[Gitter Channel](https://gitter.im/usthe/sureness)
+
+----   
+----   
+
+参与贡献
+=======================================
+
+非常欢迎参与项目贡献,我们致力于维护一个互相帮助的快乐社区。
+
+仓库的组成部分:
+- [sureness的核心代码--sureness-core](https://github.com/usthe/sureness/tree/master/core)
+- [使用sureness集成springboot搭建权限项目(配置文件方案)--sample-bootstrap](https://github.com/usthe/sureness/tree/master/sample-bootstrap)
+- [使用sureness集成springboot搭建权限项目(数据库方案)--sample-tom](https://github.com/usthe/sureness/tree/master/sample-tom)
+- [各个框架使用sureness的样例项目(javalin,ktor,quarkus)--samples](https://github.com/usthe/sureness/tree/master/samples)
+
+
+## 如何贡献?
+
+我们不仅仅接收代码的贡献提交,您也可以通过提交文档的更新或者BUG的报告来参与社区贡献。
+
+如果是新的贡献者,请首先了解参考如下样例的提交Issues,提交Pull Requests如果工作。
+
+[Good first issue](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)    
+[Help wanted](https://github.com/usthe/sureness/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)     
+[Good first pull request](https://github.com/usthe/sureness/issues?q=label%3A%22good+first+pull+request%22+)
+
+## 加入交流
+
+[Github Discussion](https://github.com/usthe/sureness/discussions)         
+[Gitter Channel](https://gitter.im/usthe/sureness)    
+QQ交流群:390083213    
+微信公众号:sureness

+ 13 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/design.md

@@ -0,0 +1,13 @@
+---
+id: design  
+title: 设计文档     
+sidebar_label: 设计文档     
+---
+
+### 高性能匹配
+
+![pathRoleMatcher](/img/docs/PathRoleMatcher.svg)
+
+### 处理流程
+
+![sureness-core](/img/docs/sureness-core.svg)  

+ 14 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-bootstrap.md

@@ -0,0 +1,14 @@
+---
+id: sample-bootstrap  
+title: Springboot项目集成-配置文件方案    
+sidebar_label: Springboot项目集成-配置文件方案    
+---
+
+[Springboot项目集成-配置文件方案代码仓库地址](https://github.com/tomsun28/sureness/tree/master/sample-bootstrap)
+
+- 基于`springboot`
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`sureness-config`
+- 使用默认的`JWT, Basic Auth, Digest Auth`方式认证鉴权
+- 保护入口: `SurenessFilterExample`
+- 推荐使用`postman`测试,测试样例为`sample-bootstrap-postman.json`,导入`postman`即可  

+ 296 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-javalin.md

@@ -0,0 +1,296 @@
+---
+id: sample-javalin  
+title: Javalin项目集成    
+sidebar_label: Javalin项目集成    
+---
+
+
+# Using Sureness to protect the security of Javalin REST API
+
+[javalin-sureness sample repository](https://github.com/tomsun28/sureness/tree/master/samples/javalin-sureness)
+
+Using Sureness to secure Javalin REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC)
+
+
+## What You Will Learn
+
+* Creating a simple REST API using Javalin
+* Learn how to integrate Sureness into a Javalin application
+* Learn how to issue a JWT
+* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API
+* Test API authorization - use different users to verify that they can access the REST API
+
+
+The tutorial assumes that you know what  JWT, Basic Auth, Digest Auth, RBAC are. If you
+do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction.
+
+## Setting Up Dependencies
+
+First, you will need to create a maven project and add Javalin, Sureness dependencies coordinate
+
+````
+        <dependency>
+            <groupId>io.javalin</groupId>
+            <artifactId>javalin</artifactId>
+            <version>{{site.javalinversion}}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usthe.sureness</groupId>
+            <artifactId>sureness-core</artifactId>
+            <version>1.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.30</version>
+        </dependency>
+````
+
+
+## Setting Up Javalin and Create REST API
+
+We need to create a simple Javalin app and provide some  REST API for test.
+
+```
+        // init javalin
+        Javalin app = Javalin.create().start(8088);
+```
+
+```
+        // create simple rest api
+        // simple rest api
+        app.routes(() ->
+            path("api", () -> {
+                path("v3", () -> {
+                    get("host", ctx -> ctx.result("get /api/v3/host success"));
+                    put("book", ctx -> ctx.result("put /api/v3/book success"));
+                });
+                path("v2", () -> {
+                    path("host", () -> {
+                        get(ctx -> ctx.result("get /api/v2/host success"));
+                        post(ctx -> ctx.result("post /api/v2/host success"));
+                        put(ctx -> ctx.result("put /api/v2/host success"));
+                        delete(ctx -> ctx.result("delete /api/v2/host success"));
+                    });
+                });
+                path("v1", () -> {
+                    path("source1", () -> {
+                        get(ctx -> ctx.result("get /api/v1/source1 success"));
+                        post(ctx -> ctx.result("post /api/v1/source1 success"));
+                        put(ctx -> ctx.result("put /api/v1/source1 success"));
+                        delete(ctx -> ctx.result("delete /api/v1/source1 success"));
+                    });
+                });
+            }));
+```
+
+
+## Setting Up Sureness
+
+#### 1. Use the Default Configuration to Configure Sureness
+
+The default configuration -`DefaultSurenessConfig` uses the document datasource `sureness.yml` as the auth datasource.  
+It supports JWT, Basic Auth, Digest Auth authentication.
+```
+    public static void main(String[] args) {
+        // init sureness default config
+        new DefaultSurenessConfig();
+    }
+```
+
+####  2. Config Document Datasource - `sureness.yml`
+
+Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`.
+
+Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`.  eg:
+
+````yaml
+## -- sureness.yml document dataSource-- ##
+
+# load api resource which need be protected, config role who can access these resource.
+# resources that are not configured are also authenticated and protected by default, but not authorized
+# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3
+# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===post===[role1]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v1/source2===get===[]
+  - /api/v2/host===post===[role2,role3]
+  - /api/v2/host===get===[role2,role3]
+  - /api/v2/host===delete===[role2,role3]
+  - /api/v2/host===put===[role2,role3]
+  - /api/v3/*===*===[role1,role2,role3]
+
+# load api resource which do not need be protected, means them need be excluded.
+# these api resource can be access by everyone
+excludedResource:
+  - /api/v3/host===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+
+# account info
+# there are three account: admin, root, tom
+# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500
+# eg: root has role1, unencrypted password is 23456
+# eg: tom has role3, unencrypted password is 32113
+account:
+  - appId: admin
+    # if add salt, the password is encrypted password - the result: MD5(password+salt)
+    # digest auth not support encrypted password
+    # if no salt, the password is unencrypted password
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1,role2]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+````
+
+
+
+####  3. Add an Interceptor Intercepting All Requests
+
+The essence of sureness is to intercept all rest requests for authenticating and authorizing.     The interceptor can be a filter or interceptor, it intercepts all request to check them. In Javalin, we use `app.before()`.
+
+```
+        // intercept all rest requests for authenticating and authorizing
+        app.before(ctx -> {
+            SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(ctx.req);
+            // when auth error , the exception throw, you should use app.exception() catch it and define return
+            if (subject != null) {
+                SurenessContextHolder.bindSubject(subject);
+            }
+        });
+
+        app.after(ctx ->  SurenessContextHolder.unbindSubject());
+
+```
+
+#### 4. Last, Implement Auth Exception Handling Process
+
+Sureness uses exception handling process:
+
+- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information.
+- If auth failure, method - `checkIn()` will throw different types of auth exceptions.
+
+We need to continue the subsequent process based on these exceptions.(eg: return the request response)
+
+Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something:
+
+````
+        // when auth error , the exception throw, you should use app.exception() catch it and define return
+        app.exception(UnknownAccountException.class, (e, ctx) -> {
+            log.debug("this request user account not exist");
+            ctx.status(401).result(e.getMessage());
+        }).exception(IncorrectCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential is incorrect");
+            ctx.status(401).result(e.getMessage());
+        }).exception(ExpiredCredentialsException.class, (e, ctx) -> {
+            log.debug("this account credential expired");
+            ctx.status(401).result(e.getMessage());
+        }).exception(NeedDigestInfoException.class, (e, ctx) -> {
+            log.debug("you should try once again with digest auth information");
+            ctx.status(401).header("WWW-Authenticate", e.getAuthenticate());
+        }).exception(UnauthorizedException.class, (e, ctx) -> {
+            log.debug("this account can not access this resource");
+            ctx.status(403).result(e.getMessage());
+        }).exception(Exception.class, (e, ctx) -> {
+            log.error("other exception happen: ", e);
+            ctx.status(500).result(e.getMessage());
+        });
+
+````
+
+
+## Provide an Issue JWT Api
+
+Now we provide a REST API to issue JWT. We can use this JWT to test JWT auth.
+
+````
+       // issue jwt rest api
+        app.get("/auth/token", ctx -> {
+            SubjectSum subjectSum = SurenessContextHolder.getBindSubject();
+            if (subjectSum == null) {
+                ctx.result("Please auth!");
+            } else {
+                String principal = (String) subjectSum.getPrincipal();
+                List<String> roles = (List<String>) subjectSum.getRoles();
+                // issue jwt
+                String jwt = JsonWebTokenUtil.issueJwt(UUID.randomUUID().toString(), principal,
+                        "token-server", 3600L, roles);
+                ctx.result(jwt);
+            }
+        });
+````
+
+**All done, we can test now!**
+
+## Test
+
+Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support?   
+This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api.
+
+Let's test it. (we use postman and chrome to test.)
+
+### Test Authentication
+
+####  1. Basic Auth Test
+
+Use postman Basic auth, as shown below:
+
+* success - input username: admin, password: admin
+
+![success](/img/docs/javalin/test1.PNG)
+
+
+* fail - input username: admin, password: 12345
+
+![fail](/img/docs/javalin/test2.PNG)
+
+####  2. Digest Auth Test
+
+Note: If password has been encrypted,  Digest auth not support.(So the account admin not support Digest auth).  
+Use chrome to Digest auth, as shown below:
+
+![success](/img/docs/javalin/test3.PNG)
+
+![success](/img/docs/javalin/test4.PNG)
+
+####  3. JWT Auth Test
+
+First, we should access **[GET /auth/token]** api to get a JWT to use, as shown below:
+
+![success](/img/docs/javalin/test5.PNG)
+
+Then, use the JWT as Bearer Token to access REST API, as shown below:
+
+![success](/img/docs/javalin/test6.PNG)
+
+
+### Test Authorization
+
+* success - user **tom** has role **role3**, the api **[DELETE - /api/v2/host]** support **role3** access, so **tom** can access api **[DELETE - /api/v2/host]** success, as shown below:
+
+![success](/img/docs/javalin/test7.PNG)
+
+
+* fail - user **tom** only has role **role3**, the api **[GET - /api/v1/source1]** only support **role2** access, not support **role3**,  so **tom** can not access api **[GET - /api/v1/source1]**, as shown below:
+
+![fail](/img/docs/javalin/test8.PNG)
+
+
+## Conclusion
+
+Javalin is a framework dedicated to simplicity and ease of use, and so is Sureness.  
+We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc.   
+Finally, thank you again for reading.
+
+[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness)

+ 17 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-ktor.md

@@ -0,0 +1,17 @@
+---
+id: sample-ktor  
+title: Ktor项目集成    
+sidebar_label: Ktor项目集成     
+---
+
+[ktor-sureness例子项目仓库地址](https://github.com/tomsun28/sureness/tree/master/samples/ktor-sureness)
+
+- 基于`ktor, servlet`
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`sureness-config`
+- 使用默认的`JWT, Basic Auth, Digest Auth`方式认证鉴权
+- 例子中包含`REST API`
+- 保护入口: `Application.kt`
+- 推荐使用`postman`测试
+
+

+ 423 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-micronaut.md

@@ -0,0 +1,423 @@
+---
+id: sample-micronaut  
+title: Micronaut项目集成    
+sidebar_label: Micronaut项目集成  
+---
+
+Using Sureness to secure micronaut REST API by providing authentication(JWT,Basic,Digest) and authorization(RBAC)
+
+
+## What You Will Learn
+
+* Creating a simple REST API using micronaut
+* Learn how to integrate Sureness into a micronaut application
+* Test API authentication - use JWT Auth, Basic Auth, Digest Auth to test the security of the REST API
+* Test API authorization - use different users to verify that they can access the REST API
+
+
+The tutorial assumes that you know what  JWT, Basic Auth, Digest Auth, RBAC are. If you
+do not, then you can check [jwt](https://jwt.io/introduction/), [basic auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_basic.html) , [digest auth](https://docs.oracle.com/cd/E50612_01/doc.11122/user_guide/content/authn_http_digest.html), [rbac](https://en.wikipedia.org/wiki/Role-based_access_control) for an introduction.
+
+## Setting Up Dependencies
+
+First, you will need to create a maven project and add micronautn, Sureness dependencies coordinate
+
+````
+
+    <properties>
+        <release.version>8</release.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <packaging>jar</packaging>
+        <jdk.version>1.8</jdk.version>
+        <micronaut.version>2.4.3</micronaut.version>
+        <micronaut-maven-plugin.version>1.1.8</micronaut-maven-plugin.version>
+        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
+        <exec.mainClass>com.usthe.sureness.micronaut.Application</exec.mainClass>
+        <micronaut.runtime>netty</micronaut.runtime>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>io.micronaut</groupId>
+                <artifactId>micronaut-bom</artifactId>
+                <version>${micronaut.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.30</version>
+        </dependency>
+        <dependency>
+            <groupId>com.usthe.sureness</groupId>
+            <artifactId>sureness-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-inject</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-validation</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut.test</groupId>
+            <artifactId>micronaut-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-http-client</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-http-server-netty</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.micronaut</groupId>
+            <artifactId>micronaut-runtime</artifactId>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+
+        <plugins>
+            <plugin>
+                <groupId>io.micronaut.build</groupId>
+                <artifactId>micronaut-maven-plugin</artifactId>
+                <version>${micronaut-maven-plugin.version}</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven-compiler-plugin.version}</version>
+                <configuration>
+                    <annotationProcessorPaths combine.children="append">
+                        <path>
+                            <groupId>io.micronaut</groupId>
+                            <artifactId>micronaut-inject-java</artifactId>
+                            <version>${micronaut.version}</version>
+                        </path>
+                        <path>
+                            <groupId>io.micronaut</groupId>
+                            <artifactId>micronaut-validation</artifactId>
+                            <version>${micronaut.version}</version>
+                        </path>
+                    </annotationProcessorPaths>
+                    <compilerArgs>
+                        <arg>-Amicronaut.processing.group=com.usthe.sureness</arg>
+                        <arg>-Amicronaut.processing.module=micronaut-sureness</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+
+     
+````
+
+
+- [User Guide](https://docs.micronaut.io/2.4.3/guide/index.html)
+- [API Reference](https://docs.micronaut.io/2.4.3/api/index.html)
+- [Configuration Reference](https://docs.micronaut.io/2.4.3/guide/configurationreference.html)
+- [Micronaut Guides](https://guides.micronaut.io/index.html)
+
+We need to create a simple micronautn app and provide some  REST API for test.
+
+
+
+## Setting Up Sureness
+
+#### 1.Run Micronaut Application
+
+```
+  
+import io.micronaut.runtime.Micronaut;
+
+
+public class Application{
+
+    public static void main(String[] args) {
+        Micronaut.run(Application.class, args);
+
+    }
+}
+
+```
+
+####  2. Config Document Datasource - `sureness.yml`
+
+Sureness authentication requires us to provide our own account data, role permission data. These data may come from document, databases,, annotations, etc. When we use sureness default configuration above, the datasource is document - `sureness.yml`.
+
+Create a file named `sureness.yml` in the `resource` directory. Configure account data, role permission data in the `sureness.yml`.  eg:
+
+````yaml
+## -- sureness.yml document dataSource-- ##
+
+# load api resource which need be protected, config role who can access these resource.
+# resources that are not configured are also authenticated and protected by default, but not authorized
+# eg: /api/v2/host===post===[role2,role3] means /api/v2/host===post can be access by role2,role3
+# eg: /api/v1/source2===get===[] means /api/v1/source2===get can not be access by any role
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===post===[role1]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v1/source2===get===[]
+  - /api/v2/host===post===[role2,role3]
+  - /api/v2/host===get===[role2,role3]
+  - /api/v2/host===delete===[role2,role3]
+  - /api/v2/host===put===[role2,role3]
+  - /api/v3/*===*===[role1,role2,role3]
+
+# load api resource which do not need be protected, means them need be excluded.
+# these api resource can be access by everyone
+excludedResource:
+  - /api/v3/host===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+
+# account info
+# there are three account: admin, root, tom
+# eg: admin has [role1,role2] ROLE, unencrypted password is admin, encrypted password is 0192023A7BBD73250516F069DF18B500
+# eg: root has role1, unencrypted password is 23456
+# eg: tom has role3, unencrypted password is 32113
+account:
+  - appId: admin
+    # if add salt, the password is encrypted password - the result: MD5(password+salt)
+    # digest auth not support encrypted password
+    # if no salt, the password is unencrypted password
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1,role2]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+````
+
+
+
+####  3. Add an Interceptor Intercepting All Requests
+
+The essence of sureness is to intercept all rest requests for authenticating and authorizing.     The interceptor can be a filter or interceptor, it intercepts all request to check them. In Micronaut, we use Filter
+```java
+@Filter("/**")
+public class MicronautSurenessFilterExample  implements HttpServerFilter {
+
+    private static final Logger logger = LoggerFactory.getLogger(MicronautSurenessFilterExample.class);
+
+    @Inject
+    private SurenessSecurityManager securityManager ;
+
+
+    @Override
+    public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request,
+                                                             ServerFilterChain chain) {
+        Integer statusCode = null;
+        String errorMsg = null;
+        try {
+            SubjectSum subject =securityManager.checkIn(request);
+            if (subject != null) {
+                SurenessContextHolder.bindSubject(subject);
+            }
+        } catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
+            logger.debug("this request is illegal");
+            statusCode = HttpStatus.BAD_REQUEST.getCode();
+            errorMsg = e4.getMessage();
+        } catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
+            logger.debug("the account is disabled");
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e2.getMessage();
+        } catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
+            logger.debug("this account credential is incorrect or expired");
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e3.getMessage();
+        } catch (UnauthorizedException e5) {
+            logger.debug("this account can not access this resource");
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e5.getMessage();
+        } catch (RuntimeException e) {
+            logger.error("other exception happen: ", e);
+            statusCode = HttpStatus.FORBIDDEN.getCode();
+            errorMsg = e.getMessage();
+        }
+        if (statusCode != null && errorMsg != null) {
+            String finalErrorMsg = errorMsg;
+            Integer finalStatusCode = statusCode;
+            logger.info(statusCode+"--->"+errorMsg);
+            try {
+                URI location = new URI("/auth/error");
+                request = request.mutate().headers(httpHeaders -> {
+                    httpHeaders.add("statusCode", String.valueOf(finalStatusCode));
+                    httpHeaders.add("errorMsg", finalErrorMsg);
+                }).uri(location);
+            }catch (URISyntaxException e){
+                logger.error("uri error");
+            }
+        }
+        return chain.proceed(request);
+    }
+
+
+
+    @Override
+    public int getOrder() {
+        return ServerFilterPhase.SECURITY.order();
+    }
+
+
+}
+```
+SurenessSecurityManager configuration
+
+```java
+import io.micronaut.context.annotation.Factory;
+
+@Factory
+public class SurenessConfiguration {
+    private static final Logger logger = LoggerFactory.getLogger(SurenessConfiguration.class);
+
+    @Factory
+    public SurenessSecurityManager init() {
+        SurenessAccountProvider accountProvider = new DocumentAccountProvider();
+        List<Processor> processorList = new LinkedList<>();
+        NoneProcessor noneProcessor = new NoneProcessor();
+        processorList.add(noneProcessor);
+        PasswordProcessor passwordProcessor = new PasswordProcessor();
+        passwordProcessor.setAccountProvider(accountProvider);
+        processorList.add(passwordProcessor);
+        DefaultProcessorManager processorManager = new DefaultProcessorManager(processorList);
+        if (logger.isDebugEnabled()) {
+            logger.debug("DefaultProcessorManager init");
+        }
+        PathTreeProvider pathTreeProvider = new DocumentPathTreeProvider();
+        DefaultPathRoleMatcher pathRoleMatcher = new DefaultPathRoleMatcher();
+        pathRoleMatcher.setPathTreeProvider(pathTreeProvider);
+        pathRoleMatcher.buildTree();
+        if (logger.isDebugEnabled()) {
+            logger.debug("DefaultPathRoleMatcher init");
+        }
+
+        // SubjectFactory init
+        SubjectFactory subjectFactory = new SurenessSubjectFactory();
+        List<SubjectCreate> subjectCreates = Arrays.asList(
+                new NoneSubjectReactiveCreator(),
+                new BasicSubjectReactiveCreator());
+        subjectFactory.registerSubjectCreator(subjectCreates);
+        if (logger.isDebugEnabled()) {
+            logger.debug("SurenessSubjectFactory init");
+        }
+
+        // surenessSecurityManager init
+        SurenessSecurityManager securityManager = SurenessSecurityManager.getInstance();
+        securityManager.setPathRoleMatcher(pathRoleMatcher);
+        securityManager.setSubjectFactory(subjectFactory);
+        securityManager.setProcessorManager(processorManager);
+        if (logger.isDebugEnabled()) {
+            logger.debug("SurenessSecurityManager init");
+        }
+        return securityManager;
+    }
+
+}
+
+
+```
+
+#### 4. Last, Implement Auth Exception Handling Process
+
+Sureness uses exception handling process:
+
+- If auth success, method - `checkIn()` will return a `SubjectSum` object containing user information.
+- If auth failure, method - `checkIn()` will throw different types of auth exceptions.
+
+We need to continue the subsequent process based on these exceptions.(eg: return the request response)
+
+Here we need to customize the exceptions thrown by `checkIn`, passed directly when auth success, catch exception when auth failure and do something:
+
+````
+// when auth error , add error msg to HttpRequest
+   if (statusCode != null && errorMsg != null) {
+            String finalErrorMsg = errorMsg;
+            Integer finalStatusCode = statusCode;
+            logger.info(statusCode+"--->"+errorMsg);
+            try {
+                URI location = new URI("/auth/error");
+                request = request.mutate().headers(httpHeaders -> {
+                    httpHeaders.add("statusCode", String.valueOf(finalStatusCode));
+                    httpHeaders.add("errorMsg", finalErrorMsg);
+                }).uri(location);
+            }catch (URISyntaxException e){
+                logger.error("uri error");
+            }
+        } 
+
+````
+
+
+**All done, we can test now!**
+
+## Test
+
+Through the above steps, a complete auth function project is completed. Someone maybe think that with only these few steps, where is its complete function and what can it support?   
+This built project is based on the RBAC permission model and supports Baisc authentication, Digest authentication and JWT authentication. It can fine-grained control the user's access to the restful api provided by the Javalin. That is to control which users can access which api.
+
+Let's test it. (we use postman and chrome to test.)
+
+### Test Authentication
+
+####  1. Basic Auth Test
+
+Use postman Basic auth, as shown below:
+
+* success - input username: admin, password: admin
+
+![success](/img/docs/micronaut/success.png)
+
+
+* fail - input username: admin, password: admin1234
+
+![fail](/img/docs/micronaut/error.png)
+
+
+## Conclusion
+
+micronaut is a framework dedicated to simplicity and ease of use, and so is Sureness.  
+We hope you enjoy this tutorial. Of course, the tutorial only introduces a simple introduction. Our account data, role permission data can not only be written in `sureness.yml`, but also loaded and obtained from the database and annotations. We can also customize the authentication method, data source, etc.   
+Finally, thank you again for reading.
+
+[DEMO SOURCE CODE ON GITHUB](https://github.com/usthe/sureness/tree/master/samples/javalin-sureness)

+ 16 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-quarkus.md

@@ -0,0 +1,16 @@
+---
+id: sample-quarkus  
+title: Quarkus项目集成     
+sidebar_label: Quarkus项目集成     
+---
+
+[quarkus-sureness例子项目仓库地址](https://github.com/tomsun28/sureness/tree/master/samples/quarkus-sureness)
+
+- 基于`quarkus, jax-rs`
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`sureness-config`
+- 使用默认的`JWT, Basic Auth, Digest Auth`方式认证鉴权
+- 例子中包含`REST API`
+- 保护入口: `SurenessFilterExample`
+- 推荐使用`postman`测试  
+

+ 15 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-spring-webflux.md

@@ -0,0 +1,15 @@
+---
+id: sample-spring-webflux  
+title: Spring-Webflux项目集成    
+sidebar_label: Spring-Webflux项目集成    
+---
+
+[spring-webflux-sureness例子项目仓库地址](https://github.com/tomsun28/sureness/tree/master/samples/spring-webflux-sureness)
+
+- 基于`spring-webflux`
+- 自定义 `subject creator (BasicSubjectReactiveCreator, JwtSubjectReactiveCreator, NoneSubjectReactiveCreator)` 适配 `ServerHttpRequest` 请求体
+- 从默认的配置文件`sureness.yml`加载账户信息,资源角色,过滤资源等信息
+- 使用默认的`JWT, Basic Auth`方式认证鉴权
+- 例子中包含`REST API`
+- 保护入口: `SurenessFilterExample`
+- 推荐使用`postman`测试

+ 21 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/integrate/sample-tom.md

@@ -0,0 +1,21 @@
+---
+id: sample-tom  
+title: Springboot项目集成-数据库方案    
+sidebar_label: Springboot项目集成-数据库方案    
+---
+
+[Springboot项目集成-数据库方案代码仓库地址](https://github.com/tomsun28/sureness/tree/master/sample-tom)
+
+- 基于`springboot,jpa...`
+- 自定义数据源,使用从数据库加载账户信息,资源角色,过滤资源等信息,这样便于动态调整(见`AccountProvider ResourceProvider`)
+- 除了使用了默认的`JWT, Basic Auth`方式认证鉴权,新增自定义认证鉴权(自定义`subject subjectCreator processor...`)
+- 推荐使用`postman`测试,测试样例为`sample-tom-postman.json`,导入`postman`即可
+
+样例中包含2种自定义认证鉴权方式:
+
+1. 自定义了一个单独的`subjectCreator` 见 `CustomPasswdSubjectCreator`     
+   演示功能就是自定义的从不同地方获取请求体的账户密码,来创建默认的`PasswordSubject`,走默认的账户密码认证流程
+
+2. 自定义了一整套流程(包含`subject subjectCreator processor`) 见 `CustomTokenSubject CustomTokenSubjectCreator CustomTokenProcessor`  
+   演示功能就是自定义一个简单的`token`作为`subject`对象,对其自定义创建获取方式-`creator`和自定义认证鉴权处理流程-`processor`.  
+   此自定义流程也演示了一个简单的`token`刷新流程  

+ 74 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/introduce.md

@@ -0,0 +1,74 @@
+---
+id: introduce  
+title: Sureness 介绍    
+sidebar_label: 介绍
+slug: /
+---
+
+> 面向`REST API`的高性能认证鉴权框架
+
+[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
+[![Maven](https://img.shields.io/badge/Maven%20Central-1.0.6-blue.svg)](https://search.maven.org/artifact/com.usthe.sureness/sureness-core)
+![GitHub pull request check contexts](https://img.shields.io/github/status/contexts/pulls/dromara/sureness/8?label=pull%20checks)
+[![Gitter](https://img.shields.io/gitter/room/usthe/sureness?label=sureness&color=orange&logo=gitter&logoColor=red)](https://gitter.im/usthe/sureness)
+![GitHub Release Date](https://img.shields.io/github/release-date/dromara/sureness?color=blue&logo=figshare&logoColor=red)
+[![star](https://gitee.com/dromara/sureness/badge/star.svg?theme=gray)](https://gitee.com/dromara/sureness/stargazers)
+[![star](https://img.shields.io/github/stars/dromara/sureness?style=social)](https://github.com/dromara/sureness)
+
+
+## 📫 背景
+
+在主流的前后端分离架构中,如何通过有效快速的认证鉴权来保护后端提供的`REST API`变得尤为重要。对现存框架,不原生支持`RESTful`的`Apache Shiro`,
+还是深度绑定`Spring`的`Spring Security`,或多或少都不是我们的理想型。   
+于是乎`Sureness`诞生了,我们希望能解决这些,提供一个面向**REST API**,**无框架依赖**,可以**动态修改权限**,**多认证策略**,**更快速度**,**易用易扩展**的认证鉴权框架。
+
+## 🎡 <font color="green">介绍</font>
+
+> [Sureness](https://github.com/dromara/sureness) 是我们在深度使用 `Apache Shiro` 之后,吸取其优点全新设计开发的一个认证鉴权框架
+> 面向 `REST API` 的认证鉴权,基于 `RBAC` (用户-角色-资源)主要关注于对 `API` 的安全保护     
+> 无特定Web框架依赖(已有 `Spring Boot,Quarkus,Javalin,Ktor,Micronaut,Jfinal,Solon` 等集成样例)     
+> 支持动态修改权限配置(动态修改配置每个 `API` 谁有权访问)   
+> 支持 `Websocket` ,主流 `HTTP` 容器 `Servlet` 和 `JAX-RS`       
+> 支持多种认证策略, `JWT, Basic Auth, Digest Auth` ... 可扩展自定义认证方式      
+> 基于改进的字典匹配树拥有的高性能      
+> 良好的扩展接口, 样例和文档助急速理解扩展使用
+
+> `Sureness`的低配置,易扩展,不耦合其他框架,希望能对系统多场景快速安全的保护
+
+##### 🔍 对比
+
+| ~         | Sureness | Shiro | Spring Security |
+| ---       | ---      | ---   | ---  |
+| **多框架支持**  | 支持      | 需改动支持   | 不支持 |
+| **REST API** | 支持 | 需改动支持   | 支持 |
+| **Websocket** | 支持 | 不支持   | 不支持 |
+| **过滤链匹配**  | 优化的字典匹配树 | ant匹配 | ant匹配 |
+| **注解支持**    | 支持      | 支持      | 支持 |
+| **Servlet**    | 支持      | 支持      | 支持|
+| **JAX-RS**     | 支持      | 不支持    | 不支持|
+| **权限动态修改** | 支持 | 需改动支持 | 需改动支持|
+| **性能速度** | 较快 | 较慢 | 较慢|
+| **学习曲线** | 简单 | 简单 | 陡峭|  
+
+##### 📈 基准性能测试
+
+![benchmark](/img/docs/benchmark_cn.png)
+
+**基准测试显示Sureness对比无权限框架应用损耗0.026ms性能,Shiro损耗0.088ms,Spring Security损耗0.116ms,
+相比之下Sureness性能(参考TPS损耗)是Shiro的3倍,Spring Security的4倍**     
+**性能差距会随着api匹配链的增加而进一步拉大**     
+详见[基准测试](https://github.com/tomsun28/sureness-shiro-spring-security)
+
+
+##### ✌ 框架支持样例
+
+- [x] Sureness集成**Spring Boot**样例(配置文件方案) [sample-bootstrap](/docs/integrate/sample-bootstrap)
+- [x] Sureness集成**Spring Boot**样例(数据库方案) [sample-tom](/docs/integrate/sample-tom)
+- [x] Sureness集成**Quarkus**样例 [sample-quarkus](/docs/integrate/sample-quarkus)
+- [x] Sureness集成**Javalin**样例 [sample-javalin](/docs/integrate/sample-javalin)
+- [x] Sureness集成**Ktor**样例 [sample-ktor](/docs/integrate/sample-ktor)
+- [x] Sureness集成**Spring Webflux**样例 [spring-webflux-sureness](/docs/integrate/sample-spring-webflux)
+- [x] Sureness集成**Micronaut**样例 [sample-micronaut](/docs/integrate/sample-micronaut)
+- [x] Sureness使用Session样例 [sureness-session](https://github.com/usthe/sureness/tree/master/samples/sureness-session)
+- [x] Sureness分布式缓存Session样例 [sureness-redis-session](https://github.com/usthe/sureness/tree/master/samples/sureness-redis-session)
+- [x] More samples todo   

+ 21 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/sponsor.md

@@ -0,0 +1,21 @@
+---
+id: sponsor  
+title: 赞助    
+sidebar_label: 赞助     
+---
+
+
+**sureness对个人或企业完全免费,如果您喜欢这个项目并且愿意提供帮助,请作者喝杯咖啡吧**      
+
+### 微信支付宝  
+
+![wechat-alipay](/img/docs/pay.png)      
+
+
+### paypal
+
+[https://paypal.me/tomsun28](https://paypal.me/tomsun28)    
+
+
+
+

+ 48 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/start/annotation-datasource.md

@@ -0,0 +1,48 @@
+---
+id: annotation-datasource  
+title: 注解形式的资源权限数据源    
+sidebar_label: 注解权限数据源    
+---
+
+`sureness`认证鉴权,当然也需要我们提供自己的账户数据,角色权限数据等,这些数据可能来自文本,关系数据库,非关系数据库,注解等。  
+我们提供了数据源接口:`SurenessAccountProvider` - 账户数据接口, `PathTreeProvider` - 资源权限数据接口,用户可以实现此接口实现自定义数据源。
+
+`sureness`实现注解权限的方式不是调用方法前aop判断,而是启动时扫描注解里的数据作为权限数据源来使用,这样方便了流程统一和框架无关性。  
+这里介绍下注解形式的权限数据源配置方法。
+
+1. 首先我们需要在sureness启动配置中配置使用注解数据源作为权限数据源。
+
+```
+@Bean
+TreePathRoleMatcher pathRoleMatcher() {
+    // 实例化资源权限路径匹配者,其会根据请求的路径和已有的资源权限数据匹配出所需的角色信息
+    DefaultPathRoleMatcher pathRoleMatcher = new DefaultPathRoleMatcher();
+    // 实例化注解形式的资源权限数据加载者AnnotationLoader,其实现了PathTreeProvider接口
+    AnnotationPathTreeProvider pathTreeProvider = new AnnotationPathTreeProvider();
+    // 设置AnnotationLoader要扫描的包路径,其会扫描包路径下所有类方法上的@RequiresRoles, @WithoutAuth 注解获取数据
+    pathTreeProvider.setScanPackages(Arrays.asList("com.usthe.sureness.sample.tom.controller"));
+    // 将AnnotationLoader数据源设置为sureness的权限资源数据源
+    pathRoleMatcher.addPathTreeProvider(pathTreeProvider);
+    pathRoleMatcher.buildTree();
+    return pathRoleMatcher;
+}
+```
+
+2. 在提供的接口方法中使用注解,注解使用格式:
+```
+@RequiresRoles(roles = {"role1", "role2"}, mapping = "/resource", method = "post")  
+其表示资源 /resource===post 的需要角色 role1或者role2才能访问  
+```
+```
+@WithoutAuth(mapping = "/resource/*", method = "put")  
+其表示资源 /resource/*===put 的可以被任何请求访问  
+```
+
+3. 建议。  
+   注解形式的权限数据源虽然比较方便我们开发,但其写死在代码中,无法动态修改权限角色配置数据,对于大型项目反而不是很适用。  
+   `sureness`提供了多个数据源同时加载的功能,即我们可以同时将注解形式的权限数据源和数据库里的配置数据作为数据源,加载到sureness配置中,
+   对于不常修改的权限配置,我们可以将其配置到注解,对于其他需要动态修改的权限数据,我们就将其配置到数据库中。
+
+
+当然也我们提供了默认文本数据源,默认文本数据源具体实现,请参考 [默认文本数据源](/docs/start/default-datasource)       
+数据源也可以来自数据库等存储,我们提供了接口让用户轻松的自定义数据源,详见[自定义数据源](/docs/advanced/custom-datasource)  

+ 51 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/start/default-auth.md

@@ -0,0 +1,51 @@
+---
+id: default-auth  
+title: 默认支持的认证方式         
+sidebar_label: 默认认证方式    
+---
+
+`sureness`目前默认支持的认证方式有`bearer jwt`,`basic auth`, `digest auth`, 当然用户可以通过扩展`Processor`,`Subject`和`SubjectCreate`接口实现自定义的认证方式
+
+#### `bearer jwt`
+`jwt`即`json web token`,是目前很流行的跨域,无状态,安全认证解决方案,介绍详见[网络](http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)  
+我们这里为啥叫`bearer jwt`是因为`jwt`是放入到http请求头的`bearer token`里面,即:  `Authorization: Bearer jsonWebTokenValue`  
+eg:
+```
+GET /api/v1/source1 HTTP/1.1
+Host: localhost:8088
+Content-Type: application/json
+Authorization: Bearer eyJhbGciOiJIUzUxMiIsInppcCI6IkRFRiJ9.eNocjEEOwiAQRe8y65IwCBQ4hlvjotAhVqs1DBoT492l7F5e_vtfuNYFAliUPs3aCrIuCW1nFDHlUaBVqJOLJpkIA_ArtnHd7o0X5s43egim8qayy6lCQOOUd15JHIA-zy4OUo5dlG2lFp46KDjvR0fKhfgCIU8r0-8PAAD__w.f-3klWWDpEO3uDLlx2S53DV2cYernwVEDwcC6z1JexocbZoxRKmASTOuky1qMCxy_hV8-RbuMjDmI3ASa_FQOw
+```  
+
+我们可以在`postman`如下使用它: 将`jwt`值塞入`Bearer Token`里.  
+![jwtPostmanUse](/img/docs/jwtPostmanUse.png)
+
+#### `basic auth`
+`basic auth`即`Basic access authentication`,经典的`http`基本认证方式,介绍详见[网络](https://www.jianshu.com/p/4cd42f7359f4)    
+这种认证方式是将账户密码组成的字符串`base64`加密,放入到请求头的 `Authorization`中, 即:`Authorization: Basic base64encode(username+":"+password)`  
+eg:
+```
+GET /api/v1/source1 HTTP/1.1
+Host: localhost:8088
+Content-Type: application/json
+Authorization: Basic dG9tOjMyMTEz
+```  
+
+我们可以在`postman`如下使用它: 在`Basic Auth`类型的`Authorization`中输入账户密码即可,`postman`会自动对其`base64`加密.  
+![basicAuthPostmanUse](/img/docs/basicAuthPostmanUse.png)
+
+#### `digest auth`
+`digest auth`即`Digest access authentication`,经典的`http`摘要认证方式,用于保护传输的密码,介绍详见[网络](https://www.cnblogs.com/xiaoxiaotank/p/11078571.html)       
+下面是`digest auth`的认证流程(图片来源于[网络](https://www.cnblogs.com/xiaoxiaotank/p/11078571.html)):  
+![digestFlow](/img/docs/digestFlow.png)
+
+我们可以在`chrome`浏览器直接使用它: 访问`url`,在弹出的对话框中输入账户密码即可,`chrome`浏览器会自动进行认证流程.    
+![digestAuthChromeUse](/img/docs/digestAuthUse.png)
+
+
+#### 其他认证方式
+目前`sureness`默认支持这三种主流的认证方式,满足绝大部分需求,当然你也可以很轻松的自定义认证方式,详见[自定义Subject](/docs/advanced/custom-subject)  
+
+我们提供了默认认证方式的使用`DEMO`,请参考 [一步一步搭建认证鉴权系统](/docs/integrate/sample-bootstrap)       
+当然我们也提供了自定义认证方式的扩展`DEMO`,请参考 [Springboot项目集成-数据库方案](/docs/integrate/sample-tom)      
+

+ 64 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/start/default-datasource.md

@@ -0,0 +1,64 @@
+---
+id: default-datasource  
+title: 默认文本配置数据源    
+sidebar_label: 默认文本数据源    
+---
+
+`sureness`认证鉴权当然也需要我们自己的配置数据:账户数据,角色权限数据等  
+这些配置数据可能来自文本,关系数据库,非关系数据库  
+我们提供了配置数据接口`SurenessAccountProvider`, `PathTreeProvider`, 用户可以实现此接口实现自定义配置数据源
+
+当然我们也提供默认文本形式的配置数据实现 `DocumentResourceDefaultProvider`  
+用户可以创建文件`sureness.yml`来配置数据,配置样例如下:
+
+```
+## -- sureness.yml文本数据源 -- ##
+
+# 加载到匹配字典的资源,也就是需要被保护的,设置了所支持角色访问的资源
+# 没有配置的资源也默认被认证保护,但不鉴权
+# eg: /api/v1/source1===get===[role2] 表示 /api/v2/host===post 这条资源支持 role2 这一种角色访问
+# eg: /api/v1/source2===get===[] 表示 /api/v1/source2===get 这条资源不支持任何角色访问
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v1/source2===get===[]
+  - /api/v1/source2/*/*===get===[role2]
+  - /api/v2/source3/*===get===[role2]
+  - /api/v3/source===*===[role2]
+
+# 需要被过滤保护的资源,不认证鉴权直接访问
+# /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权
+excludedResource:
+  - /api/v1/account/auth===post
+  - /api/v1/source3===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+  - /**/*.png===*
+  
+# 用户账户信息
+# 下面有 admin root tom三个账户
+# eg: admin 拥有[role1,role2]角色,明文密码为admin,加盐密码为0192023A7BBD73250516F069DF18B500
+# eg: root 拥有[role1],密码为明文23456
+# eg: tom 拥有[role3],密码为明文32113
+account:
+  - appId: admin
+    # 如果填写了加密盐--salt,则credential为MD5(password+salt)的32位结果
+    # 没有盐认为不加密,credential为明文
+    # 若对密码加盐 则不支持digest认证
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+```
+
+我们提供了默认文本数据源使用`DEMO`,默认文本数据源具体实现,请参考 [一步一步搭建认证鉴权系统](/docs/integrate/sample-bootstrap)     
+当然数据源也可以来自数据库等存储,我们提供了接口让用户轻松的自定义数据源,详见[自定义数据源](/docs/advanced/custom-datasource)  

+ 45 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/start/default-exception.md

@@ -0,0 +1,45 @@
+---
+id: default-exception  
+title: sureness 默认认证鉴权异常        
+sidebar_label: 默认异常类型      
+---
+
+`sureness`使用异常处理流程:
+1. 若认证鉴权成功,`checkIn`会返回包含用户信息的`SubjectSum`对象
+2. 若中间认证鉴权失败,`checkIn`会抛出不同类型的认证鉴权异常,用户需根据这些异常来继续后面的流程(返回相应的请求响应)
+
+这里我们就需要对`checkIn`抛出的异常做自定义处理,认证鉴权成功直接通过,失败抛出特定异常进行处理,如下:
+
+```
+        try {
+            SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest);
+        } catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
+            // 账户创建相关异常 
+        } catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
+            // 账户禁用相关异常
+        } catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
+            // 认证失败相关异常
+        } catch (UnauthorizedException e5) {
+            // 鉴权失败相关异常
+        } catch (RuntimeException e) {
+            // 其他自定义异常
+        }
+```
+
+sureness 默认支持的异常处理流程中的异常如下:
+
+`sureness`异常                             | 异常描述
+---                                       | ---
+SurenessAuthenticationException           | 基础认证异常,认证相关的子异常应该继承此异常
+SurenessAuthorizationException            | 基础鉴权异常,鉴权相关的子异常应该继承此异常
+ProcessorNotFoundException                | 认证异常,未找到支持此subject的processor
+UnknownAccountException                   | 认证异常,不存在此账户
+UnSupportedSubjectException               | 认证异常,不支持的请求,未创建出subject
+DisabledAccountException                  | 认证异常,账户禁用
+ExcessiveAttemptsException                | 认证异常,账户尝试认证次数过多
+IncrrectCredentialsException              | 认证异常,密钥错误
+ExpiredCredentialsException               | 认证异常,密钥认证过期
+NeedDigestInfoException                   | 认证异常, 通过getAuthenticate()返回客户端digest认证所需信息
+UnauthorizedException                     | 鉴权异常,没有权限访问此资源
+
+自定义异常需要继承`SurenessAuthenticationException`或`SurenessAuthorizationException`才能被最外层捕获  

+ 27 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/start/path-match.md

@@ -0,0 +1,27 @@
+---
+id: path-match  
+title: URI路径匹配        
+sidebar_label: URI路径匹配    
+---
+
+我们配置的资源格式为:`requestUri===httpMethod`, 即请求的路径加上其请求方式(`post,get,put,delete...或者*,*匹配所有请求方式`)作为一个整体被视作一个资源   
+`eg: /api/v2/book===get` `get`方式请求`/api/v2/book`接口数据  
+这里的`requestUri`支持url路径匹配符匹配: `str*str`, `*`, `**`
+
+| 通配符                      | 描述 |
+| ---                        | --- |
+| `str*str`                  | 字符串中的*匹配0个或者多个任意字符 |
+| `*`                        | 匹配0个或1个目录                |
+| `**`                       | 匹配0个或多个目录               |
+
+
+| 样例                    | 说明  |
+| ---                    | ---  |
+| `*.html`               | 可以匹配 `content.html`, `user-ui.html` 等 |
+| `/api/*/book`          | 可以匹配 `/api/user/book` 或 `/api/book` 等 |
+| `/**`                  | 可以匹配任何路径                             |
+| `/**/foo`              | 可以匹配 `/api/user/book/foo` 等            |
+
+匹配优先级: 原始字符串 > `str*str` > `*` > `**`  
+最长路径匹配原则:  
+eg: `requestUri` 为`/app/book/foo`,若存在两个路径匹配模式`/app/**`和`/app/book/*`,则会匹配到`/app/book/*`  

+ 93 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/start/quickstart.md

@@ -0,0 +1,93 @@
+---
+id: quickstart  
+title: 快速开始    
+sidebar_label: 快速开始    
+---
+
+#### 🐕 使用前一些约定    
+
+- `Sureness`基于`RBAC`,即用户-角色-资源: 用户所属角色--角色拥有资源(API)--用户就能访问资源(API)
+- 我们将`REST API`请求视作一个资源,资源格式为: `requestUri===httpMethod`  
+  即请求的路径加上其请求方式(`post,get,put,delete...`)作为一个整体被视作资源来赋权配置  
+  `eg: /api/v2/book===get` `get`方式请求`/api/v2/book`接口数据
+
+资源路径匹配详见 [url路径匹配](/docs/start/path-match)   
+
+#### 项目中加入Sureness
+
+项目使用`maven`或`gradle`构建,加入坐标
+```
+<dependency>
+    <groupId>com.usthe.sureness</groupId>
+    <artifactId>sureness-core</artifactId>
+    <version>1.0.6</version>
+</dependency>
+```
+```
+compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.6'
+```
+
+#### 🐵 使用默认配置来配置Sureness
+默认配置使用了文件数据源`sureness.yml`作为账户权限数据源  
+默认配置支持了`JWT, Basic auth, Digest auth`认证
+```
+@Bean
+public DefaultSurenessConfig surenessConfig() {
+    return new DefaultSurenessConfig();
+}
+```
+
+#### 配置权限账户数据源
+
+`Sureness`认证鉴权,当然也需要我们提供自己的账户数据,角色权限数据等,这些数据可能来自文本,关系数据库,非关系数据库,注解等。  
+我们提供了数据源接口:`SurenessAccountProvider`, `PathTreeProvider`,用户可以实现此接口实现自定义数据源。
+
+当前我们也提供文本形式的数据源实现 `DocumentResourceDefaultProvider` 和 注解形式的资源权限数据源实现 `AnnotationLoader`。  
+如果是使用了[默认sureness配置-DefaultSurenessConfig](#使用默认配置来配置sureness),其配置的是文本数据源,用户可以直接通过修改`sureness.yml`文件来配置数据。
+
+文本数据源`sureness.yml`配置使用方式详见文档 [默认文本数据源](/docs/start/default-datasource)     
+注解形式的资源权限数据源配置使用方式详见文档 [注解资源权限数据源](/docs/start/annotation-datasource)   
+
+我们提供了使用代码`DEMO`:  
+默认文本数据源具体实现,请参考[Sureness集成Spring Boot样例(配置文件方案)--sample-bootstrap](https://github.com/tomsun28/sureness/tree/master/sample-bootstrap)   
+若权限配置数据来自数据库,请参考[Sureness集成Spring Boot样例(数据库方案)--sample-tom](https://github.com/tomsun28/sureness/tree/master/sample-tom)
+
+#### 添加过滤器拦截所有请求
+
+`Sureness`的本质就拦截所有`API`请求对其认证鉴权判断。  
+入口拦截器器实现一般可以是 `filter or spring interceptor`  
+在拦截器中加入`Sureness`的安全过滤器,如下:
+
+```
+SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest)
+```
+
+#### 实现认证鉴权相关异常处理流程
+
+`Sureness`使用异常处理流程:
+1. 若认证鉴权成功,`checkIn`会返回包含用户信息的`SubjectSum`对象
+2. 若中间认证鉴权失败,`checkIn`会抛出不同类型的认证鉴权异常,用户需根据这些异常来继续后面的流程(返回相应的请求响应)
+
+这里我们就需要对`checkIn`抛出的异常做自定义处理,认证鉴权成功直接通过,失败抛出特定异常进行处理,如下:
+
+```
+try {
+    SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest);
+} catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
+    // 账户创建相关异常 
+} catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
+    // 账户禁用相关异常
+} catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
+    // 认证失败相关异常
+} catch (UnauthorizedException e5) {
+    // 鉴权失败相关异常
+} catch (SurenessAuthenticationException | SurenessAuthorizationException e) {
+    // 其他自定义异常
+}
+```
+
+异常详见 [默认异常类型](/docs/start/default-exception)
+
+**HAVE FUN**
+
+> 如果这个[快速开始]对您不是很友好,可以参考下面一篇[一步一步搭建](https://juejin.cn/post/6921262609731682318),里面一步一步详细介绍了使用Sureness搭建一个完整功能认证鉴权项目的步骤。    

+ 432 - 0
home/i18n/en/docusaurus-plugin-content-docs/current/start/step-by-step.md

@@ -0,0 +1,432 @@
+---
+id: quickstart  
+title: 一步一步搭建    
+sidebar_label: 一步一步搭建  
+---
+
+下面我们来一步一步基于springboot,sureness搭建一个如下功能的认证鉴权系统。  
+
+1. 使用了配置文件来作为系统的账户数据和权限数据的数据源。   
+2. 系统基于rbac权限模型,支持basic认证,digest认证,jwt认证。  
+3. 能细粒度的控制用户对系统提供的restful api的访问权限,即哪些用户能访问哪些api。  
+
+多说无益,快速开始!  
+
+这里为了照顾到刚入门的同学,图文展示了每一步操作。有基础可直接略过。  
+
+### 初始化一个springboot web工程  
+
+在IDEA如下操作:  
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c2bc0a723ea74c86a75952cc486367cb~tplv-k3u1fbpfcp-zoom-1.image)
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/00d3cd3a015b4a079c0b30da064139d0~tplv-k3u1fbpfcp-zoom-1.image)
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3be963ed9c28493d9ddc3b48caab54af~tplv-k3u1fbpfcp-zoom-1.image)
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2bcce3d56cb244b6967914a5b9b807c5~tplv-k3u1fbpfcp-zoom-1.image)
+ 
+
+### 提供一些模拟的restful api  
+
+新建一个controller, 在里面实现一些简单的restful api供外部测试调用  
+
+````
+/**
+ * simulate api controller, for testing
+ * @author tomsun28
+ * @date 17:35 2019-05-12
+ */
+@RestController
+public class SimulateController {
+
+    /** access success message **/
+    public static final String SUCCESS_ACCESS_RESOURCE = "access this resource success";
+
+    @GetMapping("/api/v1/source1")
+    public ResponseEntity<String> api1Mock1() {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+    @PutMapping("/api/v1/source1")
+    public ResponseEntity<String> api1Mock3() {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+    @DeleteMapping("/api/v1/source1")
+    public ResponseEntity<String> api1Mock4() {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+    @GetMapping("/api/v1/source2")
+    public ResponseEntity<String> api1Mock5() {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+    @GetMapping("/api/v1/source2/{var1}/{var2}")
+    public ResponseEntity<String> api1Mock6(@PathVariable String var1, @PathVariable Integer var2 ) {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+    @PostMapping("/api/v2/source3/{var1}")
+    public ResponseEntity<String> api1Mock7(@PathVariable String var1) {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+    @GetMapping("/api/v1/source3")
+    public ResponseEntity<String> api1Mock11(HttpServletRequest request) {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+    @GetMapping("/api/v2/source2")
+    public ResponseEntity<String> api1Mock12() {
+        return ResponseEntity.ok(SUCCESS_ACCESS_RESOURCE);
+    }
+
+}
+````
+
+### 项目中加入sureness依赖  
+
+在项目的pom.xml加入sureness的maven依赖坐标    
+```
+<dependency>
+    <groupId>com.usthe.sureness</groupId>
+    <artifactId>sureness-core</artifactId>
+    <version>1.0.3</version>
+</dependency>
+```
+如下:  
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1c0df60e4bae4d4b80c0209f461fc8b5~tplv-k3u1fbpfcp-zoom-1.image)
+
+
+### 使用默认配置来配置sureness    
+
+新建一个配置类,创建对应的sureness默认配置bean  
+sureness默认配置使用了文件数据源`sureness.yml`作为账户权限数据源  
+默认配置支持了`jwt, basic auth, digest auth`认证  
+```
+@Configuration
+public class SurenessConfiguration {
+
+    /**
+     * sureness default config bean
+     * @return default config bean
+     */
+    @Bean
+    public DefaultSurenessConfig surenessConfig() {
+        return new DefaultSurenessConfig();
+    }
+
+}
+```
+
+### 配置默认文本配置数据源   
+
+认证鉴权当然也需要我们自己的配置数据:账户数据,角色权限数据等  
+这些配置数据可能来自文本,关系数据库,非关系数据库  
+我们这里使用默认的文本形式配置 - sureness.yml, 在resource资源目录下创建sureness.yml文件  
+在sureness.yml文件里配置我们的角色权限数据和账户数据,如下:  
+
+````
+## -- sureness.yml文本数据源 -- ##
+
+# 加载到匹配字典的资源,也就是需要被保护的,设置了所支持角色访问的资源
+# 没有配置的资源也默认被认证保护,但不鉴权,例如/api/v1/source2===get
+# eg: /api/v1/source1===get===[role2] 表示 /api/v1/source1===get 这条资源支持 role2这一种角色访问
+# eg: /api/v2/source2===get===[] 表示 /api/v1/source2===get 这条资源不支持任何角色访问
+resourceRole:
+  - /api/v1/source1===get===[role2]
+  - /api/v1/source1===delete===[role3]
+  - /api/v1/source1===put===[role1,role2]
+  - /api/v2/source2===get===[]
+  - /api/v1/source2/*/*===get===[role2]
+  - /api/v2/source3/*===get===[role2]
+
+# 需要被过滤保护的资源,不认证鉴权直接访问
+# /api/v1/source3===get 表示 /api/v1/source3===get 可以被任何人访问 无需登录认证鉴权
+excludedResource:
+  - /api/v1/account/auth===post
+  - /api/v1/source3===get
+  - /**/*.html===get
+  - /**/*.js===get
+  - /**/*.css===get
+  - /**/*.ico===get
+
+# 用户账户信息
+# 下面有 admin root tom三个账户
+# eg: admin 拥有[role1,role2]角色,明文密码为admin,加盐密码为0192023A7BBD73250516F069DF18B500
+# eg: root 拥有[role1],密码为明文23456
+# eg: tom 拥有[role3],密码为明文32113
+account:
+  - appId: admin
+    # 如果填写了加密盐--salt,则credential为MD5(password+salt)的32位结果
+    # 没有盐认为不加密,credential为明文
+    # 若密码加盐 则digest认证不支持  
+    credential: 0192023A7BBD73250516F069DF18B500
+    salt: 123
+    role: [role1,role2]
+  - appId: root
+    credential: 23456
+    role: [role1]
+  - appId: tom
+    credential: 32113
+    role: [role3]
+
+````
+
+### 添加过滤器拦截所有请求,对所有请求进行认证鉴权      
+
+新建一个filter, 拦截所有请求,用sureness对所有请求进行认证鉴权。认证鉴权失败的请求sureness会抛出对应的异常,我们捕获响应的异常进行处理返回response即可。  
+
+````
+@Order(1)
+@WebFilter(filterName = "SurenessFilterExample", urlPatterns = "/*", asyncSupported = true)
+public class SurenessFilterExample implements Filter {
+
+    @Override
+    public void init(FilterConfig filterConfig) {}
+
+    @Override
+    public void destroy() {}
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+            throws IOException, ServletException {
+
+        try {
+            SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest);
+            // 认证鉴权成功则会返回带用户信息的subject 可以将subject信息绑定到当前线程上下文holder供后面使用
+            if (subject != null) {
+                SurenessContextHolder.bindSubject(subject);
+            }
+        } catch (ProcessorNotFoundException | UnknownAccountException | UnsupportedSubjectException e4) {
+            // 账户创建相关异常
+            responseWrite(ResponseEntity
+                    .status(HttpStatus.BAD_REQUEST).body(e4.getMessage()), servletResponse);
+            return;
+        } catch (DisabledAccountException | ExcessiveAttemptsException e2 ) {
+            // 账户禁用相关异常
+            responseWrite(ResponseEntity
+                    .status(HttpStatus.UNAUTHORIZED).body(e2.getMessage()), servletResponse);
+            return;
+        } catch (IncorrectCredentialsException | ExpiredCredentialsException e3) {
+            // 认证失败相关异常
+            responseWrite(ResponseEntity
+                    .status(HttpStatus.UNAUTHORIZED).body(e3.getMessage()), servletResponse);
+            return;
+        } catch (NeedDigestInfoException e5) {
+            // digest认证需要重试异常
+            responseWrite(ResponseEntity
+                    .status(HttpStatus.UNAUTHORIZED)
+                    .header("WWW-Authenticate", e5.getAuthenticate()).build(), servletResponse);
+            return;
+        } catch (UnauthorizedException e6) {
+            // 鉴权失败相关异常,即无权访问此api
+            responseWrite(ResponseEntity
+                    .status(HttpStatus.FORBIDDEN).body(e6.getMessage()), servletResponse);
+            return;
+        } catch (RuntimeException e) {
+            // 其他异常
+            responseWrite(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(),
+                    servletResponse);
+            return;
+        }
+        try {
+            // 若未抛出异常 则认证鉴权成功 继续下面请求流程
+            filterChain.doFilter(servletRequest, servletResponse);
+        } finally {
+            SurenessContextHolder.clear();
+        }
+    }
+
+    /**
+     * write response json data
+     * @param content content
+     * @param response response
+     */
+    private static void responseWrite(ResponseEntity content, ServletResponse response) {
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json;charset=utf-8");
+        ((HttpServletResponse)response).setStatus(content.getStatusCodeValue());
+        content.getHeaders().forEach((key, value) ->
+                ((HttpServletResponse) response).addHeader(key, value.get(0)));
+        try (PrintWriter printWriter = response.getWriter()) {
+            if (content.getBody() != null) {
+                if (content.getBody() instanceof String) {
+                    printWriter.write(content.getBody().toString());
+                } else {
+                    ObjectMapper objectMapper = new ObjectMapper();
+                    printWriter.write(objectMapper.writeValueAsString(content.getBody()));
+                }
+            } else {
+                printWriter.flush();
+            }
+        } catch (IOException e) {}
+    }
+}
+
+````
+
+像上面一样,
+1. 若认证鉴权成功,`checkIn`会返回包含用户信息的`SubjectSum`对象  
+2. 若中间认证鉴权失败,`checkIn`会抛出不同类型的认证鉴权异常,用户需根据这些异常来继续后面的流程(返回相应的请求响应)
+
+为了使filter在springboot生效 需要在boot启动类加注解 `@ServletComponentScan`   
+
+````
+@SpringBootApplication
+@ServletComponentScan
+public class BootstrapApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(BootstrapApplication.class, args);
+    }
+}
+````
+
+### 一切完毕,验证测试  
+
+通过上面的步骤 我们的一个完整功能认证鉴权项目就搭建完成了,有同学想 就这几步骤 它的完整功能体现在哪里啊 能支持啥。  
+
+这个搭好的认证鉴权项目基于rbac权限模型,支持 baisc 认证,digest认证, jwt认证。能细粒度的控制用户对后台提供的restful api的访问权限,即哪些用户能访问哪些api。 我们这里来测试一下。 
+
+IDEA上启动工程项目。  
+
+##### basic认证测试  
+
+资源api/v1/source2===get没有配置到文本数据源里,代表所有角色或无角色都可以访问 前提是认证成功,用该资源来做认证测试
+
+**认证成功**:  
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a09f6caa4ef845adb9f5b5fa6e86040b~tplv-k3u1fbpfcp-zoom-1.image)
+
+
+**密码错误**:  
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/caa656a9a9174df6afc8768dc859ecb6~tplv-k3u1fbpfcp-zoom-1.image)
+ 
+
+**账户不存在**:  
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/37d8c0d0cd144b4fb8cfd5e0a47e6961~tplv-k3u1fbpfcp-zoom-1.image)
+ 
+
+##### digest认证测试  
+
+**注意如果密码配置了加密盐,则无法使用digest认证**  
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53fe5e08301c4171b31b6180a77b5837~tplv-k3u1fbpfcp-zoom-1.image)
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c12598db019549efa5f990c0136e7582~tplv-k3u1fbpfcp-zoom-1.image)
+
+
+
+##### jwt认证测试  
+
+jwt认证首先你得拥有一个签发的jwt,创建如下api接口提供jwt签发- `/api/v1/account/auth`  
+````
+@RestController()
+public class AccountController {
+
+    private static final String APP_ID = "appId";
+    /**
+     * account data provider
+     */
+    private SurenessAccountProvider accountProvider = new DocumentAccountProvider();
+
+    /**
+     * login, this provider a get jwt api, convenient to test other api with jwt
+     * @param requestBody request
+     * @return response
+     *
+     */
+    @PostMapping("/api/v1/account/auth")
+    public ResponseEntity<Object> login(@RequestBody Map<String,String> requestBody) {
+        if (requestBody == null || !requestBody.containsKey(APP_ID)
+                || !requestBody.containsKey("password")) {
+            return ResponseEntity.badRequest().build();
+        }
+        String appId = requestBody.get("appId");
+        String password = requestBody.get("password");
+        SurenessAccount account = accountProvider.loadAccount(appId);
+        if (account == null || account.isDisabledAccount() || account.isExcessiveAttempts()) {
+            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+        }
+        if (account.getPassword() != null) {
+            if (account.getSalt() != null) {
+                password = Md5Util.md5(password + account.getSalt());
+            }
+            if (!account.getPassword().equals(password)) {
+                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+            }
+        }
+        // Get the roles the user has - rbac
+        List<String> roles = account.getOwnRoles();
+        long refreshPeriodTime = 36000L;
+        // issue jwt
+        String jwt = JsonWebTokenUtil.issueJwt(UUID.randomUUID().toString(), appId,
+                "token-server", refreshPeriodTime >> 1, roles,
+                null, Boolean.FALSE);
+        Map<String, String> body = Collections.singletonMap("token", jwt);
+        return ResponseEntity.ok().body(body);
+    }
+
+
+}
+````
+
+**请求api接口登录认证获取jwt**    
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3295df4eb54d4deb8498d3ae51aadcb8~tplv-k3u1fbpfcp-zoom-1.image)
+
+
+**携带使用获取的jwt值请求api接口**    
+
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec0f95fe506946df8b14c8fab5ffd9f2~tplv-k3u1fbpfcp-zoom-1.image)
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fb6b8c67ec474e229ba37e62654651d5~tplv-k3u1fbpfcp-zoom-1.image)
+ 
+
+
+##### 鉴权测试  
+
+通过上面的sureness.yml文件配置的用户-角色-资源,我们可以关联下面几个典型测试点  
+1. `/api/v1/source3===get`资源可以被任何直接访问,不需要认证鉴权  
+2. `api/v1/source2===get`资源持所有角色或无角色访问 前提是认证成功  
+3. 用户admin能访问`/api/v1/source1===get`资源,而用户root,tom无权限  
+4. 用户tom能访`/api/v1/source1===delete`资源,而用户admin.root无权限  
+测试如下:  
+
+**`/api/v1/source3===get`资源可以被任何直接访问,不需要认证鉴权**  
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae2b3db8d7b64c9abf18c243943f7a4d~tplv-k3u1fbpfcp-zoom-1.image)
+
+**`api/v1/source2===get`资源持所有角色或无角色访问 前提是认证成功**  
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ace184c86cc5438f91f7614b961dfab1~tplv-k3u1fbpfcp-zoom-1.image)
+
+**用户admin能访问`/api/v1/source1===get`资源,而用户root,tom无权限**  
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f5fcd36befa84fb59b49c3cc35cb206e~tplv-k3u1fbpfcp-zoom-1.image)
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/45f9454365ef4a45abc90e436eb0d2f0~tplv-k3u1fbpfcp-zoom-1.image)
+
+**用户tom能访`/api/v1/source1===delete`资源,而用户admin.root无权限**  
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e9bce40c97dd4aaf9924845922c4917b~tplv-k3u1fbpfcp-zoom-1.image)
+![image](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/209598b7717947c585bf479817b24726~tplv-k3u1fbpfcp-zoom-1.image)
+
+
+### 其他  
+
+这次图文一步一步的详细描述了构建一个简单但完整的认证鉴权项目的流程,当然里面的授权账户等信息是写在配置文件里面的,实际的项目是会把这些数据写在数据库中。
+万变不离其宗,无论是写配置文件还是数据库,它只是作为数据源提供数据,基于sureness我们也能轻松快速构建基于数据库的认证鉴权项目,支持动态刷新等各种功能。  
+
+基于数据库方案的项目可参考下方样例->sureness集成springboot样例(数据库方案), 此次一步一步完成的系统源代码也在下方 -> sureness集成springboot样例(配置文件方案)   
+
+<br>    
+
+#### DEMO源代码仓库  
+
+- [x] sureness集成springboot样例(配置文件方案) [sample-bootstrap](https://github.com/tomsun28/sureness/tree/master/sample-bootstrap)      
+- [x] sureness集成springboot样例(数据库方案) [sample-tom](https://github.com/tomsun28/sureness/tree/master/sample-tom)    
+
+
+
+

+ 62 - 0
home/i18n/en/docusaurus-theme-classic/footer.json

@@ -0,0 +1,62 @@
+{
+  "link.title.About Sureness": {
+    "message": "关于 Sureness",
+    "description": "The title of the footer links column with title=About Sureness in the footer"
+  },
+  "link.title.Community": {
+    "message": "社区与交流",
+    "description": "The title of the footer links column with title=Community in the footer"
+  },
+  "link.title.More": {
+    "message": "更多相关",
+    "description": "The title of the footer links column with title=More in the footer"
+  },
+  "link.item.label.High Performance": {
+    "message": "高性能",
+    "description": "The label of footer link with label=High Performance linking to https://github.com/tomsun28/sureness-shiro-spring-security-benchmark"
+  },
+  "link.item.label.Dashboard": {
+    "message": "看板",
+    "description": "The label of footer link with label=Dashboard linking to https://github.com/dromara/sureness/projects/1"
+  },
+  "link.item.label.Github": {
+    "message": "Github",
+    "description": "The label of footer link with label=Github linking to https://github.com/dromara/sureness"
+  },
+  "link.item.label.Gitee": {
+    "message": "Gitee",
+    "description": "The label of footer link with label=Gitee linking to https://gitee.com/dromara/sureness"
+  },
+  "link.item.label.Github Discussion": {
+    "message": "Github Discussion",
+    "description": "The label of footer link with label=Github Discussion linking to https://github.com/dromara/sureness/discussions"
+  },
+  "link.item.label.Gitter Channel": {
+    "message": "Gitter Channel",
+    "description": "The label of footer link with label=Gitter Channel linking to https://gitter.im/usthe/sureness"
+  },
+  "link.item.label.USTHE": {
+    "message": "USTHE",
+    "description": "The label of footer link with label=USTHE linking to https://github.com/usthe"
+  },
+  "copyright": {
+    "message": "Apache License 2.0 | Copyright © 2021",
+    "description": "The footer copyright"
+  },
+  "link.item.label.Dromara": {
+    "message": "Dromara 社区",
+    "description": "The label of footer link with label=Dromara linking to https://dromara.org"
+  },
+  "link.item.label.QQ Group - 390083213": {
+    "message": "QQ 群 - 390083213",
+    "description": "The label of footer link with label=QQ Group - 390083213 linking to https://qm.qq.com/cgi-bin/qm/qr?k=3IpzQjFOztJe464_eMBmDHfT0YTWK5Qa&jump_from=webapi"
+  },
+  "link.item.label.Tom Blog": {
+    "message": "Tom 博客",
+    "description": "The label of footer link with label=Tom Blog linking to https://blog.usthe.com"
+  },
+  "link.item.label.Tom": {
+    "message": "Tom",
+    "description": "The label of footer link with label=Tom linking to https://github.com/tomsun28"
+  }
+}

+ 54 - 0
home/i18n/en/docusaurus-theme-classic/navbar.json

@@ -0,0 +1,54 @@
+{
+  "title": {
+    "message": " ",
+    "description": "The title in the navbar"
+  },
+  "item.label.Document": {
+    "message": "文档",
+    "description": "Navbar item with label Document"
+  },
+  "item.label.Dashboard": {
+    "message": "看板",
+    "description": "Navbar item with label Dashboard"
+  },
+  "item.label.High Performance": {
+    "message": "高性能",
+    "description": "Navbar item with label High Performance"
+  },
+  "item.label.Discuss": {
+    "message": "讨论",
+    "description": "Navbar item with label Discuss"
+  },
+  "item.label.Github Discussion": {
+    "message": "Github Discussion",
+    "description": "Navbar item with label Github Discussion"
+  },
+  "item.label.Gitter Channel": {
+    "message": "Gitter Channel",
+    "description": "Navbar item with label Gitter Channel"
+  },
+  "item.label.QQ Group - 390083213": {
+    "message": "QQ 群 - 390083213",
+    "description": "Navbar item with label QQ Group - 390083213"
+  },
+  "item.label.Blog": {
+    "message": "博客",
+    "description": "Navbar item with label Blog"
+  },
+  "item.label.Other": {
+    "message": "其它",
+    "description": "Navbar item with label Other"
+  },
+  "item.label.Design": {
+    "message": "设计文档",
+    "description": "Navbar item with label Design"
+  },
+  "item.label.Contributing": {
+    "message": "参与贡献",
+    "description": "Navbar item with label Contributing"
+  },
+  "item.label.Sponsor": {
+    "message": "赞助",
+    "description": "Navbar item with label Sponsor"
+  }
+}

+ 49 - 0
home/package.json

@@ -0,0 +1,49 @@
+{
+  "name": "tancloud",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "docusaurus": "docusaurus",
+    "start": "docusaurus start",
+    "build": "docusaurus build",
+    "swizzle": "docusaurus swizzle",
+    "deploy": "docusaurus deploy",
+    "serve": "docusaurus serve",
+    "clear": "docusaurus clear",
+    "write-translations": "docusaurus write-translations",
+    "write-heading-ids": "docusaurus write-heading-ids"
+  },
+  "dependencies": {
+    "@docusaurus/core": "^2.0.0-beta.14",
+    "@docusaurus/plugin-client-redirects": "^2.0.0-beta.14",
+    "@docusaurus/plugin-ideal-image": "^2.0.0-beta.14",
+    "@docusaurus/plugin-pwa": "^2.0.0-beta.14",
+    "@docusaurus/preset-classic": "^2.0.0-beta.14",
+    "@docusaurus/remark-plugin-npm2yarn": "^2.0.0-beta.14",
+    "@docusaurus/theme-search-algolia": "^2.0.0-beta.14",
+    "@docusaurus/theme-live-codeblock": "2.0.0-beta.14",
+    "@mdx-js/react": "^1.6.21",
+    "@svgr/webpack": "^5.5.0",
+    "clsx": "^1.1.1",
+    "color": "^3.1.3",
+    "file-loader": "^6.2.0",
+    "react": "^17.0.1",
+    "react-dom": "^17.0.1",
+    "sharp": "^0.28.3",
+    "url-loader": "^4.1.1",
+    "workbox-routing": "^6.1.0",
+    "workbox-strategies": "^6.1.0"
+  },
+  "browserslist": {
+    "production": [
+      ">0.5%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  }
+}

+ 63 - 0
home/sidebars.json

@@ -0,0 +1,63 @@
+{
+  "docs": [
+    {
+      "type": "category",
+      "label": "快速入门",
+      "items": [
+        "introduce",
+        "start/quickstart",
+        "start/mysql-init",
+        "start/tdengine-init",
+        "start/docker-deploy",
+        "start/package-deploy"
+      ]
+    },
+    {
+      "type": "category",
+      "label": "自定义监控",
+      "items": [
+        "advanced/extend-point",
+        "advanced/custom-subject",
+        "advanced/custom-subject-creator",
+        "advanced/custom-processor",
+        "advanced/custom-datasource"
+      ]
+    },
+    {
+      "type": "category",
+      "label": "帮助文档",
+      "items": [
+        {
+          "type": "category",
+          "label": "应用服务监控",
+          "items": [
+            "integrate/sample-bootstrap"
+          ]
+        },
+        {
+          "type": "category",
+          "label": "数据库监控",
+          "items": [
+            "integrate/sample-bootstrap"
+          ]
+        },
+        "integrate/sample-bootstrap",
+        "integrate/sample-tom",
+        "integrate/sample-quarkus",
+        "integrate/sample-javalin",
+        "integrate/sample-spring-webflux",
+        "integrate/sample-ktor",
+        "integrate/sample-micronaut"
+      ]
+    },
+    {
+      "type": "category",
+      "label": "Others",
+      "items": [
+        "design",
+        "contributing",
+        "sponsor"
+      ]
+    }
+  ]
+}

+ 11 - 0
home/src/CdnTransfer.js

@@ -0,0 +1,11 @@
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
+
+export default function transfer(imageUrl) {
+    const {siteConfig} = useDocusaurusContext();
+    const {cdnUrl} = siteConfig.customFields;
+    if (cdnUrl !== null && imageUrl !== undefined && imageUrl !== null) {
+        return cdnUrl + imageUrl;
+    } else {
+        return imageUrl;
+    }
+}

+ 171 - 0
home/src/constants.js

@@ -0,0 +1,171 @@
+import React from 'react'
+
+import Translate, {translate} from '@docusaurus/Translate'
+
+
+export const features = [{
+    title: translate({
+        message: '开箱即用'
+    }),
+    description: (
+        <>
+            <Translate values={{
+                TANCLOUD: <strong>TANCLOUD</strong>,
+                SAAS: <strong>SAAS</strong>,
+                console: <a href={'https://www.console.tancloud.cn'}><strong>注册登陆</strong></a>,
+                authorization: <strong>authorization</strong>,
+                simple: <strong>simple</strong>,
+                useful: <strong>useful</strong>,
+                interface: <strong>扩展接口</strong>,
+                sample: <strong>样例</strong>,
+                doc: <strong>文档</strong>,
+                br: <br/>
+            }}>
+                {'中小团队和个人无需再为了监控自己的网站资源,而去部署一套繁琐的监控系统。往往有时候,那套监控系统比自身网站消耗的资源还大。' +
+                '{TANCLOUD} 提供{SAAS}云版本,{console}即可开始您的服务监控。{br}' +
+                '安全是最重要的,我们对账户密钥和监控密钥全链路加密。'
+                }
+            </Translate>
+        </>
+    ),
+}, {
+    title: translate({
+        message: '多支持与自定义'
+    }),
+    description: (
+        <>
+            <Translate values={{
+                WebSockets: <strong>WebSockets</strong>,
+                Servlet: <strong>Servlet</strong>,
+                JaxRs: <strong>JAX-RS</strong>,
+                native: <strong>Native</strong>,
+                SpringBoot: <strong>Spring Boot</strong>,
+                SpringWebFlux: <strong>Spring WebFlux</strong>,
+                Javalin: <strong>Javalin</strong>,
+                Quarkus: <strong>Quarkus</strong>,
+                Ktor: <strong>Ktor</strong>,
+                Micronaut: <strong>Micronaut</strong>,
+                Solon: <strong>Solon</strong>,
+                Jfinal: <strong>Jfinal</strong>,
+                br: <br/>
+            }}>
+                {'TANCLOUD目前支持对网站,API,PING连通性,端口可用性,MYSQL数据库等的监控,不久我们将兼容 prometheus 协议,提供更多的监控类型和性能指标。{br}' +
+                '我们提供了更自由化的阈值告警配置,支持邮箱,短信,webhook等告警通知,更多三方应用通知正在集成中。{br}' +
+                '不同团队的监控需求千变万化,我们提供自定义监控,仅需配置YML就能快速接入监控系统。'
+                }
+            </Translate>
+        </>
+    ),
+},
+    {
+        title: translate({
+            message: '拥抱开源'
+        }),
+        description: (
+            <>
+                <Translate values={{
+                    jwt: <strong>JWT</strong>,
+                    basic: <strong>Basic Auth</strong>,
+                    digest: <strong>Digest Auth</strong>,
+                    dynamic: <strong>Dynamic</strong>,
+                    perm: <strong>permissions</strong>,
+                    dynamicCn: <strong>动态修改权限</strong>,
+                    high: <strong>High performance</strong>,
+                    highCn: <strong>高性能</strong>,
+                    tree: <strong>Dictionary Matching Tree</strong>,
+                    treeCn: <strong>改进的字典匹配树</strong>,
+                    br: <br/>
+                }}>
+                    {'TANCLOUD监控系统代码开源,非常欢迎任何对此有兴趣的同学参与中来,我们一起进步,丰富的资源文档正在完善中。{br}' +
+                    '我们相信开源改变世界!'
+                    }
+                </Translate>
+            </>
+        ),
+    }]
+
+export const friendLinks = [
+    {
+        img: 'ShenYu_logo.png',
+        alt: 'ShenYu',
+        url: 'https://dromara.org/projects/soul/overview/'
+    }, {
+        img: 'maxkey_logo.png',
+        alt: 'MaxKey',
+        url: 'https://maxkey.top/'
+    }, {
+        img: 'tlog_logo.png',
+        alt: 'TLog',
+        url: 'https://yomahub.com/tlog/'
+    }, {
+        img: 'hutool_logo.jpg',
+        alt: 'Hutool',
+        url: 'https://hutool.cn/'
+    }, {
+        img: 'satoken_logo.png',
+        alt: 'Sa-Token',
+        url: 'http://sa-token.dev33.cn/'
+    }, {
+        img: 'justauth_logo.png',
+        alt: 'Justauth',
+        url: 'https://justauth.wiki/'
+    }, {
+        img: 'pha_logo.jfif',
+        alt: 'pha_api',
+        url: 'https://www.phalapi.net/'
+    }, {
+        img: 'liteflow_logo.png',
+        alt: 'LiteFlow',
+        url: 'https://yomahub.com/liteflow/'
+    }
+]
+
+export const mediaPartners = [
+    {
+        img: 'dromara_qr.png',
+        alt: 'Dromara',
+        url: 'https://mp.weixin.qq.com/s/Q3b7ZE802IMF6MwIPJIGQA'
+    },
+    {
+        img: 'JavaHouDuan_logo.png',
+        alt: 'JavaHouDuan',
+        url: 'https://mp.weixin.qq.com/s/Ylq51a7Av8ZRuH811xZnDA'
+    }
+]
+
+
+export const SetupExample = `
+<dependency>
+    <groupId>com.usthe.sureness</groupId>
+    <artifactId>sureness-core</artifactId>
+    <version>1.0.6</version>
+</dependency>
+
+compile group: 'com.usthe.sureness', name: 'sureness-core', version: '1.0.6'
+`
+
+export const SurenessIntegration = `
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+            throws IOException, ServletException {
+        try {
+            SubjectSum subject = SurenessSecurityManager.getInstance().checkIn(servletRequest);
+        } catch (IncorrectCredentialsException | UnknownAccountException | ExpiredCredentialsException e1) {
+            logger.debug("this request account info is illegal");
+            responseWrite(ResponseEntity
+                    .status(HttpStatus.UNAUTHORIZED).body(e1.getMessage()), servletResponse);
+            return;
+        } catch (UnauthorizedException e4) {
+            logger.debug("this account can not access this resource");
+            responseWrite(ResponseEntity
+                    .status(HttpStatus.FORBIDDEN).body(e4.getMessage()), servletResponse);
+            return;
+        } catch (RuntimeException e) {
+            logger.error("other exception happen: ", e);
+            responseWrite(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(),
+                    servletResponse);
+            return;
+        }
+        filterChain.doFilter(servletRequest, servletResponse);
+    }
+`

+ 14 - 0
home/src/css/buttons.css

@@ -0,0 +1,14 @@
+html[data-theme=dark] .button {
+    --ifm-button-border-color: transparent;
+}
+
+.button--secondary {
+    background-color: var(--ifm-color-primary);
+    color: #fff;
+    font-family: 'IBM Plex Mono';
+    font-size: 1em;
+}
+
+body .button.button--secondary {
+    color: var(--ifm-color-white)!important;
+}

+ 280 - 0
home/src/css/custom.css

@@ -0,0 +1,280 @@
+@import url(navbar.css);
+@import url(docsearch.css);
+@import url(hero.css);
+@import url(buttons.css);
+
+:root {
+    --ifm-color-primary: #7228B5;
+    --ifm-color-primary-dark: #6228B5;
+    --ifm-color-primary-darker: #5228B5;
+    --ifm-color-primary-darkest: #5128B5;
+    --ifm-color-primary-light: #7228B5;
+    --ifm-color-primary-lighter: #8228B5;
+    --ifm-color-primary-lightest: #9228B5;
+    --ifm-navbar-link-hover-color: var(--ifm-color-primary);
+    --ifm-background-color: #fafafa;
+    --ifm-code-font-size: 95%;
+    --ifm-navbar-link-color: #000;
+    --ifm-font-family-base: 'IBM Plex Sans', PT Sans, Montserrat, Lato;
+    --ifm-font-family-monospace: 'IBM Plex Mono', PT Sans, Open Sans, Roboto Slab, Roboto Condensed;
+    --ifm-button-padding-horizontal: 0.55rem;
+    --ifm-button-padding-vertical: .7rem;
+    --ifm-menu-color: #000;
+    --ifm-menu-color-background-active: transparent;
+    --ifm-toc-link-color: #000;
+    --ifm-table-border-color: #efefef;
+    --ifm-navbar-height: 85px;
+    --ifm-button-border-width: 0;
+
+    --wdio-spacing-vertical: 60px;
+    --wdio-link-background-color: #eee0d7;
+    --wdio-link-color: #000;
+    --wdio-section-background-color: #fff;
+    --wdio-dark-section-background-color: #efefef;
+}
+
+html[data-theme=dark] {
+    --wdio-link-background-color: #4e1e02;
+    --wdio-link-color: #fff;
+    --ifm-menu-color: #fff;
+    --ifm-table-border-color: var(--ifm-color-emphasis-300);
+    --ifm-toc-link-color: #fff;
+}
+
+@media (max-width: 996px) {
+    :root {
+        --wdio-spacing-vertical: 20px;
+    }
+}
+
+@media (max-width: 500px) {
+    :root {
+        --wdio-spacing-vertical: 10px;
+    }
+}
+
+@media only screen and (-webkit-min-device-pixel-ratio: 1) {
+    :root {
+        --ifm-line-height-base: 1.85;
+    }
+}
+
+.menu__link {
+    font-weight: bold;
+}
+
+.menu__link {
+    font-size: 1.1em;
+}
+
+.menu__list .menu__list {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+}
+
+.menu__list .menu__list .menu__list-item {
+    margin: 0;
+}
+
+.menu__list .menu__list .menu__list-item .menu__link {
+    border-left: 1px solid transparent;
+}
+
+.menu__list .menu__list .menu__list-item .menu__link:hover,
+.menu__list .menu__list .menu__list-item .menu__link--active {
+    border-left: 1px solid var(--ifm-color-primary);
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+}
+
+.menu__list .menu__list .menu__list-item .menu__link {
+    font-size: 0.9em;
+    font-weight: normal;
+}
+
+.menu__list .menu__list .menu__list-item .menu__link--active {
+    font-weight: bold;
+    color: var(--wdio-link-color);
+}
+
+.footer__links {
+    width: 75%;
+    float: right;
+}
+.footer__bottom {
+    width: 25%;
+}
+.footer__bottom > div {
+    text-align: left;
+}
+.footer--dark {
+    --ifm-footer-background-color: #20232a;
+    --ifm-footer-padding-vertical: 85px;
+}
+.footer__copyright {
+    font-size: .8em;
+    text-align: left;
+}
+
+/*.footer__link-item[target="_blank"]:after {*/
+/*    padding: 0 4px;*/
+/*    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 100 100' width='15' height='15'%3E%3Cpath fill='gray' d=' M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0, 0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z '%3E%3C/path%3E%3Cpolygon fill='gray' points=' 45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8, 14.9 62.8,22.9 71.5,22.9 '%3E%3C/polygon%3E%3C/svg%3E")*/
+/*}*/
+
+html[data-theme=dark] {
+    --wdio-section-background-color: var(--ifm-footer-background-color);
+    --wdio-dark-section-background-color: #18191A;
+}
+
+.docusaurus-highlight-code-line {
+    background-color: #484d5b;
+    display: block;
+    margin: 0 calc(-1 * var(--ifm-pre-padding));
+    padding: 0 var(--ifm-pre-padding);
+}
+
+.features_src-pages- h3 {
+    text-align: center;
+}
+.features_src-pages- .featureImage_src-pages- {
+    width: 120px;
+    height: 120px;
+    margin-bottom: 20px;
+}
+
+.twitterProfile,
+.githubProfile {
+    margin-left: 10px;
+}
+.twitterProfile svg,
+.githubProfile svg {
+    width: 23px;
+    color: #1B98E4;
+    transform: translate(0, 3px);
+}
+
+.githubProfile:before {
+    content: '';
+    width: 27px;
+    height: 25px;
+    background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat;
+}
+
+html[data-theme="dark"] .githubProfile:before {
+    background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
+        no-repeat;
+}
+
+img[alt^=Support] {
+    width: 20px;
+    margin-right: 5px;
+    display: inline-block;
+}
+
+.main-wrapper .markdown p > a,
+.main-wrapper .container p > a {
+    background-color: var(--wdio-link-background-color);
+    color: var(--wdio-link-color);
+    text-decoration: none!important;
+    border-bottom: 1px solid var(--ifm-color-primary);
+    padding: 2px;
+}
+.main-wrapper .container .admonition p > a {
+    background-color: transparent;
+    color: inherit;
+    text-decoration: underline!important;
+    border-bottom: 0;
+}
+
+.main-wrapper .markdown p > a:hover,
+.main-wrapper .container p > a:hover {
+    color: var(--ifm-color-primary)
+}
+
+article thead {
+    background-color: var(--ifm-table-border-color);
+    font-family: 'IBM Plex Mono';
+    text-align: left;
+}
+
+h1, h2, h3 {
+    font-weight: 400;
+}
+
+.prism-code > div {
+    background-color: var(--ifm-table-border-color)!important;
+}
+.darkSection .prism-code > div {
+    background-color: var(--wdio-section-background-color)!important;
+}
+html[data-theme=dark] .prism-code > div {
+    background-color: #282B36!important;
+}
+div[class^='codeBlockTitle'] {
+    background-color: var(--ifm-table-border-color)!important;
+    border-bottom: var(--ifm-color-emphasis-300) 1px solid;
+}
+html[data-theme=dark] div[class^='codeBlockTitle'] {
+    background-color: #282B36!important;
+}
+div[role="tabpanel"] .prism-code {
+    border-top-left-radius: 0;
+}
+.tabs__item:hover {
+    border-bottom-left-radius: 0;
+}
+.markdown > div > .margin-vert--md {
+    margin-top: 0!important;
+}
+
+a code {
+    background-color: transparent;
+}
+
+.highlightSection .token-line {
+    font-size: 90%!important
+}
+
+.highlightSection .prism-code:before {
+    content: "";
+    width: 12px;
+    height: 12px;
+    background: #ff5f56;
+    display: block;
+    border-radius: 50%;
+    position: absolute;
+    top: 12px;
+    left: 12px;
+}
+
+.highlightSection .prism-code:after {
+    content: "";
+    width: 12px;
+    height: 12px;
+    background: #ffbd2e;
+    display: block;
+    border-radius: 50%;
+    position: absolute;
+    top: 12px;
+    left: 32px;
+}
+
+.highlightSection .prism-code > div {
+    padding-top: calc(var(--ifm-pre-padding) + 5px);
+}
+.highlightSection .prism-code > div:before {
+    content: "";
+    display: block;
+    width: 12px;
+    height: 12px;
+    background: #27C941;
+    border-radius: 50%;
+    position: absolute;
+    top: 12px;
+    left: 52px;
+}
+
+div[role="tabpanel"] .margin-vert--md {
+    margin-top: 0!important;
+    margin-bottom: 0!important;
+}

+ 21 - 0
home/src/css/docsearch.css

@@ -0,0 +1,21 @@
+.navbar .DocSearch-Button {
+    height: 30px;
+}
+
+.navbar .DocSearch-Button-Placeholder {
+    font-size: 1.1rem;
+    font-family: 'IBM Plex Mono';
+    color: #999
+}
+
+body .navbar .DocSearch-Search-Icon {
+    width: 15px;
+    height: 15px;
+    color:#999
+}
+
+.navbar .DocSearch-Button-Key {
+    width: 18px;
+    height: 16px;
+    top: 0px;
+}

+ 38 - 0
home/src/css/hero.css

@@ -0,0 +1,38 @@
+.hero--primary {
+    --ifm-hero-background-color: #fff;
+    --ifm-hero-text-color: #000;
+}
+
+html[data-theme=dark] .hero--primary {
+    --ifm-hero-background-color: var(--ifm-footer-background-color);
+    --ifm-hero-text-color: #fff;
+}
+
+.hero__title {
+    background-size: 110%;
+    color: transparent;
+    width: 100%;
+    max-width: 600px;
+    margin: 0 auto;
+    height: 280px;
+}
+
+.hero__subtitle {
+    font-size: 2em;
+    max-width: 600px;
+    width: 100%;
+    margin: 0 auto;
+}
+
+@media (max-width: 400px) {
+    .hero__subtitle {
+        font-size: 1.3em;
+    }
+}
+
+#fblike {
+    height: 20px;
+}
+#fblike, #twitter-widget-1 {
+    margin-right: 20px;
+}

+ 92 - 0
home/src/css/navbar.css

@@ -0,0 +1,92 @@
+:root {
+    --ifm-navbar-background-color: #efefef;
+}
+
+html[data-theme=dark] {
+    --ifm-navbar-link-color: #ffffff;
+    --ifm-navbar-background-color: #000000;
+}
+
+.navbar {
+    background: var(--ifm-navbar-background-color);
+    text-align: left;
+    padding: 0;
+    height: 60px;
+}
+
+.navbar__items--right {
+    margin-right: 20px;
+}
+
+.navbar .navbar__items > .navbar__brand {
+    height: 40px;
+    margin: 5px 10px 10px 25px;
+}
+
+.navbar .navbar-sidebar .navbar__brand {
+    margin-left: 8px;
+}
+
+.navbar__link {
+    font-size: 16px;
+}
+
+@media (max-width: 996px) {
+    .navbar__toggle {
+        margin-left: 1rem;
+    }
+}
+
+.header-console-link {
+    background-color: var(--ifm-color-primary);
+    color: #fff;
+    border-radius: 4px;
+    margin-right: 30px;
+}
+
+.header-github-link:hover {
+    opacity: 0.6;
+}
+
+.header-github-link:before {
+    content: '';
+    width: 30px;
+    height: 30px;
+    display: flex;
+    background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat;
+}
+.teamProfile {
+    margin-bottom: 20px;
+}
+.githubProfile {
+    margin-left: 15px;
+}
+.githubProfile:before {
+    content: '';
+    display: inline-block;
+    transform: translate(0, 3px);
+    height: 20px;
+    width: 20px;
+}
+html[data-theme="dark"] .header-github-link:before {
+    background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
+        no-repeat;
+}
+
+.toggle_node_modules-\@docusaurus-theme-classic-lib-next-theme-Toggle- {
+    width: 0
+}
+
+body .react-toggle-track {
+    width: 55px;
+    height: 30px;
+}
+
+body .react-toggle-track-x {
+    right: 15px;
+}
+
+body .react-toggle-thumb {
+    top: 4px;
+    left: 3px;
+}

+ 19 - 0
home/src/pages/components/Feature.js

@@ -0,0 +1,19 @@
+import React from 'react'
+import clsx from 'clsx'
+import styles from './Feature.module.css'
+import cdnTransfer from '../../CdnTransfer'
+
+export default function Feature({ imageUrl, title, description }) {
+    const imgUrl = cdnTransfer(imageUrl)
+    return (
+        <div className={clsx('col col--4', styles.feature)}>
+            {imgUrl && (
+                <div className="text--center">
+                    <img className={styles.featureImage} src={imgUrl} alt={title} />
+                </div>
+            )}
+            <h3>{title}</h3>
+            <p>{description}</p>
+        </div>
+    )
+}

+ 15 - 0
home/src/pages/components/Feature.module.css

@@ -0,0 +1,15 @@
+.features {
+    display: flex;
+    align-items: center;
+}
+
+.featureImage {
+    height: 200px;
+    width: 200px;
+}
+
+.features h3 {
+    font-size: 1.7em;
+    font-weight: 500;
+    text-align: left;
+}

+ 34 - 0
home/src/pages/components/Highlight.js

@@ -0,0 +1,34 @@
+import React from 'react'
+import clsx from 'clsx'
+
+import styles from './highlight.module.css'
+
+export default function Highlight({ reversed, title, img, text, isDark }) {
+    const left = <div className={clsx('col col--6', styles.featureImage, reversed ? styles.featureImageReversed : '')}>{img}</div>
+    const right = (
+        <div className={clsx('col col--6', styles.featureContent, reversed ? styles.featureContentReversed : '')}>
+            <h3 className={styles.featureTitle}>{title}</h3>
+            {text}
+        </div>
+    )
+
+    return (
+        <section className={clsx('highlightSection', isDark ? styles.darkSection + ' darkSection' : '')}>
+            <div className="container">
+                <div className="row">
+                    {reversed ? (
+                        <>
+                            {right}
+                            {left}
+                        </>
+                    ) : (
+                        <>
+                            {left}
+                            {right}
+                        </>
+                    )}
+                </div>
+            </div>
+        </section>
+    )
+}

+ 96 - 0
home/src/pages/components/LogoCarousel.js

@@ -0,0 +1,96 @@
+import React from 'react'
+import clsx from 'clsx'
+import styles from './LogoCarousel.module.css'
+
+const INTERVAL_LENGTH = 5000
+const LOGO_WIDTH = 150
+const cdnUrl = 'https://cdn.jsdelivr.net/gh/usthe/sureness@gh-pages/img/icons/'
+
+let ticks = 0
+
+export default class LogoCarousel extends React.Component {
+    constructor (props) {
+        super(props)
+        this.state = {
+            position: -0,
+            activePage: 0,
+            swapInterval: 0,
+            pages: Math.ceil(props.logos ? props.logos.length / 6 : 1),
+            margin: 70
+        }
+
+        this.imgUrl = 
+
+        this.containerRef = React.createRef()
+    }
+
+    componentDidMount() {
+        const rect = this.containerRef.current.getBoundingClientRect()
+        const logosPerPage = Math.floor(rect.width / LOGO_WIDTH)
+        this.setState({
+            swapInterval: setInterval(this.nextPage.bind(this), INTERVAL_LENGTH),
+            pages: Math.ceil(this.props.logos ? this.props.logos.length / logosPerPage : 1),
+            margin: rect.width < 700 ? 0 : 70
+        })
+    }
+
+    componentWillUnmount () {
+        clearInterval(this.state.swapInterval)
+    }
+
+    animateTo (i) {
+        const width = this.containerRef.current.getBoundingClientRect().width - this.state.margin
+        const x = i * -width
+        this.setState({ position: x, activePage: i })
+    }
+
+    handleClick (i) {
+        this.animateTo(i)
+        clearInterval(this.state.swapInterval)
+        this.setState({
+            swapInterval: setInterval(this.nextPage.bind(this), INTERVAL_LENGTH)
+        })
+    }
+
+    nextPage () {
+        const pages = this.state.pages - 1
+        const direction = Math.floor(ticks / pages) % 2
+        this.animateTo(direction
+            ? pages - (ticks % pages)
+            : ticks % pages
+        )
+        ++ticks
+    }
+
+    render () {
+        if (!this.props || !this.props.logos) {
+            return (
+                <div></div>
+            )
+        }
+
+        this.buttons = () => [...Array(this.state.pages)].map((_, index) => (
+            <button onClick={() => this.handleClick(index)} key={index} className={clsx(styles.button, index === this.state.activePage ? styles.buttonActive : '')}>{index + 1}</button>
+        ))
+
+        this.list = () => (
+            <ul style={{ transform: `translate(${this.state.position}px, 0px)` }}>
+                {this.props.logos.map((value, index) => (
+                    <li key={index}><a href={value.url} target="_blank" rel="noopener noreferrer"><img src={cdnUrl + value.img} alt={value.alt} /></a></li>
+                ))}
+            </ul>
+        )
+
+        return (
+            <div className={styles.companyUsage} ref={this.containerRef}>
+                <h3>{this.props.headerTitle}</h3>
+                <div className={clsx(styles.logos)}>
+                    {this.list()}
+                    <div className={styles.logoNavigation}>
+                        {this.buttons()}
+                    </div>
+                </div>
+            </div>
+        )
+    }
+}

+ 82 - 0
home/src/pages/components/LogoCarousel.module.css

@@ -0,0 +1,82 @@
+.companyUsage {
+    width: 100%;
+}
+
+body .companyUsage h3 {
+    font-size: 1.1em;
+    font-weight: 900;
+}
+
+.logos {
+    overflow: hidden;
+    margin: 0 25px;
+}
+
+.logos ul {
+    display: flex;
+    justify-content: space-around;
+    /*width: 3200px;*/
+    margin: 0;
+    transition: transform 1000ms ease;
+}
+
+@media screen and (max-width: 1100px) {
+    .logos {
+        margin: 0;
+    }
+    .logos ul {
+        padding: 0;
+        width: 3000px;
+    }
+    .logos ul li {
+        min-width: 125px;
+    }
+}
+
+.logos ul li {
+    display: flex;
+    min-width: 150px;
+    justify-content: center;
+    align-items: center;
+}
+.logos ul img {
+    transition: filter 200ms;
+    width: 100px;
+}
+
+.logos ul a {
+    background: none;
+    border: 0;
+}
+
+.logoNavigation {
+    text-align: center;
+}
+
+.button:focus {
+    outline: none;
+}
+
+.button::before {
+    content: "\2022";
+    color: var(--wdio-link-color);
+    font-size: 50px;
+    opacity: .25;
+}
+
+.button.buttonActive::before {
+    opacity: 1;
+}
+
+.button {
+    transition: opacity 200ms ease;
+    color: transparent;
+    border: 0;
+    background: 0;
+    cursor: pointer;
+    padding: 10px 10px 0 10px;
+    width: 30px;
+    line-height: 10px;
+    margin: 0 5px;
+    transform: translateY(20px);
+}

File diff ditekan karena terlalu besar
+ 23 - 0
home/src/pages/components/Robot.js


+ 122 - 0
home/src/pages/components/Robot.module.css

@@ -0,0 +1,122 @@
+.svg {
+    width: 60%;
+    transform: translateY(10%);
+}
+
+#headDot1, #headDot2 {
+    animation: headblinker 5000ms infinite;
+}
+@keyframes headblinker {
+    0% { opacity: 0; }
+    10% { opacity: 1; }
+    20% { opacity: 0; }
+    30% { opacity: 1; }
+    40% { opacity: 0; }
+    50% { opacity: 1; }
+    60% { opacity: 0; }
+    70% { opacity: 1; }
+    75% { opacity: 0; }
+    100% { opacity: 0; }
+}
+
+#bodyDot1 {
+    animation: blinker1st 1s ease-in-out infinite;
+}
+@keyframes blinker1st {
+    10% { opacity: 0; }
+    20% { opacity: 1; }
+    60% { opacity: 1; }
+    70% { opacity: 0; }
+    80% { opacity: 1; }
+    90% { opacity: 0; }
+    100% { opacity: 1; }
+}
+
+#bodyDot2 {
+    animation: blinker2nd 1s ease-in-out infinite;
+}
+@keyframes blinker2nd {
+    10% { opacity: 1; }
+    20% { opacity: 0; }
+    30% { opacity: 1; }
+    60% { opacity: 1; }
+    70% { opacity: 0; }
+    80% { opacity: 1; }
+    90% { opacity: 0; }
+    100% { opacity: 1; }
+}
+
+#bodyDot3 {
+    animation: blinker3rd 1s ease-in-out infinite;
+}
+@keyframes blinker3rd {
+    20% { opacity: 1; }
+    30% { opacity: 0; }
+    40% { opacity: 1; }
+    60% { opacity: 1; }
+    70% { opacity: 0; }
+    80% { opacity: 1; }
+    90% { opacity: 0; }
+    100% { opacity: 1; }
+}
+
+#bodyDot4 {
+    animation: blinker4th 1s ease-in-out infinite;
+}
+@keyframes blinker4th {
+    30% { opacity: 1; }
+    40% { opacity: 0; }
+    50% { opacity: 1; }
+    60% { opacity: 1; }
+    70% { opacity: 0; }
+    80% { opacity: 1; }
+    90% { opacity: 0; }
+    100% { opacity: 1; }
+}
+
+#head,
+.ear,
+.eye {
+    animation-name: wiggle;
+    animation-duration: 5000ms;
+    animation-iteration-count: infinite;
+    transform-origin: unset;
+}
+@keyframes wiggle {
+    75% {transform: rotate(0deg);}
+    80% {transform: rotate(5deg);}
+    85% {transform: rotate(-5deg);}
+    90% {transform: rotate(10deg);}
+    95% {transform: rotate(-3deg);}
+    100% {transform: rotate(0deg);}
+}
+
+#lineTop {
+    stroke-dasharray: 38;
+    stroke-dashoffset: 0;
+    animation: lineAnimation 1s ease-in-out infinite alternate;
+}
+#lineMiddle {
+    stroke-dasharray: 38;
+    stroke-dashoffset: 0;
+    animation: lineAnimation 1s cubic-bezier(0.1, 0.7, 1.0, 0.1) infinite alternate;
+}
+#lineBottom {
+    stroke-dasharray: 38;
+    stroke-dashoffset: 0;
+    animation: lineAnimation 1s cubic-bezier(0.5, 0.2, .3, 0.9) infinite alternate;
+}
+@keyframes lineAnimation {
+    0% { stroke-dashoffset: 0; }
+    100% { stroke-dashoffset: 38; }
+}
+
+#processLine {
+    stroke-dasharray: 82;
+    stroke-dashoffset: -82;
+    animation: processAnimation 4s linear infinite;
+}
+@keyframes processAnimation {
+    0% { stroke-dashoffset: -82; }
+    100% { stroke-dashoffset: -246; }
+}

+ 16 - 0
home/src/pages/components/Section.js

@@ -0,0 +1,16 @@
+import React from 'react'
+
+import styles from './Section.module.css'
+
+export default function Section({ isDark, children }) {
+    return (
+        <section className={[styles.section, ...(isDark ? [styles.darkSection, 'darkSection'] : [])].join(' ')}>
+            <div className="container">
+                <div className="row">
+                    {children}
+                </div>
+            </div>
+        </section>
+    )
+}
+

+ 19 - 0
home/src/pages/components/Section.module.css

@@ -0,0 +1,19 @@
+.section {
+    padding: 4rem 0;
+    width: 100%;
+    background-color: var(--wdio-section-background-color);
+}
+
+.section h3 {
+    text-align: center;
+    font-size: 1.7em;
+    font-weight: 500;
+}
+
+.darkSection {
+    background-color: var(--wdio-dark-section-background-color);
+}
+
+html[data-theme=dark] {
+    background-color: var(--wdio-section-background-color);
+}

+ 61 - 0
home/src/pages/components/highlight.module.css

@@ -0,0 +1,61 @@
+.featureTitle {
+    font-size: 36px;
+    font-weight: 500;
+}
+
+.featureContent {
+    padding: var(--wdio-spacing-vertical) 0 var(--wdio-spacing-vertical) calc(var(--ifm-spacing-horizontal) + 20px)!important;
+}
+
+.featureContentReversed {
+    padding: var(--wdio-spacing-vertical) calc(var(--ifm-spacing-horizontal) + 20px) var(--wdio-spacing-vertical) var(--ifm-spacing-horizontal)!important;
+}
+
+.featureImage {
+    height: 100%;
+    padding: var(--wdio-spacing-vertical) var(--ifm-spacing-horizontal)!important;
+}
+
+.featureImage iframe {
+    width: 100%;
+}
+
+.darkSection {
+    background-color: var(--wdio-dark-section-background-color);
+}
+
+@media (max-width: 996px) {
+    .featureContent {
+        padding: var(--wdio-spacing-vertical) 0 calc(var(--wdio-spacing-vertical) * 4) calc(var(--ifm-spacing-horizontal) + 20px)!important;
+    }
+
+    .featureImage {
+        padding: calc(var(--wdio-spacing-vertical) * 4) var(--ifm-spacing-horizontal) var(--wdio-spacing-vertical)!important;
+    }
+
+    .featureContentReversed {
+        padding: calc(var(--wdio-spacing-vertical) * 4) calc(var(--ifm-spacing-horizontal) + 20px) var(--wdio-spacing-vertical) var(--ifm-spacing-horizontal)!important;
+    }
+
+    .featureImageReversed {
+        padding: var(--wdio-spacing-vertical) var(--ifm-spacing-horizontal) calc(var(--wdio-spacing-vertical) * 4)!important;
+    }
+}
+
+@media (max-width: 500px) {
+    .featureContent {
+        padding: var(--wdio-spacing-vertical) var(--ifm-spacing-horizontal) calc(var(--wdio-spacing-vertical) * 4) var(--ifm-spacing-horizontal)!important;
+    }
+
+    .featureImage {
+        padding: calc(var(--wdio-spacing-vertical) * 4) var(--ifm-spacing-horizontal) var(--wdio-spacing-vertical)!important;
+    }
+
+    .featureContentReversed {
+        padding: calc(var(--wdio-spacing-vertical) * 4) var(--ifm-spacing-horizontal) var(--wdio-spacing-vertical) var(--ifm-spacing-horizontal)!important;
+    }
+
+    .featureImageReversed {
+        padding: var(--wdio-spacing-vertical) var(--ifm-spacing-horizontal) calc(var(--wdio-spacing-vertical) * 4)!important;
+    }
+}

+ 79 - 0
home/src/pages/index.js

@@ -0,0 +1,79 @@
+import React from 'react'
+import clsx from 'clsx'
+import Layout from '@theme/Layout'
+import CodeBlock from '@theme/CodeBlock'
+import Link from '@docusaurus/Link'
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
+import useBaseUrl from '@docusaurus/useBaseUrl'
+import Translate, {translate} from '@docusaurus/Translate';
+
+import Feature from './components/Feature'
+import Section from './components/Section'
+import Highlight from './components/Highlight'
+import LogoCarousel from './components/LogoCarousel'
+import cdnTransfer from '../CdnTransfer'
+
+import styles from './styles.module.css'
+import { features, SetupExample, SurenessIntegration, friendLinks, mediaPartners } from '../constants'
+
+function Home() {
+    const context = useDocusaurusContext()
+    const {siteConfig = {}} = context
+    return (
+        <Layout
+            title={`${siteConfig.title} · ${siteConfig.tagline}`}
+            description={`${siteConfig.tagline}`}>
+            <header className={clsx('hero hero--primary', styles.heroBanner)}>
+                <div className="container">
+                    <h1 className="hero__title">
+                        <img style={{width: '500px', marginTop: '100px'}} src={cdnTransfer('img/tancloud-brand.svg')} alt={'#'}/>
+                    </h1>
+                    <p className="hero__subtitle"><Translate>易用友好的高性能监控云</Translate></p>
+                    <div className={styles.social}>
+                        <a href="https://console.tancloud.cn"><img src="https://img.shields.io/badge/网站监控-4EB1BA.svg" alt={''}/></a>
+                        <a href="https://console.tancloud.cn"><img src="https://img.shields.io/badge/PING连通性监控-blue.svg" alt={''}/></a>
+                        <a href="https://console.tancloud.cn"><img src="https://img.shields.io/badge/端口可用性监控-green.svg" alt={''}/></a>
+                        <a href="https://console.tancloud.cn"><img src="https://img.shields.io/badge/数据库监控-yellow.svg" alt={''}/></a>
+                        <a href="https://console.tancloud.cn"><img src="https://img.shields.io/badge/自定义监控-orange.svg" alt={''}/></a>
+                        <a href="https://console.tancloud.cn"><img src="https://img.shields.io/badge/阈值告警-red.svg" alt={''}/></a>
+                        <a href="https://console.tancloud.cn"><img src="https://img.shields.io/badge/告警转发通知-blueviolet.svg" alt={''}/></a>
+                    </div>
+                    <div className={styles.buttons}>
+                        <Link
+                            to="https://console.tancloud.cn"
+                            className={clsx(
+                                'button button--outline button--secondary button--lg',
+                                styles.getStarted,
+                            )}>
+                            <Translate>即刻登陆免费使用</Translate>
+                        </Link>
+                    </div>
+                </div>
+            </header>
+            <main>
+                {features && features.length > 0 && (
+                    <Section isDark>
+                        {features.map((props, idx) => (
+                            <Feature key={idx} {...props} />
+                        ))}
+                    </Section>
+                )}
+                {/*who is using*/}
+                {/*<Section>*/}
+                {/*    <LogoCarousel logos={logos}></LogoCarousel>*/}
+                {/*</Section>*/}
+
+                {/*Friend Links*/}
+                {/*<Section>*/}
+                {/*    <LogoCarousel logos={friendLinks} headerTitle={translate({message: 'Friend Links'})}></LogoCarousel>*/}
+                {/*</Section>*/}
+                {/*/!*Media Partners*!/*/}
+                {/*<Section>*/}
+                {/*    <LogoCarousel logos={mediaPartners} headerTitle={translate({message: 'Media Partners'})}></LogoCarousel>*/}
+                {/*</Section>*/}
+            </main>
+        </Layout>
+    )
+}
+
+export default Home

+ 101 - 0
home/src/pages/styles.module.css

@@ -0,0 +1,101 @@
+/* stylelint-disable docusaurus/copyright-header */
+
+/**
+ * CSS files with the .module.css suffix will be treated as CSS modules
+ * and scoped locally.
+ */
+
+.heroBanner {
+    padding: 1rem 0 5rem;
+    text-align: center;
+    position: relative;
+    overflow: hidden;
+}
+
+@media screen and (max-width: 966px) {
+    .heroBanner {
+        padding: 2rem;
+    }
+}
+
+.buttons {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    justify-content: center;
+    margin: 30px 0 0;
+}
+
+.buttons > a {
+    margin: 0 5px;
+}
+
+.frameworkLogos {
+    background-color: transparent!important;
+    border-bottom: 0!important;
+}
+.frameworkLogos img {
+    height: 60px;
+    padding: 0 10px 0 0;
+}
+
+.reactImage {
+    max-width: 115%;
+    transform: translateX(-45px);
+}
+
+.social {
+    margin-top: 20px;
+}
+
+.social > a {
+    margin-left: 10px;
+}
+
+@media (max-width: 650px) {
+    .buttons > a {
+        margin: 10px 5px;
+    }
+
+    .frameworkLogos img {
+        height: 45px;
+    }
+}
+
+.tweetBtn {
+    position: relative;
+    height: 20px;
+    box-sizing: border-box;
+    padding: 2px 8px 4px 22px;
+    background-color: #1b95e0;
+    color: #fff;
+    border-radius: 3px;
+    cursor: pointer;
+    font-family: "Helvetica Neue", Arial, sans-serif;
+    font-weight: 900;
+    font-size: 11px;
+    font-stretch: 100%;
+    line-height: 18px;
+    margin-left: 3px;
+    margin-right: 20px;
+    text-decoration-thickness: auto;
+    vertical-align: top;
+    zoom: 1;
+}
+.tweetBtn:hover {
+    color: white;
+    text-decoration: none;
+    background-color: #0c7abf;
+}
+.tweetBtn::before {
+    content: '';
+    position: absolute;
+    top: 3px;
+    left: 5px;
+    display: inline-block;
+    width: 14px;
+    height: 14px;
+    margin: 0 3px 0 0px;
+    background: transparent 0 0 no-repeat;
+    background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2072%2072%22%3E%3Cpath%20fill%3D%22none%22%20d%3D%22M0%200h72v72H0z%22%2F%3E%3Cpath%20class%3D%22icon%22%20fill%3D%22%23fff%22%20d%3D%22M68.812%2015.14c-2.348%201.04-4.87%201.744-7.52%202.06%202.704-1.62%204.78-4.186%205.757-7.243-2.53%201.5-5.33%202.592-8.314%203.176C56.35%2010.59%2052.948%209%2049.182%209c-7.23%200-13.092%205.86-13.092%2013.093%200%201.026.118%202.02.338%202.98C25.543%2024.527%2015.9%2019.318%209.44%2011.396c-1.125%201.936-1.77%204.184-1.77%206.58%200%204.543%202.312%208.552%205.824%2010.9-2.146-.07-4.165-.658-5.93-1.64-.002.056-.002.11-.002.163%200%206.345%204.513%2011.638%2010.504%2012.84-1.1.298-2.256.457-3.45.457-.845%200-1.666-.078-2.464-.23%201.667%205.2%206.5%208.985%2012.23%209.09-4.482%203.51-10.13%205.605-16.26%205.605-1.055%200-2.096-.06-3.122-.184%205.794%203.717%2012.676%205.882%2020.067%205.882%2024.083%200%2037.25-19.95%2037.25-37.25%200-.565-.013-1.133-.038-1.693%202.558-1.847%204.778-4.15%206.532-6.774z%22%2F%3E%3C%2Fsvg%3E);
+}

+ 17 - 0
home/src/sw.js

@@ -0,0 +1,17 @@
+import { registerRoute } from 'workbox-routing'
+import { StaleWhileRevalidate } from 'workbox-strategies'
+
+export default function swCustom(params) {
+    if (params.debug) {
+        console.log('[WebdriverIO-PWA][SW]: running swCustom code', params)
+    }
+
+    // Cache responses from external resources
+    registerRoute((context) => {
+        return [
+            /graph\.facebook\.com\/.*\/picture/,
+            /netlify\.com\/img/,
+            /avatars1\.githubusercontent/,
+        ].some((regex) => context.url.href.match(regex))
+    }, new StaleWhileRevalidate())
+}

+ 54 - 0
home/src/theme/card.jsx

@@ -0,0 +1,54 @@
+import React from 'react'
+
+function TwitterLink ({ url }) {
+    if (!url) {
+        return null
+    }
+
+    return (
+        <a href={ url } className="twitterProfile">
+            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 273.5 222.3" className="octicon"><path d="M273.5 26.3a109.77 109.77 0 0 1-32.2 8.8 56.07 56.07 0 0 0 24.7-31 113.39 113.39 0 0 1-35.7 13.6 56.1 56.1 0 0 0-97 38.4 54 54 0 0 0 1.5 12.8A159.68 159.68 0 0 1 19.1 10.3a56.12 56.12 0 0 0 17.4 74.9 56.06 56.06 0 0 1-25.4-7v.7a56.11 56.11 0 0 0 45 55 55.65 55.65 0 0 1-14.8 2 62.39 62.39 0 0 1-10.6-1 56.24 56.24 0 0 0 52.4 39 112.87 112.87 0 0 1-69.7 24 119 119 0 0 1-13.4-.8 158.83 158.83 0 0 0 86 25.2c103.2 0 159.6-85.5 159.6-159.6 0-2.4-.1-4.9-.2-7.3a114.25 114.25 0 0 0 28.1-29.1" fill="currentColor"></path></svg>
+        </a>
+    )
+}
+
+function GitHubLink ({ url }) {
+    if (!url) {
+        return null
+    }
+
+    return (
+        <a href={url}
+            className="githubProfile"
+            target="_blank"
+            rel="noopener noreferrer"
+            aria-label="GitHub profile"
+        >
+        </a>
+    )
+}
+
+export default function Card ({
+    name,
+    avatar,
+    github,
+    twitter,
+    children
+}) {
+    return (
+        <div className="avatar teamProfile">
+            <img
+                className="avatar__photo avatar__photo--lg"
+                src={ avatar }
+            />
+            <div className="avatar__intro">
+                <h4 className="avatar__name">
+                    { name }
+                    <GitHubLink url={github && `https://github.com/${github}`} />
+                    <TwitterLink url={twitter && `https://twitter.com/${twitter}`} />
+                </h4>
+                <small className="avatar__subtitle">{ children }</small>
+            </div>
+        </div>
+    )
+}

+ 0 - 0
home/static/.nojekyll


File diff ditekan karena terlalu besar
+ 0 - 0
home/static/img/docs/PathRoleMatcher.svg


TEMPAT SAMPAH
home/static/img/docs/basicAuthPostmanUse.png


TEMPAT SAMPAH
home/static/img/docs/benchmark_cn.png


TEMPAT SAMPAH
home/static/img/docs/benchmark_en.png


TEMPAT SAMPAH
home/static/img/docs/bot.jpg


TEMPAT SAMPAH
home/static/img/docs/digestAuthUse.png


TEMPAT SAMPAH
home/static/img/docs/digestFlow.png


TEMPAT SAMPAH
home/static/img/docs/flow-cn.png


TEMPAT SAMPAH
home/static/img/docs/flow-en.png


File diff ditekan karena terlalu besar
+ 0 - 0
home/static/img/docs/hertzbeat-stru.svg


TEMPAT SAMPAH
home/static/img/docs/javalin/test1.PNG


TEMPAT SAMPAH
home/static/img/docs/javalin/test2.PNG


TEMPAT SAMPAH
home/static/img/docs/javalin/test3.PNG


TEMPAT SAMPAH
home/static/img/docs/javalin/test4.PNG


TEMPAT SAMPAH
home/static/img/docs/javalin/test5.PNG


TEMPAT SAMPAH
home/static/img/docs/javalin/test6.PNG


TEMPAT SAMPAH
home/static/img/docs/javalin/test7.PNG


TEMPAT SAMPAH
home/static/img/docs/javalin/test8.PNG


TEMPAT SAMPAH
home/static/img/docs/jwtPostmanUse.png


TEMPAT SAMPAH
home/static/img/docs/micronaut/error.png


TEMPAT SAMPAH
home/static/img/docs/micronaut/success.png


TEMPAT SAMPAH
home/static/img/docs/pay.png


File diff ditekan karena terlalu besar
+ 0 - 0
home/static/img/docs/sureness-core.svg


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini