mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-10 11:07:06 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40ea6a9396 | ||
|
|
78d724ea83 | ||
|
|
eadbdc4e30 | ||
|
|
b8831317e9 |
89
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
89
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,89 +0,0 @@
|
||||
name: SuperSonic Bug report
|
||||
title: "[Bug] "
|
||||
description: Problems and issues with code of SuperSonic
|
||||
labels: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you very much for submitting feedback to SuperSonic to help SuperSonic develop better.
|
||||
|
||||
If it is an idea or help wanted, please go to:
|
||||
[Github Discussion](https://github.com/tencentmusic/supersonic/discussions)
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Search before asking
|
||||
description: >
|
||||
Please make sure to search in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) first to see
|
||||
whether the same issue was reported already.
|
||||
options:
|
||||
- label: >
|
||||
I had searched in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) and found no similar
|
||||
issues.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Version
|
||||
description: What is the current version
|
||||
placeholder: >
|
||||
Please provide the version you are using.
|
||||
If it is the trunk version, please input commit id.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What's Wrong?
|
||||
description: Describe the bug.
|
||||
placeholder: >
|
||||
Describe the specific problem, the more detailed the better.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What You Expected?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: How to Reproduce?
|
||||
placeholder: >
|
||||
Please try to give reproducing steps to facilitate quick location of the problem.
|
||||
|
||||
- What actions were performed
|
||||
- Table building statement
|
||||
- Import statement
|
||||
- Cluster information: number of nodes, configuration, etc.
|
||||
|
||||
If it is hard to reproduce, please also explain the general scene.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Anything Else?
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Are you willing to submit PR?
|
||||
description: >
|
||||
We very much look forward to developers or users to help solve the SuperSonic problem together.
|
||||
If you are willing to submit a PR to fix this problem, please tick it.
|
||||
options:
|
||||
- label: Yes I am willing to submit a PR!
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it.
|
||||
options:
|
||||
- label: >
|
||||
I agree to follow this project's
|
||||
[Code of Conduct](https://www.apache.org/foundation/policies/conduct)
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Thanks for completing our form!"
|
||||
57
.github/ISSUE_TEMPLATE/enhancement_request.yml
vendored
57
.github/ISSUE_TEMPLATE/enhancement_request.yml
vendored
@@ -1,57 +0,0 @@
|
||||
name: SuperSonic enhancement request
|
||||
description: Add an enhancement for SuperSonic
|
||||
title: "[Enhancement] "
|
||||
labels: enhancement
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you very much for your good enhancement for SuperSonic.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Search before asking
|
||||
description: >
|
||||
Please make sure to search in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) first to see
|
||||
whether the same issue was reported already.
|
||||
options:
|
||||
- label: >
|
||||
I had searched in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) and found no similar
|
||||
issues.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Describe the enhancement what you want, including motivation if it exists.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Solution
|
||||
placeholder: >
|
||||
Add overview of proposed solution.
|
||||
|
||||
Add related materials like links if they exist.
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Are you willing to submit PR?
|
||||
description: >
|
||||
We very much look forward to developers or users to help develop the SuperSonic together.
|
||||
If you are willing to submit a PR to implement this feature, please tick it.
|
||||
options:
|
||||
- label: Yes I am willing to submit a PR!
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it.
|
||||
options:
|
||||
- label: >
|
||||
I agree to follow this project's
|
||||
[Code of Conduct](https://www.apache.org/foundation/policies/conduct)
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Thanks for completing our form!"
|
||||
60
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
60
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,60 +0,0 @@
|
||||
name: SuperSonic feature request
|
||||
description: Suggest an idea for SuperSonic
|
||||
title: "[Feature] "
|
||||
labels: feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you very much for your good ideas and suggestions for SuperSonic
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Search before asking
|
||||
description: >
|
||||
Please make sure to search in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) first to see
|
||||
whether the same issue was reported already.
|
||||
options:
|
||||
- label: >
|
||||
I had searched in the [issues](https://github.com/tencentmusic/supersonic/issues?q=is%3Aissue) and found no similar
|
||||
issues.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Describe your ideas and needs.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Use case
|
||||
placeholder: >
|
||||
What problem does this feature mainly solve, or what scenarios it is suitable for.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Related issues
|
||||
description: Is there currently another issue associated with this?
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Are you willing to submit PR?
|
||||
description: >
|
||||
We very much look forward to developers or users to help develop the SuperSonic together.
|
||||
If you are willing to submit a PR to implement this feature, please tick it.
|
||||
options:
|
||||
- label: Yes I am willing to submit a PR!
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it.
|
||||
options:
|
||||
- label: >
|
||||
I agree to follow this project's
|
||||
[Code of Conduct](https://www.apache.org/foundation/policies/conduct)
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "Thanks for completing our form!"
|
||||
58
.github/ISSUE_TEMPLATE/question_request.yml
vendored
58
.github/ISSUE_TEMPLATE/question_request.yml
vendored
@@ -1,58 +0,0 @@
|
||||
name: SuperSonic question request
|
||||
description: Ask a question about SuperSonic
|
||||
title: "[question] "
|
||||
labels: question
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Ask a Question about SuperSonic
|
||||
Please provide a detailed description of your question or the clarification you seek regarding the SuperSonic project.
|
||||
- type: textarea
|
||||
id: describe-question
|
||||
attributes:
|
||||
label: Describe your question
|
||||
description: Please provide a clear and concise description of your question.
|
||||
placeholder: "Type your question here..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Provide any additional context or information
|
||||
description: If your question is related to a specific part of the SuperSonic project or if you have already looked through certain documentation, please provide that information here.
|
||||
placeholder: "Add context here..."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: tried-to-resolve
|
||||
attributes:
|
||||
label: What have you tried to resolve your question
|
||||
description: Let us know what you have done to try and understand or resolve your question. This can help us provide you with the most useful guidance.
|
||||
placeholder: "I've already tried..."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Your environment
|
||||
description: Share details about your environment to help us reproduce the issue. Include your operating system, version of SuperSonic, and any other relevant details.
|
||||
placeholder: "OS, SuperSonic version, etc..."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: screenshots-logs
|
||||
attributes:
|
||||
label: Screenshots or Logs
|
||||
description: If applicable, add screenshots or logs to help explain your problem.
|
||||
placeholder: "Paste your logs or attach screenshots here..."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: additional-information
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Add any other context or details you think might be helpful for understanding your question.
|
||||
placeholder: "Any other information..."
|
||||
validations:
|
||||
required: false
|
||||
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,36 +0,0 @@
|
||||
# Pull Request Template
|
||||
|
||||
## Description
|
||||
|
||||
Please include a summary of the change and which issue is fixed. Also include relevant motivation and context. List any dependencies that are required for this change.
|
||||
|
||||
## Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
## How Has This Been Tested?
|
||||
|
||||
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration.
|
||||
|
||||
- [ ] Test A
|
||||
- [ ] Test B
|
||||
|
||||
## Checklist:
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
||||
- [ ] Any dependent changes have been merged and published in downstream modules
|
||||
|
||||
## Additional information
|
||||
|
||||
Any additional information, configuration or data that might be necessary to reproduce the issue.
|
||||
64
.github/workflows/centos-ci.yml
vendored
64
.github/workflows/centos-ci.yml
vendored
@@ -1,64 +0,0 @@
|
||||
name: supersonic CentOS CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: quay.io/centos/centos:stream8 # 使用 CentOS Stream 8 容器
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: [8, 11, 21] # 定义要测试的JDK版本
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Reset DNF repositories
|
||||
run: |
|
||||
cd /etc/yum.repos.d/
|
||||
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
|
||||
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
|
||||
|
||||
- name: Update DNF package index
|
||||
run: dnf makecache
|
||||
|
||||
- name: Install Java and Maven with retry
|
||||
run: |
|
||||
if [ ${{ matrix.java-version }} -eq 8 ]; then
|
||||
for i in {1..5}; do
|
||||
dnf install -y java-1.8.0-openjdk-devel maven && break || sleep 15
|
||||
done
|
||||
elif [ ${{ matrix.java-version }} -eq 11 ]; then
|
||||
for i in {1..5}; do
|
||||
dnf install -y java-11-openjdk-devel maven && break || sleep 15
|
||||
done
|
||||
elif [ ${{ matrix.java-version }} -eq 21 ]; then
|
||||
for i in {1..5}; do
|
||||
dnf install -y java-21-openjdk-devel maven && break || sleep 15
|
||||
done
|
||||
fi
|
||||
|
||||
- name: Verify Java and Maven installation
|
||||
run: |
|
||||
java -version
|
||||
mvn -version
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
|
||||
- name: Test with Maven
|
||||
run: mvn test
|
||||
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: supersonic CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'adopt'
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
- name: Test with Maven
|
||||
run: mvn test
|
||||
39
.github/workflows/mac-ci.yml
vendored
39
.github/workflows/mac-ci.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: supersonic mac CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest # Specify a macOS runner
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: [8, 11, 21] # Define the JDK versions to test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/Library/Caches/Maven # macOS Maven cache path
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
|
||||
- name: Test with Maven
|
||||
run: mvn test
|
||||
39
.github/workflows/ubuntu-ci.yml
vendored
39
.github/workflows/ubuntu-ci.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: supersonic ubuntu CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: [8, 11, 21] # 定义要测试的JDK版本
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
|
||||
- name: Test with Maven
|
||||
run: mvn test
|
||||
39
.github/workflows/windows-ci.yml
vendored
39
.github/workflows/windows-ci.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: supersonic windows CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest # Specify a Windows runner
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: [8, 11, 21] # Add JDK 21 to the matrix
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
distribution: 'adopt' # You might need to change this if 'adopt' doesn't support JDK 21
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~\.m2 # Windows uses a backslash for paths
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
|
||||
- name: Test with Maven
|
||||
run: mvn test
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,7 +8,6 @@ log/
|
||||
*.bin
|
||||
*.log
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.lib
|
||||
assembly/runtime/*
|
||||
**/dist/
|
||||
@@ -18,5 +17,4 @@ assembly/runtime/*
|
||||
**/.flattened-pom.xml
|
||||
chm_db/
|
||||
__pycache__/
|
||||
/dict
|
||||
assembly/build/*-SNAPSHOT
|
||||
/dict
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -4,38 +4,6 @@
|
||||
- "Breaking Changes" describes any changes that may break existing functionality or cause
|
||||
compatibility issues with previous versions.
|
||||
|
||||
## SuperSonic [0.9.8] - 2024-11-01
|
||||
- Add LLM management module to reuse connection across agents.
|
||||
- Add ChatAPP configuration sub-module in Agent Management.
|
||||
- Add dimension value management sub-module.
|
||||
- Enhance memory management and term management sub-module.
|
||||
- Support semantic translation of complex S2SQL.
|
||||
- Introduce LLM-based semantic corrector and data interpreter.
|
||||
- Introduce new experience in Chat UI.
|
||||
|
||||
## SuperSonic [0.9.2] - 2024-06-01
|
||||
|
||||
### Added
|
||||
- support multiple rounds of dialogue
|
||||
- add term configuration and identification to help LLM learn private domain knowledge
|
||||
- support configuring LLM parameters in the agent
|
||||
- metric market supports searching in natural language
|
||||
|
||||
### Updated
|
||||
- introducing WorkFlow, Mapper, Parser, and Corrector support jump execution
|
||||
- Introducing the concept of Model-Set to simplify Domain management
|
||||
- overall optimization and upgrade of system pages
|
||||
- optimize startup script
|
||||
|
||||
## SuperSonic [0.9.0] - 2024-04-03
|
||||
|
||||
### Added
|
||||
- add tag abstraction and enhance tag marketplace management.
|
||||
- headless-server provides Chat API interface.
|
||||
|
||||
### Updated
|
||||
- migrate chat-core core component to headless-core.
|
||||
|
||||
## SuperSonic [0.8.6] - 2024-02-23
|
||||
|
||||
### Added
|
||||
|
||||
21
LICENSE
21
LICENSE
@@ -1,23 +1,3 @@
|
||||
SuperSonic is licensed under the MIT License, with the following additional conditions:
|
||||
|
||||
1. You may provide SuperSonic to third parties as a commercial software or service. However,
|
||||
when the following conditions are met, you must contact the producer to obtain a commercial license:
|
||||
|
||||
a. Multi-tenant SaaS service: Unless explicitly authorized by SuperSonic in writing, you may not use the
|
||||
SuperSonic source code to operate a multi-tenant SaaS service.
|
||||
b. LOGO and copyright information: In the process of using SuperSonic, you may not remove or modify
|
||||
the LOGO or copyright information on the SuperSonic UI. This restriction is inapplicable to uses of
|
||||
SuperSonic that do not involve its frontend components.
|
||||
|
||||
Please contact jerryjzhang@tencent.com by email to inquire about licensing matters.
|
||||
|
||||
2. As a contributor, you should agree that:
|
||||
|
||||
a. The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
|
||||
b. Your contributed code may be used for commercial purposes, including but not limited to its business operations.
|
||||
|
||||
Terms of the MIT License:
|
||||
--------------------------------------------------------------------
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Tencent Music Entertainment
|
||||
@@ -40,7 +20,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
||||
Other dependencies and licenses:
|
||||
|
||||
65
README.md
65
README.md
@@ -1,72 +1,53 @@
|
||||
[中文版](README_CN.md) | [日本語版](README_JP.md) | [Docs](https://supersonicbi.github.io/)
|
||||
[中文介绍](README_CN.md) | [文档中心](https://github.com/tencentmusic/supersonic/wiki)
|
||||
|
||||

|
||||
|
||||
# SuperSonic
|
||||
# SuperSonic (超音数)
|
||||
|
||||
SuperSonic is the next-generation BI platform that unifies **Chat BI** (powered by LLM) and **Headless BI** (powered by semantic layer) paradigms. This unification ensures that Chat BI has access to the same curated and governed semantic data models as traditional BI. Furthermore, the implementation of both paradigms benefit from each other:
|
||||
**SuperSonic is the next-generation LLM-powered data analytics platform that integrates ChatBI and HeadlessBI**. SuperSonic provides a chat interface that empowers users to query data using natural language and visualize the results with suitable charts. To enable such experience, the only thing necessary is to build logical semantic models (definition of entities/metrics/dimensions/tags, along with their meaning, context and relationships) on top of physical data models, and **no data modification or copying** is required. Meanwhile, SuperSonic is designed to be **highly extensible**, allowing custom functionalities to be added and configured with Java SPI.
|
||||
|
||||
- Chat BI's Text2SQL gets augmented with context-retrieval from semantic models.
|
||||
- Headless BI's query interface gets extended with natural language API.
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_ideas.png" height="75%" width="75%" />
|
||||
|
||||
SuperSonic provides a **Chat BI interface** that empowers users to query data using natural language and visualize the results with suitable charts. To enable such experience, the only thing necessary is to build logical semantic models (definition of metric/dimension/tag, along with their meaning and relationships) through a **Headless BI interface**. Meanwhile, SuperSonic is designed to be extensible and composable, allowing custom implementations to be added and configured with Java SPI.
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_demo.gif" height="100%" width="100%" />
|
||||
<img src="./docs/images/supersonic_demo.gif" height="100%" width="100%" align="center"/>
|
||||
|
||||
## Motivation
|
||||
|
||||
The emergence of Large Language Model (LLM) like ChatGPT is reshaping the way information is retrieved, leading to a new paradigm in the field of data analytics known as Chat BI. To implement Chat BI, both academia and industry are primarily focused on harnessing the power of LLMs to convert natural language into SQL, commonly referred to as Text2SQL or NL2SQL. While some approaches show promising results, their **reliability** falls short for large-scale real-world applications.
|
||||
The emergence of Large Language Model (LLM) like ChatGPT is reshaping the way information is retrieved. In the field of data analytics, both academia and industry are primarily focused on leveraging LLM to convert natural language into SQL (so called Text2SQL or NL2SQL). While some approaches exhibit promising results, their **reliability** and **efficiency** are insufficient for real-world applications.
|
||||
|
||||
Meanwhile, another emerging paradigm called Headless BI, which focuses on constructing unified semantic data models, has garnered significant attention. Headless BI is implemented through a universal semantic layer that exposes consistent data semantics via an open API.
|
||||
From our perspective, the key to filling the real-world gap lies in three aspects:
|
||||
1. Integrate ChatBI with HeadlessBI encapsulating underlying data context (joins, keys, formulas, etc) to **reduce complexity**.
|
||||
2. Augment the LLM with schema mappers(as a kind of preprocessor) and semantic correctors(as a kind of postprocessor) to **mitigate hallucination**.
|
||||
3. Utilize rule-based schema parsers when necessary to **improve efficiency**(in terms of latency and cost).
|
||||
|
||||
From our perspective, the integration of Chat BI and Headless BI has the potential to enhance the Text2SQL generation in two dimensions:
|
||||
|
||||
1. Incorporate data semantics (such as business terms, column values, etc.) into the prompt, enabling LLM to better understand the semantics and **reduce hallucination**.
|
||||
2. Offload the generation of advanced SQL syntax (such as join, formula, etc.) from LLM to the semantic layer to **reduce complexity**.
|
||||
|
||||
With these ideas in mind, we develop SuperSonic as a practical reference implementation and use it to power our real-world products. Additionally, to facilitate further development we decide to open source SuperSonic as an extensible framework.
|
||||
With these ideas in mind, we develop SuperSonic as a practical reference implementation and use it to power our real-world products. Additionally, to facilitate further development of ChatBI, we decide to open source SuperSonic as an extensible framework.
|
||||
|
||||
## Out-of-the-box Features
|
||||
|
||||
- Built-in Chat BI interface for *business users* to enter natural language queries
|
||||
- Built-in Headless BI interface for *analytics engineers* to build semantic data models
|
||||
- Built-in rule-based semantic parser to improve efficiency in certain scenarios (e.g. demonstration, integration testing)
|
||||
- Built-in support for input auto-completion, multi-turn conversation as well as post-query recommendation
|
||||
- Built-in support for three-level data access control: dataset-level, column-level and row-level
|
||||
- Built-in ChatBI interface for *business users* to enter natural language queries
|
||||
- Built-in HeadlessBI interface for *analytics engineers* to build semantic models
|
||||
- Built-in GUI for *system administrators* to manage chat agents and third-party plugins
|
||||
- Support input auto-completion as well as query recommendation
|
||||
- Support multi-turn conversation and history context management
|
||||
- Support four-level permission control: domain-level, model-level, column-level and row-level
|
||||
|
||||
## Extensible Components
|
||||
|
||||
The high-level architecture and main process flow is as follows:
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_components.png" height="65%" width="65%" />
|
||||
<img src="./docs/images/supersonic_components.png" height="65%" width="65%" align="center"/>
|
||||
|
||||
- **Knowledge Base:** extracts schema information periodically from the semantic models and build dictionary and index to facilitate schema mapping.
|
||||
|
||||
- **Schema Mapper:** identifies references to schema elements(metrics/dimensions/entities/values) in user queries. It matches the query text against the knowledge base.
|
||||
|
||||
- **Semantic Parser:** understands user queries and generates semantic query statement. It consists of a combination of rule-based and LLM-based parsers, each of which deals with specific scenarios.
|
||||
- **Semantic Parser:** understands user queries and extracts semantic information. It consists of a combination of rule-based and model-based parsers, each of which deals with specific scenarios.
|
||||
|
||||
- **Semantic Corrector:** checks validity of semantic query statement and performs correction if necessary. It consists of a combination of rule-based and LLM-based correctors, each of which deals with specific scenarios.
|
||||
- **Semantic Corrector:** checks validity of extracted semantic information and performs correction and optimization if needed.
|
||||
|
||||
- **Semantic Translator:** converts semantic query statement into SQL statement that can be executed against physical data models.
|
||||
- **Semantic Interpreter:** performs execution according to extracted semantic information. It generates SQL statements and executes them against physical data models.
|
||||
|
||||
- **Chat Plugin:** extends functionality with third-party tools. Given a list of configured plugins with descriptions and sample questions, an LLM will be leveraged to select the most suitable one.
|
||||
|
||||
- **Chat Memory:** encapsulates a collection of historical query trajectories that can be recalled to facilitate few-shot prompting.
|
||||
- **Chat Plugin:** extends functionality with third-party tools. The LLM is going to select the most suitable one, given all configured plugins with function description and sample questions.
|
||||
|
||||
## Quick Demo
|
||||
### Online playground
|
||||
Visit http://117.72.46.148:9080 to register and experience as a new user. Please do not modify system configurations. We will restart to reset configurations regularly every weekend.
|
||||
|
||||
### Docker Deployment
|
||||
- Install Docker and docker-compose.
|
||||
- Download the docker-compose.yml file; Execute: wget https://raw.githubusercontent.com/tencentmusic/supersonic/master/docker/docker-compose.yml.
|
||||
- Execute "docker-compose up -d".
|
||||
- Open a browser and visit http://localhost:9080 to start exploring.
|
||||
|
||||
### Local build
|
||||
SuperSonic comes with sample semantic models as well as chat conversations that can be used as a starting point. Please follow the steps:
|
||||
|
||||
- Download the latest prebuilt binary from the [release page](https://github.com/tencentmusic/supersonic/releases)
|
||||
@@ -75,10 +56,10 @@ SuperSonic comes with sample semantic models as well as chat conversations that
|
||||
|
||||
## Build and Development
|
||||
|
||||
Please refer to project [Docs](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA/).
|
||||
Please refer to project [wiki](https://github.com/tencentmusic/supersonic/wiki).
|
||||
|
||||
## WeChat Contact
|
||||
|
||||
Please follow SuperSonic wechat official account:
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_wechat_oa.png" height="50%" width="50%" />
|
||||
<img src="./docs/images/supersonic_wechat_oa.png" height="50%" width="50%" align="center"/>
|
||||
63
README_CN.md
63
README_CN.md
@@ -1,72 +1,49 @@
|
||||
[English](README.md) | [日本語版](README_JP.md) | [文档中心](https://supersonicbi.github.io/)
|
||||
# SuperSonic (超音数)
|
||||
|
||||
# SuperSonic
|
||||
**SuperSonic融合ChatBI和HeadlessBI打造新一代的数据分析平台**。通过SuperSonic的问答对话界面,用户能够使用自然语言查询数据,系统会选择合适的可视化图表呈现结果。SuperSonic不需要修改或复制数据,只需要在物理数据模型之上构建逻辑语义模型(指标/维度/实体的定义,以及他们的业务含义、相互间关系等),即可开启数据问答体验。与此同时,SuperSonic被设计为可插拔的框架,采用Java SPI机制来扩展定制功能。
|
||||
|
||||
**SuperSonic融合Chat BI(powered by LLM)和Headless BI(powered by 语义层)打造新一代的BI平台**。这种融合确保了Chat BI能够与传统BI一样访问统一化治理的语义数据模型。此外,两种BI新范式都从中获得收益:
|
||||
|
||||
- Chat BI的Text2SQL生成通过检索语义数据模型得到增强。
|
||||
- Headless BI的查询接口通过支持自然语言API得到拓展。
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_ideas.png" height="75%" width="75%" />
|
||||
|
||||
通过SuperSonic的问答对话界面,用户能够使用自然语言查询数据,系统会选择合适的可视化图表呈现结果。SuperSonic不需要修改或复制数据,只需要在物理数据模型之上构建逻辑语义模型(定义指标/维度/实体/标签,以及它们的业务含义、相互关系等),即可开启数据问答体验。与此同时,SuperSonic被设计为可插拔的框架,采用Java SPI机制来扩展定制功能。
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_demo.gif" height="100%" width="100%" />
|
||||
<img src="./docs/images/supersonic_demo.gif" height="100%" width="100%" align="center"/>
|
||||
|
||||
## 项目动机
|
||||
|
||||
大型语言模型(LLM)如ChatGPT的出现正在重塑信息检索的方式,引领数据分析领域的一种新范式,被称为Chat BI。为了实现Chat BI,学术界和工业界主要关注利用LLM的能力将自然语言转换为SQL,通常称为Text2SQL或NL2SQL。尽管一些方法显示出有希望的结果,但它们在大规模实际应用中的可靠性还不足。
|
||||
大型语言模型(LLMs)如ChatGPT的出现正在重塑信息检索的方式。在数据分析领域,学术界和工业界主要关注利用深度学习模型将自然语言查询转换为SQL查询。虽然一些工作显示出有前景的结果,但它们的可靠性还达不到生产可用的要求。
|
||||
|
||||
与此同时,另一种新兴范式被称为Headless BI,它专注于构建统一的语义数据模型,并引起了广泛的关注。Headless BI通过一个通用的语义层来实现,通过开放的API公开一致的数据语义。
|
||||
|
||||
从我们的角度来看,Chat BI和Headless BI的融合有潜力在两个方面增强Text2SQL的能力:
|
||||
|
||||
1. 将数据语义(如业务术语、列值等)纳入提示词中,使LLM能够更好地理解语义,以**减少幻觉**。
|
||||
2. 将高级SQL语法(如连接、公式等)的生成从LLM卸载到语义层,以**减少复杂度**。
|
||||
在我们看来,为了在实际场景发挥价值,有三个关键点:
|
||||
1. 融合HeadlessBI,通过统一语义层封装底层数据细节(关联、键值、公式等),降低SQL生成的**复杂度**。
|
||||
2. 通过一前一后的模式映射器和语义修正器,来缓解LLM常见的**幻觉**现象。
|
||||
3. 设计启发式的规则,在一些特定场景提升语义解析的**效率**。
|
||||
|
||||
为了验证上述想法,我们开发了SuperSonic项目,并将其应用在实际的内部产品中。与此同时,我们将SuperSonic作为一个可扩展的框架开源,希望能够促进数据问答对话领域的进一步发展。
|
||||
|
||||
## 开箱即用的特性
|
||||
|
||||
- 内置Chat BI界面以便*业务用户*输入数据查询。
|
||||
- 内置Headless BI界面以便*分析工程师*构建语义模型。
|
||||
- 内置基于规则的语义解析器,在特定场景(比如DEMO演示、集成测试)可以提升推理效率。
|
||||
- 支持文本输入联想、多轮对话、查询后问题推荐等高级特征。
|
||||
- 支持三级权限控制:数据集级、列级、行级。
|
||||
- 内置ChatBI界面以便*业务用户*输入数据查询。
|
||||
- 内置HeadlessBI界面以便*分析工程师*构建语义模型。
|
||||
- 内置图形用户界面以便*系统管理员*管理第三方插件和对话助理。
|
||||
- 支持文本输入的联想和查询问题的推荐。
|
||||
- 支持多轮对话,根据语境自动切换上下文。
|
||||
- 支持四级权限控制:主题域级、模型级、列级、行级。
|
||||
|
||||
## 易于扩展的组件
|
||||
|
||||
SuperSonic的整体架构和主流程如下图所示:
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_components.png" height="65%" width="65%" />
|
||||
<img src="./docs/images/supersonic_components.png" height="65%" width="65%" align="center"/>
|
||||
|
||||
- **模型知识库(Knowledge Base):** 定期从语义模型中提取相关的模式信息,构建词典和索引,以便后续的模式映射。
|
||||
|
||||
- **模式映射器(Schema Mapper):** 将自然语言文本在知识库中进行匹配,为后续的语义解析提供相关信息。
|
||||
|
||||
- **语义解析器(Semantic Parser):** 理解用户查询并抽取语义信息,生成语义查询语句S2SQL。
|
||||
- **语义解析器(Semantic Parser):** 理解用户查询并抽取语义信息,其由一组基于规则和基于模型的解析器组成,每个解析器可应对不同的特定场景。
|
||||
|
||||
- **语义修正器(Semantic Corrector):** 检查语义查询语句的合法性,对不合法的信息做修正和优化处理。
|
||||
- **语义修正器(Semantic Corrector):** 检查语义信息的合法性,对不合法的信息做修正和优化处理。
|
||||
|
||||
- **语义翻译器(Semantic Translator):** 将语义查询语句翻译成可在物理数据模型上执行的SQL语句。
|
||||
- **语义解释器(Semantic Interpreter):** 根据语义信息生成物理SQL执行查询。
|
||||
|
||||
- **问答插件(Chat Plugin):** 通过第三方工具扩展功能。给定所有配置的插件及其功能描述和示例问题,大语言模型将选择最合适的插件。
|
||||
|
||||
- **问答记忆(Chat Memory):** 将历史的查询轨迹进行封装,可被召回作为few-shot样例嵌入提示词。
|
||||
|
||||
## 快速体验
|
||||
|
||||
### 线上环境体验
|
||||
访问http://117.72.46.148:9080 注册新用户体验. 请勿修改系统配置。我们每周末定期重启重置配置。
|
||||
|
||||
### Docker部署
|
||||
- 安装好Docker以及docker-compose
|
||||
- 下载docker-compose.yml;执行命令:wget https://raw.githubusercontent.com/tencentmusic/supersonic/master/docker/docker-compose.yml
|
||||
- 执行:"docker-compose up -d"
|
||||
- 在浏览器访问http://localhost:9080 开启探索
|
||||
|
||||
### 本地构建
|
||||
|
||||
SuperSonic自带样例的语义模型和问答对话,只需以下三步即可快速体验:
|
||||
|
||||
- 从[release page](https://github.com/tencentmusic/supersonic/releases)下载预先构建好的发行包
|
||||
@@ -75,10 +52,10 @@ SuperSonic自带样例的语义模型和问答对话,只需以下三步即可
|
||||
|
||||
## 如何构建和部署
|
||||
|
||||
请参考项目[文档](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA/)。
|
||||
请参考项目[wiki](https://github.com/tencentmusic/supersonic/wiki)。
|
||||
|
||||
## 微信联系方式
|
||||
|
||||
欢迎关注微信公众号:
|
||||
|
||||
<img src="https://github.com/supersonicbi/supersonic-website/blob/main/static/img/supersonic_wechat_oa.png" height="50%" width="50%" />
|
||||
<img src="./docs/images/supersonic_wechat_oa.png" height="50%" width="50%" align="center"/>
|
||||
80
README_JP.md
80
README_JP.md
@@ -1,80 +0,0 @@
|
||||
[英文版](README.md) | [中国語版](README_CN.md)
|
||||
|
||||
# SuperSonic
|
||||
|
||||
**SuperSonicは、LLM(大規模言語モデル)によるChat BIと、セマンティックレイヤーによるHeadless BIを統合した次世代のBIプラットフォームです。** この統合により、Chat BIは従来のBIと同様に、統一されたガバナンスされたセマンティックデータモデルにアクセスできます。さらに、両方のBIパラダイムは統合から恩恵を受けます:
|
||||
|
||||
- Chat BIのText2SQLは、セマンティックモデルからのコンテキスト検索によって強化されます。
|
||||
- Headless BIのクエリインターフェースは、自然言語APIによって拡張されます。
|
||||
|
||||

|
||||
|
||||
SuperSonicは、ユーザーが自然言語でデータをクエリし、適切なチャートで結果を視覚化できる**Chat BIインターフェース**を提供します。このような体験を実現するために必要なのは、**Headless BIインターフェース**を通じて論理的なセマンティックモデル(メトリック/ディメンション/タグの定義、それらの意味と関係など)を構築することだけです。同時に、SuperSonicは拡張可能で構成可能な設計を採用しており、Java SPIを使用してカスタム実装を追加および設定できます。
|
||||
|
||||

|
||||
|
||||
## プロジェクトの動機
|
||||
|
||||
ChatGPTのような大規模言語モデル(LLM)の出現は、情報検索の方法を再定義し、データ分析分野における新しいパラダイムであるChat BIをリードしています。Chat BIを実装するために、学術界と産業界は主に、自然言語をSQLに変換するLLMの能力を活用することに焦点を当てています。これは一般にText2SQLまたはNL2SQLと呼ばれます。一部のアプローチは有望な結果を示していますが、大規模な実世界のアプリケーションでの**信頼性**はまだ不十分です。
|
||||
|
||||
一方で、統一されたセマンティックデータモデルを構築することに焦点を当てた別の新興パラダイムであるHeadless BIも、大きな注目を集めています。Headless BIは、オープンAPIを介して一貫したデータセマンティクスを公開するユニバーサルセマンティックレイヤーを通じて実装されます。
|
||||
|
||||
私たちの観点から見ると、Chat BIとHeadless BIの統合は、Text2SQL生成を2つの側面で強化する可能性があります:
|
||||
|
||||
1. データセマンティクス(ビジネス用語、列の値など)をプロンプトに組み込むことで、LLMがセマンティクスをよりよく理解し、**幻覚を減らす**ことができます。
|
||||
2. 高度なSQL構文(結合、式など)の生成をLLMからセマンティックレイヤーにオフロードすることで、**複雑さを減らす**ことができます。
|
||||
|
||||
これらのアイデアを念頭に置いて、私たちはSuperSonicプロジェクトを開発し、実際の製品でそれを使用しています。同時に、SuperSonicを拡張可能なフレームワークとしてオープンソース化し、データクエリ対話分野のさらなる発展を促進したいと考えています。
|
||||
|
||||
## 初期設定で利用可能な機能
|
||||
|
||||
- *ビジネスユーザー*が自然言語クエリを入力できる組み込みのChat BIインターフェース。
|
||||
- *分析エンジニア*がセマンティックモデルを構築できる組み込みのHeadless BIインターフェース。
|
||||
- 特定のシナリオ(例:デモンストレーション、統合テスト)で推論効率を向上させるための組み込みのルールベースのセマンティックパーサー。
|
||||
- 入力自動補完、マルチターン会話、クエリ後の質問推奨などの高度な機能をサポート。
|
||||
- データセットレベル、列レベル、行レベルの3レベルのデータアクセス制御をサポート。
|
||||
|
||||
## 拡張可能なコンポーネント
|
||||
|
||||
高レベルのアーキテクチャとメインのプロセスフローは以下の通りです:
|
||||
|
||||

|
||||
|
||||
- **モデル知識ベース(Knowledge Base):** セマンティックモデルから定期的にスキーマ情報を抽出し、辞書とインデックスを構築して、スキーママッピングを容易にします。
|
||||
|
||||
- **スキーママッパー(Schema Mapper):** ユーザークエリ内のスキーマ要素(メトリック/ディメンション/エンティティ/値)を識別します。クエリテキストを知識ベースと照合します。
|
||||
|
||||
- **セマンティックパーサー(Semantic Parser):** ユーザークエリを理解し、セマンティッククエリステートメントS2SQLを生成します。
|
||||
|
||||
- **セマンティック修正器(Semantic Corrector):** セマンティッククエリステートメントの妥当性をチェックし、必要に応じて修正と最適化を行います。
|
||||
|
||||
- **セマンティックトランスレーター(Semantic Translator):** セマンティッククエリステートメントを、物理データモデル上で実行可能なSQLステートメントに変換します。
|
||||
|
||||
- **チャットプラグイン(Chat Plugin):** サードパーティツールで機能を拡張します。すべての設定されたプラグインとその機能説明、サンプル質問が与えられた場合、LLMは最も適切なプラグインを選択します。
|
||||
|
||||
## クイックデモ
|
||||
### オンラインプレイグラウンド
|
||||
http://117.72.46.148:9080 にアクセスして、新規ユーザーとして登録して体験してください。システム設定を変更しないでください。毎週末に定期的に再起動して設定をリセットします。
|
||||
|
||||
### Dockerのデプロイメント
|
||||
- Dockerおよびdocker-composeをインストールします。
|
||||
- docker-compose.ymlファイルをダウンロードします。コマンドを実行します:wget https://raw.githubusercontent.com/tencentmusic/supersonic/master/docker/docker-compose.yml。
|
||||
- docker-compose up -dを実行します。
|
||||
- ブラウザを開いてhttp://localhost:9080にアクセスし、探索を開始します。
|
||||
|
||||
### ローカルビルド
|
||||
SuperSonicには、サンプルのセマンティックモデルとチャット会話が付属しており、以下の手順で簡単に体験できます:
|
||||
|
||||
- [リリースページ](https://github.com/tencentmusic/supersonic/releases)から最新のプリビルドバイナリをダウンロード
|
||||
- スクリプト "assembly/bin/supersonic-daemon.sh start" を実行して、スタンドアロンJavaサービスを起動
|
||||
- ブラウザで http://localhost:9080 にアクセスして探索を開始
|
||||
|
||||
## ビルドと開発
|
||||
|
||||
プロジェクト[ドキュメント](https://supersonicbi.github.io/docs/%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2/%E7%BC%96%E8%AF%91%E6%9E%84%E5%BB%BA/)を参照してください。
|
||||
|
||||
## WeChat連絡先
|
||||
|
||||
SuperSonicの公式WeChatアカウントをフォローしてください:
|
||||
|
||||

|
||||
@@ -1,94 +1,72 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
setlocal
|
||||
chcp 65001
|
||||
|
||||
set "sbinDir=%~dp0"
|
||||
call %sbinDir%/supersonic-common.bat %*
|
||||
|
||||
set "baseDir=%~dp0.."
|
||||
set "buildDir=%baseDir%\build"
|
||||
set "runtimeDir=%baseDir%\..\runtime"
|
||||
set "pip_path=pip3"
|
||||
set "service=%~1"
|
||||
|
||||
cd %projectDir%
|
||||
if "%service%"=="" (
|
||||
set service=%standalone_service%
|
||||
|
||||
rem 1. build backend java modules
|
||||
del /q "%buildDir%\*.tar.gz" 2>NUL
|
||||
call mvn -f "%baseDir%\..\pom.xml" clean package -DskipTests
|
||||
|
||||
IF ERRORLEVEL 1 (
|
||||
ECHO Failed to build backend Java modules.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
call mvn help:evaluate -Dexpression=project.version > temp.txt
|
||||
for /f "delims=" %%i in (temp.txt) do (
|
||||
set line=%%i
|
||||
if not "!line:~0,1!"=="[" (
|
||||
set MVN_VERSION=!line!
|
||||
)
|
||||
)
|
||||
del temp.txt
|
||||
cd %baseDir%
|
||||
rem 2. move package to build
|
||||
echo f|xcopy "%baseDir%\..\launchers\standalone\target\*.tar.gz" "%buildDir%\supersonic-standalone.tar.gz"
|
||||
|
||||
rem 3. build frontend webapp
|
||||
cd "%baseDir%\..\webapp"
|
||||
call start-fe-prod.bat
|
||||
copy /y "%baseDir%\..\webapp\supersonic-webapp.tar.gz" "%buildDir%\"
|
||||
|
||||
if "%service%"=="webapp" (
|
||||
call :buildWebapp
|
||||
tar xvf supersonic-webapp.tar.gz
|
||||
move /y supersonic-webapp webapp
|
||||
move /y webapp %projectDir%\launchers\%STANDALONE_SERVICE%\target\classes
|
||||
goto :EOF
|
||||
) else (
|
||||
call :buildJavaService
|
||||
call :buildWebapp
|
||||
call :packageRelease
|
||||
goto :EOF
|
||||
IF ERRORLEVEL 1 (
|
||||
ECHO Failed to build frontend webapp.
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
rem 4. copy webapp to java classpath
|
||||
cd "%buildDir%"
|
||||
tar -zxvf supersonic-webapp.tar.gz
|
||||
move supersonic-webapp webapp
|
||||
move webapp ..\..\launchers\standalone\target\classes
|
||||
|
||||
:buildJavaService
|
||||
set "model_name=%service%"
|
||||
echo "starting building supersonic-%model_name% service"
|
||||
call mvn -f %projectDir% clean package -DskipTests -Dspotless.skip=true
|
||||
IF ERRORLEVEL 1 (
|
||||
ECHO Failed to build backend Java modules.
|
||||
EXIT /B 1
|
||||
)
|
||||
copy /y %projectDir%\launchers\%model_name%\target\*.tar.gz %buildDir%\
|
||||
echo "finished building supersonic-%model_name% service"
|
||||
goto :EOF
|
||||
rem 5. build backend python modules
|
||||
if "%service%"=="pyllm" (
|
||||
echo "start installing python modules with pip: ${pip_path}"
|
||||
set requirementPath="%baseDir%/../chat/python/requirements.txt"
|
||||
%pip_path% install -r %requirementPath%
|
||||
echo "install python modules success"
|
||||
)
|
||||
|
||||
call :BUILD_RUNTIME
|
||||
|
||||
:buildWebapp
|
||||
echo "starting building supersonic webapp"
|
||||
cd %projectDir%\webapp
|
||||
call start-fe-prod.bat
|
||||
copy /y supersonic-webapp.tar.gz %buildDir%\
|
||||
rem check build result
|
||||
IF ERRORLEVEL 1 (
|
||||
ECHO Failed to build frontend webapp.
|
||||
EXIT /B 1
|
||||
)
|
||||
echo "finished building supersonic webapp"
|
||||
goto :EOF
|
||||
:BUILD_RUNTIME
|
||||
rem 6. reset runtime
|
||||
IF EXIST "%runtimeDir%" (
|
||||
echo begin to delete dir : %runtimeDir%
|
||||
rd /s /q "%runtimeDir%"
|
||||
) ELSE (
|
||||
echo %runtimeDir% does not exist, create directly
|
||||
)
|
||||
mkdir "%runtimeDir%"
|
||||
tar -zxvf "%buildDir%\supersonic-standalone.tar.gz" -C "%runtimeDir%"
|
||||
for /d %%f in ("%runtimeDir%\launchers-standalone-*") do (
|
||||
move "%%f" "%runtimeDir%\supersonic-standalone"
|
||||
)
|
||||
|
||||
|
||||
:packageRelease
|
||||
set "model_name=%service%"
|
||||
set "release_dir=supersonic-%model_name%-%MVN_VERSION%"
|
||||
set "service_name=launchers-%model_name%-%MVN_VERSION%"
|
||||
echo "starting packaging supersonic release"
|
||||
cd %buildDir%
|
||||
if exist %release_dir% rmdir /s /q %release_dir%
|
||||
if exist %release_dir%.zip del %release_dir%.zip
|
||||
mkdir %release_dir%
|
||||
rem package webapp
|
||||
tar xvf supersonic-webapp.tar.gz
|
||||
move /y supersonic-webapp webapp
|
||||
echo {"env": ""} > webapp\supersonic.config.json
|
||||
move /y webapp %release_dir%
|
||||
rem package java service
|
||||
tar xvf %service_name%-bin.tar.gz
|
||||
for /d %%D in ("%service_name%\*") do (
|
||||
move "%%D" "%release_dir%"
|
||||
)
|
||||
rem generate zip file
|
||||
powershell Compress-Archive -Path %release_dir% -DestinationPath %release_dir%.zip
|
||||
del %service_name%-bin.tar.gz
|
||||
del supersonic-webapp.tar.gz
|
||||
rmdir /s /q %service_name%
|
||||
echo "finished packaging supersonic release"
|
||||
goto :EOF
|
||||
rem 7. copy webapp to runtime
|
||||
tar -zxvf "%buildDir%\supersonic-webapp.tar.gz" -C "%buildDir%"
|
||||
if not exist "%runtimeDir%\supersonic-standalone\webapp" mkdir "%runtimeDir%\supersonic-standalone\webapp"
|
||||
xcopy /s /e /h /y "%buildDir%\supersonic-webapp\*" "%runtimeDir%\supersonic-standalone\webapp"
|
||||
if not exist "%runtimeDir%\supersonic-standalone\conf\webapp" mkdir "%runtimeDir%\supersonic-standalone\conf\webapp"
|
||||
xcopy /s /e /h /y "%runtimeDir%\supersonic-standalone\webapp\*" "%runtimeDir%\supersonic-standalone\conf\webapp"
|
||||
rd /s /q "%buildDir%\supersonic-webapp"
|
||||
|
||||
endlocal
|
||||
@@ -1,78 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
sbinDir=$(cd "$(dirname "$0")"; pwd)
|
||||
chmod +x $sbinDir/supersonic-common.sh
|
||||
source $sbinDir/supersonic-common.sh
|
||||
cd $projectDir
|
||||
MVN_VERSION=$(mvn help:evaluate -Dexpression=project.version | grep -e '^[^\[]')
|
||||
|
||||
cd $baseDir
|
||||
|
||||
service=$1
|
||||
if [ -z "$service" ]; then
|
||||
service=${STANDALONE_SERVICE}
|
||||
#1. build backend java modules
|
||||
rm -fr ${buildDir}/*.tar.gz
|
||||
rm -fr dist
|
||||
set +x
|
||||
mvn -f $baseDir/../ clean package -DskipTests
|
||||
# check build result
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to build backend Java modules."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function buildJavaService {
|
||||
model_name=$1
|
||||
echo "starting building supersonic-${model_name} service"
|
||||
mvn -f $projectDir clean package -DskipTests -Dspotless.skip=true
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to build backend Java modules."
|
||||
exit 1
|
||||
fi
|
||||
cp $projectDir/launchers/${model_name}/target/*.tar.gz ${buildDir}/
|
||||
echo "finished building supersonic-${model_name} service"
|
||||
}
|
||||
#2. move package to build
|
||||
cp $baseDir/../launchers/headless/target/*.tar.gz ${buildDir}/supersonic-headless.tar.gz
|
||||
cp $baseDir/../launchers/chat/target/*.tar.gz ${buildDir}/supersonic-chat.tar.gz
|
||||
cp $baseDir/../launchers/standalone/target/*.tar.gz ${buildDir}/supersonic-standalone.tar.gz
|
||||
|
||||
function buildWebapp {
|
||||
echo "starting building supersonic webapp"
|
||||
chmod +x $projectDir/webapp/start-fe-prod.sh
|
||||
cd $projectDir/webapp
|
||||
sh ./start-fe-prod.sh
|
||||
cp -fr ./supersonic-webapp.tar.gz ${buildDir}/
|
||||
# check build result
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to build frontend webapp."
|
||||
exit 1
|
||||
fi
|
||||
echo "finished building supersonic webapp"
|
||||
}
|
||||
#3. build frontend webapp
|
||||
chmod +x $baseDir/../webapp/start-fe-prod.sh
|
||||
cd ../webapp
|
||||
sh ./start-fe-prod.sh
|
||||
cp -fr ./supersonic-webapp.tar.gz ${buildDir}/
|
||||
|
||||
function packageRelease {
|
||||
model_name=$1
|
||||
release_dir=supersonic-${model_name}-${MVN_VERSION}
|
||||
service_name=launchers-${model_name}-${MVN_VERSION}
|
||||
echo "starting packaging supersonic release"
|
||||
cd $buildDir
|
||||
[ -d "$release_dir" ] && rm -rf "$release_dir"
|
||||
[ -f "$release_dir.zip" ] && rm -f "$release_dir.zip"
|
||||
mkdir $release_dir
|
||||
# package webapp
|
||||
tar xvf supersonic-webapp.tar.gz
|
||||
mv supersonic-webapp webapp
|
||||
json='{"env": "''"}'
|
||||
echo $json > webapp/supersonic.config.json
|
||||
mv webapp $release_dir/
|
||||
# package java service
|
||||
tar xvf $service_name-bin.tar.gz
|
||||
mv $service_name/* $release_dir/
|
||||
# generate zip file
|
||||
zip -r $release_dir.zip $release_dir
|
||||
# delete intermediate files
|
||||
rm supersonic-webapp.tar.gz $service_name-bin.tar.gz
|
||||
rm -rf webapp $service_name $release_dir
|
||||
echo "finished packaging supersonic release"
|
||||
}
|
||||
# check build result
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to build frontend webapp."
|
||||
exit 1
|
||||
fi
|
||||
#4. copy webapp to java classpath
|
||||
cd $buildDir
|
||||
tar xvf supersonic-webapp.tar.gz
|
||||
mv supersonic-webapp webapp
|
||||
cp -fr webapp ../../launchers/headless/target/classes
|
||||
cp -fr webapp ../../launchers/chat/target/classes
|
||||
cp -fr webapp ../../launchers/standalone/target/classes
|
||||
rm -fr ${buildDir}/webapp
|
||||
|
||||
#1. build backend services
|
||||
if [ "$service" == "webapp" ]; then
|
||||
buildWebapp
|
||||
target_path=$projectDir/launchers/$STANDALONE_SERVICE/target/classes
|
||||
tar xvf $projectDir/webapp/supersonic-webapp.tar.gz -C $target_path
|
||||
rm -rf $target_path/webapp
|
||||
mv $target_path/supersonic-webapp $target_path/webapp
|
||||
else
|
||||
buildJavaService $service
|
||||
buildWebapp
|
||||
packageRelease $service
|
||||
fi
|
||||
#5. build backend python modules
|
||||
if [ "$service" == "pyllm" ]; then
|
||||
echo "start installing python modules with pip: ${pip_path}"
|
||||
requirementPath=$baseDir/../chat/python/requirements.txt
|
||||
${pip_path} install -r ${requirementPath}
|
||||
echo "install python modules success"
|
||||
fi
|
||||
|
||||
#6. reset runtime
|
||||
rm -fr $runtimeDir/supersonic*
|
||||
moveAllToRuntime
|
||||
setEnvToWeb chat
|
||||
setEnvToWeb headless
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
set "sbinDir=%~dp0"
|
||||
set "baseDir=%~dp0.."
|
||||
set "buildDir=%baseDir%\build"
|
||||
set "main_class=com.tencent.supersonic.StandaloneLauncher"
|
||||
set "standalone_service=standalone"
|
||||
set "projectDir=%baseDir%\.."
|
||||
@@ -1,16 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# environment parameters
|
||||
python_path=${PYTHON_PATH:-"python3"}
|
||||
pip_path=${PIP_PATH:-"pip3"}
|
||||
|
||||
sbinDir=$(cd "$(dirname "$0")"; pwd)
|
||||
baseDir=$(cd "$sbinDir/.." && pwd -P)
|
||||
runtimeDir=$baseDir/runtime
|
||||
runtimeDir=$baseDir/../runtime
|
||||
buildDir=$baseDir/build
|
||||
projectDir=$baseDir/..
|
||||
|
||||
readonly CHAT_APP_NAME="supersonic_chat"
|
||||
readonly HEADLESS_APP_NAME="supersonic_headless"
|
||||
readonly PYLLM_APP_NAME="supersonic_pyllm"
|
||||
readonly STANDALONE_APP_NAME="supersonic_standalone"
|
||||
|
||||
readonly CHAT_SERVICE="chat"
|
||||
readonly HEADLESS_SERVICE="headless"
|
||||
readonly STANDALONE_SERVICE="standalone"
|
||||
readonly PYLLM_SERVICE="pyllm"
|
||||
readonly STANDALONE_SERVICE="standalone"
|
||||
readonly PYLLM_HOST="127.0.0.1"
|
||||
readonly PYLLM_PORT="9092"
|
||||
|
||||
function setEnvToWeb {
|
||||
model_name=$1
|
||||
json='{"env": "'$model_name'"}'
|
||||
echo $json > ${runtimeDir}/supersonic-${model_name}/webapp/supersonic.config.json
|
||||
echo $json > $baseDir/../launchers/${model_name}/target/classes/webapp/supersonic.config.json
|
||||
}
|
||||
|
||||
function moveToRuntime {
|
||||
model_name=$1
|
||||
file="${buildDir}/supersonic-${model_name}.tar.gz"
|
||||
if [ -f "$file" ]; then
|
||||
tar -zxvf "$file" -C ${runtimeDir}
|
||||
mv ${runtimeDir}/launchers-${model_name}-* ${runtimeDir}/supersonic-${model_name}
|
||||
mkdir -p ${runtimeDir}/supersonic-${model_name}/webapp
|
||||
cp -fr ${buildDir}/webapp/* ${runtimeDir}/supersonic-${model_name}/webapp
|
||||
else
|
||||
echo "File $file does not exist. Skipping the move to runtime."
|
||||
fi
|
||||
}
|
||||
|
||||
function moveAllToRuntime {
|
||||
mkdir -p ${runtimeDir}
|
||||
tar xvf ${buildDir}/supersonic-webapp.tar.gz -C ${buildDir}
|
||||
mv ${buildDir}/supersonic-webapp ${buildDir}/webapp
|
||||
|
||||
moveToRuntime chat
|
||||
moveToRuntime headless
|
||||
moveToRuntime standalone
|
||||
rm -fr ${buildDir}/webapp
|
||||
}
|
||||
|
||||
# run java service
|
||||
function runJavaService {
|
||||
javaRunDir=${runtimeDir}/supersonic-${model_name}
|
||||
local_app_name=$1
|
||||
libDir=$javaRunDir/lib
|
||||
confDir=$javaRunDir/conf
|
||||
|
||||
CLASSPATH=""
|
||||
CLASSPATH=$CLASSPATH:$confDir
|
||||
|
||||
for jarPath in $libDir/*.jar; do
|
||||
CLASSPATH=$CLASSPATH:$jarPath
|
||||
done
|
||||
|
||||
export CLASSPATH
|
||||
export LANG="zh_CN.UTF-8"
|
||||
|
||||
cd $javaRunDir
|
||||
if [[ "$JAVA_HOME" == "" ]]; then
|
||||
JAVA_HOME=$(ls /usr/jdk64/jdk* -d 2>/dev/null | xargs | awk '{print "'$local_app_name'"}')
|
||||
fi
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
command="-Dfile.encoding="UTF-8" -Duser.language="Zh" -Duser.region="CN" -Duser.timezone="GMT+08" -Dapp_name=${local_app_name} -Xms1024m -Xmx2048m "$main_class
|
||||
|
||||
mkdir -p $javaRunDir/logs
|
||||
if [[ "$is_test" == "true" ]]; then
|
||||
java -Dspring.profiles.active="dev" $command >/dev/null 2>$javaRunDir/logs/error.log &
|
||||
else
|
||||
java $command $javaRunDir >/dev/null 2>$javaRunDir/logs/error.log &
|
||||
fi
|
||||
}
|
||||
|
||||
# run python service
|
||||
function runPythonService {
|
||||
pythonRunDir=${runtimeDir}/supersonic-${model_name}/pyllm
|
||||
cd $pythonRunDir
|
||||
nohup ${python_path} supersonic_pyllm.py > $pythonRunDir/pyllm.log 2>&1 &
|
||||
# add health check
|
||||
for i in {1..10}
|
||||
do
|
||||
echo "pyllm health check attempt $i..."
|
||||
response=$(curl -s http://${PYLLM_HOST}:${PYLLM_PORT}/health)
|
||||
echo "pyllm health check response: $response"
|
||||
status_ok="Healthy"
|
||||
if [[ $response == *$status_ok* ]] ; then
|
||||
echo "pyllm Health check passed."
|
||||
break
|
||||
else
|
||||
if [ "$i" -eq 10 ]; then
|
||||
echo "pyllm Health check failed after 10 attempts."
|
||||
echo "May still downloading model files. Please check pyllm.log in runtime directory."
|
||||
fi
|
||||
echo "Retrying after 5 seconds..."
|
||||
sleep 5
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
@@ -1,71 +1,118 @@
|
||||
@echo off
|
||||
setlocal
|
||||
chcp 65001
|
||||
|
||||
set "sbinDir=%~dp0"
|
||||
call %sbinDir%/supersonic-common.bat %*
|
||||
set "baseDir=%~dp0.."
|
||||
set "runtimeDir=%baseDir%\..\runtime"
|
||||
set "buildDir=%baseDir%\build"
|
||||
set "main_class=com.tencent.supersonic.StandaloneLauncher"
|
||||
set "python_path=python"
|
||||
set "pip_path=pip3"
|
||||
set "standalone_service=standalone"
|
||||
set "pyllm_service=pyllm"
|
||||
|
||||
set "javaRunDir=%runtimeDir%\supersonic-standalone"
|
||||
set "pythonRunDir=%runtimeDir%\supersonic-standalone\pyllm"
|
||||
|
||||
set "command=%~1"
|
||||
set "service=%~2"
|
||||
set "profile=%~3"
|
||||
|
||||
if "%service%"=="" (
|
||||
set "service=%standalone_service%"
|
||||
)
|
||||
|
||||
if "%profile%"=="" (
|
||||
set "profile=local"
|
||||
IF "%service%"=="pyllm" (
|
||||
SET "llmProxy=PythonLLMProxy"
|
||||
)
|
||||
|
||||
set "model_name=%service%"
|
||||
|
||||
cd %baseDir%
|
||||
call :BUILD_RUNTIME
|
||||
|
||||
if "%command%"=="restart" (
|
||||
call :stop
|
||||
call :start
|
||||
call :STOP
|
||||
call :START
|
||||
goto :EOF
|
||||
) else if "%command%"=="start" (
|
||||
call :start
|
||||
call :START
|
||||
goto :EOF
|
||||
) else if "%command%"=="stop" (
|
||||
call :stop
|
||||
goto :EOF
|
||||
call :STOP
|
||||
goto :EOF
|
||||
) else if "%command%"=="reload" (
|
||||
call :reloadExamples
|
||||
goto :EOF
|
||||
call :RELOAD_EXAMPLE
|
||||
goto :EOF
|
||||
) else (
|
||||
echo "Use command {start|stop|restart} to run."
|
||||
goto :EOF
|
||||
)
|
||||
|
||||
:start
|
||||
call :runJavaService
|
||||
:START
|
||||
if "%service%"=="%pyllm_service%" (
|
||||
call :START_PYTHON
|
||||
call :START_JAVA
|
||||
goto :EOF
|
||||
)
|
||||
call :START_JAVA
|
||||
goto :EOF
|
||||
|
||||
:STOP
|
||||
call :STOP_PYTHON
|
||||
call :STOP_JAVA
|
||||
goto :EOF
|
||||
|
||||
:START_PYTHON
|
||||
echo 'python service starting, see logs in pyllm/pyllm.log'
|
||||
cd "%pythonRunDir%"
|
||||
start /B %python_path% supersonic_pyllm.py > %pythonRunDir%\pyllm.log 2>&1
|
||||
timeout /t 10 >nul
|
||||
echo 'python service started'
|
||||
goto :EOF
|
||||
|
||||
:stop
|
||||
call :stopJavaService
|
||||
goto :EOF
|
||||
|
||||
:runJavaService
|
||||
echo 'java service starting, see logs in logs/'
|
||||
set "libDir=%baseDir%\lib"
|
||||
set "confDir=%baseDir%\conf"
|
||||
set "webDir=%baseDir%\webapp"
|
||||
set "logDir=%baseDir%\logs"
|
||||
set "classpath=%baseDir%;%webDir%;%libDir%\*;%confDir%"
|
||||
set "java-command=-Dfile.encoding=UTF-8 -Duser.language=Zh -Duser.region=CN -Duser.timezone=GMT+08 -Dspring.profiles.active=%profile% -Xms1024m -Xmx1024m -cp %CLASSPATH% %MAIN_CLASS%"
|
||||
if not exist %logDir% mkdir %logDir%
|
||||
:START_JAVA
|
||||
echo 'java service starting, see logs in logs/'
|
||||
cd "%javaRunDir%"
|
||||
if not exist "%runtimeDir%\supersonic-standalone\logs" mkdir "%runtimeDir%\supersonic-standalone\logs"
|
||||
set "libDir=%runtimeDir%\supersonic-standalone\lib"
|
||||
set "confDir=%runtimeDir%\supersonic-standalone\conf"
|
||||
set "webDir=%runtimeDir%\supersonic-standalone\webapp"
|
||||
set "classpath=%confDir%;%webDir%;%libDir%\*"
|
||||
set "java-command=-Dfile.encoding=UTF-8 -Duser.language=Zh -Duser.region=CN -Duser.timezone=GMT+08 -Xms1024m -Xmx2048m -cp %CLASSPATH% %MAIN_CLASS%"
|
||||
start /B java %java-command% >nul 2>&1
|
||||
timeout /t 10 >nul
|
||||
echo 'java service started'
|
||||
goto :EOF
|
||||
|
||||
:stopJavaService
|
||||
:STOP_PYTHON
|
||||
for /f "tokens=2" %%i in ('tasklist ^| findstr /i "python"') do (
|
||||
taskkill /PID %%i /F
|
||||
echo "python service (PID = %%i) is killed."
|
||||
)
|
||||
goto :EOF
|
||||
|
||||
:STOP_JAVA
|
||||
for /f "tokens=2" %%i in ('tasklist ^| findstr /i "java"') do (
|
||||
taskkill /PID %%i /F
|
||||
echo "java service (PID = %%i) is killed."
|
||||
)
|
||||
goto :EOF
|
||||
|
||||
endlocal
|
||||
:RELOAD_EXAMPLE
|
||||
cd "%runtimeDir%\supersonic-standalone\pyllm\sql"
|
||||
start %python_path% examples_reload_run.py
|
||||
goto :EOF
|
||||
|
||||
:BUILD_RUNTIME
|
||||
rem 6. reset runtime
|
||||
if exist "%runtimeDir%" goto :EOF
|
||||
mkdir "%runtimeDir%"
|
||||
tar -zxvf "%buildDir%\supersonic-standalone.tar.gz" -C "%runtimeDir%"
|
||||
for /d %%f in ("%runtimeDir%\launchers-standalone-*") do (
|
||||
move "%%f" "%runtimeDir%\supersonic-standalone"
|
||||
)
|
||||
|
||||
rem 7. copy webapp to runtime
|
||||
tar -zxvf "%buildDir%\supersonic-webapp.tar.gz" -C "%buildDir%"
|
||||
if not exist "%runtimeDir%\supersonic-standalone\webapp" mkdir "%runtimeDir%\supersonic-standalone\webapp"
|
||||
xcopy /s /e /h /y "%buildDir%\supersonic-webapp\*" "%runtimeDir%\supersonic-standalone\webapp"
|
||||
if not exist "%runtimeDir%\supersonic-standalone\conf\webapp" mkdir "%runtimeDir%\supersonic-standalone\conf\webapp"
|
||||
xcopy /s /e /h /y "%runtimeDir%\supersonic-standalone\webapp\*" "%runtimeDir%\supersonic-standalone\conf\webapp"
|
||||
rd /s /q "%buildDir%\supersonic-webapp"
|
||||
@@ -1,111 +1,143 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
sbinDir=$(cd "$(dirname "$0")"; pwd)
|
||||
chmod +x $sbinDir/supersonic-common.sh
|
||||
source $sbinDir/supersonic-common.sh
|
||||
|
||||
# 1.init environment parameters
|
||||
if [ ! -d "$runtimeDir" ]; then
|
||||
echo "the runtime dir does not exist move all to runtime"
|
||||
moveAllToRuntime
|
||||
fi
|
||||
set +x
|
||||
|
||||
command=$1
|
||||
service=$2
|
||||
profile=$3
|
||||
|
||||
if [ -z "$service" ]; then
|
||||
service=${STANDALONE_SERVICE}
|
||||
fi
|
||||
|
||||
if [ -z "$profile" ]; then
|
||||
profile="local"
|
||||
app_name=$STANDALONE_APP_NAME
|
||||
main_class="com.tencent.supersonic.StandaloneLauncher"
|
||||
model_name=$service
|
||||
|
||||
if [ "$service" == "pyllm" ]; then
|
||||
model_name=${STANDALONE_SERVICE}
|
||||
export llmProxy=PythonLLMProxy
|
||||
fi
|
||||
|
||||
model_name=$service
|
||||
cd $baseDir
|
||||
|
||||
# 2.set main class
|
||||
function setMainClass {
|
||||
if [ "$service" == $CHAT_SERVICE ]; then
|
||||
main_class="com.tencent.supersonic.ChatLauncher"
|
||||
elif [ "$service" == $HEADLESS_SERVICE ]; then
|
||||
main_class="com.tencent.supersonic.HeadlessLauncher"
|
||||
else
|
||||
main_class="com.tencent.supersonic.StandaloneLauncher"
|
||||
fi
|
||||
}
|
||||
|
||||
setMainClass
|
||||
# 3.set app name
|
||||
function setAppName {
|
||||
if [ "$service" == $CHAT_SERVICE ]; then
|
||||
app_name=$CHAT_APP_NAME
|
||||
elif [ "$service" == $HEADLESS_SERVICE ]; then
|
||||
app_name=$HEADLESS_APP_NAME
|
||||
else
|
||||
app_name=$STANDALONE_APP_NAME
|
||||
elif [ "$service" == $PYLLM_SERVICE ]; then
|
||||
app_name=$PYLLM_APP_NAME
|
||||
fi
|
||||
}
|
||||
setAppName
|
||||
|
||||
function runJavaService {
|
||||
javaRunDir=$baseDir
|
||||
local_app_name=$1
|
||||
libDir=$baseDir/lib
|
||||
confDir=$baseDir/conf
|
||||
|
||||
CLASSPATH=""
|
||||
CLASSPATH=$CLASSPATH:$confDir
|
||||
|
||||
for jarPath in $libDir/*.jar; do
|
||||
CLASSPATH=$CLASSPATH:$jarPath
|
||||
done
|
||||
|
||||
export CLASSPATH
|
||||
export LANG="zh_CN.UTF-8"
|
||||
|
||||
cd $javaRunDir
|
||||
if [[ "$JAVA_HOME" == "" ]]; then
|
||||
JAVA_HOME=$(ls /usr/jdk64/jdk* -d 2>/dev/null | xargs | awk '{print "'$local_app_name'"}')
|
||||
fi
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
command="-Dfile.encoding=UTF-8 -Duser.language=Zh -Duser.region=CN -Duser.timezone=GMT+08 -Dapp_name=${local_app_name} -Xms1024m -Xmx1024m $main_class"
|
||||
|
||||
mkdir -p $javaRunDir/logs
|
||||
java -Dspring.profiles.active="$profile" $command >/dev/null 2>$javaRunDir/logs/error.log &
|
||||
function reloadExamples {
|
||||
pythonRunDir=${runtimeDir}/supersonic-${model_name}/pyllm
|
||||
cd $pythonRunDir/sql
|
||||
${python_path} examples_reload_run.py
|
||||
}
|
||||
|
||||
function start() {
|
||||
|
||||
function start()
|
||||
{
|
||||
local_app_name=$1
|
||||
echo "Starting ${local_app_name}"
|
||||
pid=$(ps aux | grep ${local_app_name} | grep -v grep | awk '{print $2}')
|
||||
pid=$(ps aux |grep ${local_app_name} | grep -v grep | awk '{print $2}')
|
||||
if [[ "$pid" == "" ]]; then
|
||||
runJavaService ${local_app_name}
|
||||
if [[ ${local_app_name} == $PYLLM_APP_NAME ]]; then
|
||||
runPythonService ${local_app_name}
|
||||
else
|
||||
runJavaService ${local_app_name}
|
||||
fi
|
||||
else
|
||||
echo "Process (PID = $pid) is running."
|
||||
return 1
|
||||
fi
|
||||
echo "Start success"
|
||||
}
|
||||
|
||||
function stop() {
|
||||
echo "Stopping $1"
|
||||
function stop()
|
||||
{
|
||||
pid=$(ps aux | grep $1 | grep -v grep | awk '{print $2}')
|
||||
if [[ "$pid" == "" ]]; then
|
||||
echo "Process $1 is not running!"
|
||||
echo "Process $1 is not running !"
|
||||
return 1
|
||||
else
|
||||
kill -9 $pid
|
||||
echo "Process (PID = $pid) is killed!"
|
||||
echo "Process (PID = $pid) is killed !"
|
||||
return 0
|
||||
fi
|
||||
echo "Stop success"
|
||||
}
|
||||
|
||||
setMainClass
|
||||
setAppName
|
||||
function reload()
|
||||
{
|
||||
if [[ $1 == $PYLLM_APP_NAME ]]; then
|
||||
reloadExamples
|
||||
fi
|
||||
}
|
||||
|
||||
# 4. execute command operation
|
||||
case "$command" in
|
||||
start)
|
||||
start ${app_name}
|
||||
;;
|
||||
if [ "$service" == $PYLLM_SERVICE ]; then
|
||||
echo "Starting $app_name"
|
||||
start $app_name
|
||||
echo "Starting $STANDALONE_APP_NAME"
|
||||
start $STANDALONE_APP_NAME
|
||||
else
|
||||
echo "Starting $app_name"
|
||||
start $app_name
|
||||
fi
|
||||
echo "Start success"
|
||||
;;
|
||||
stop)
|
||||
stop $app_name
|
||||
;;
|
||||
echo "Stopping $app_name"
|
||||
stop $app_name
|
||||
echo "Stopping $PYLLM_APP_NAME"
|
||||
stop $PYLLM_APP_NAME
|
||||
echo "Stop success"
|
||||
;;
|
||||
reload)
|
||||
echo "Reloading ${app_name}"
|
||||
reload ${app_name}
|
||||
echo "Reload success"
|
||||
;;
|
||||
restart)
|
||||
stop ${app_name}
|
||||
start ${app_name}
|
||||
;;
|
||||
if [ "$service" == $PYLLM_SERVICE ]; then
|
||||
echo "Stopping ${app_name}"
|
||||
stop ${app_name}
|
||||
echo "Stopping ${STANDALONE_APP_NAME}"
|
||||
stop $STANDALONE_APP_NAME
|
||||
echo "Starting ${app_name}"
|
||||
start ${app_name}
|
||||
echo "Starting ${STANDALONE_APP_NAME}"
|
||||
start $STANDALONE_APP_NAME
|
||||
else
|
||||
echo "Stopping ${app_name}"
|
||||
stop ${app_name}
|
||||
echo "Starting ${app_name}"
|
||||
start ${app_name}
|
||||
fi
|
||||
echo "Restart success"
|
||||
;;
|
||||
*)
|
||||
echo "Use command {start|stop|restart} to run."
|
||||
exit 1
|
||||
esac
|
||||
echo "Use command {start|stop|restart} to run."
|
||||
exit 1
|
||||
esac
|
||||
|
||||
@@ -21,12 +21,8 @@
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/../../assembly/bin</directory>
|
||||
<excludes>
|
||||
<exclude>supersonic-build.sh</exclude>
|
||||
<exclude>supersonic-build.bat</exclude>
|
||||
</excludes>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<directory>${project.basedir}/../../chat/python</directory>
|
||||
<outputDirectory>pyllm</outputDirectory>
|
||||
<fileMode>0777</fileMode>
|
||||
<directoryMode>0755</directoryMode>
|
||||
</fileSet>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.adaptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.Organization;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserToken;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.request.UserReq;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** UserAdaptor defines some interfaces for obtaining user and organization information */
|
||||
/**
|
||||
* UserAdaptor defines some interfaces for obtaining user and organization information
|
||||
*/
|
||||
public interface UserAdaptor {
|
||||
|
||||
List<String> getUserNames();
|
||||
@@ -21,23 +20,9 @@ public interface UserAdaptor {
|
||||
|
||||
void register(UserReq userReq);
|
||||
|
||||
String login(UserReq userReq, HttpServletRequest request);
|
||||
|
||||
String login(UserReq userReq, String appKey);
|
||||
String login(UserReq userReq);
|
||||
|
||||
List<User> getUserByOrg(String key);
|
||||
|
||||
Set<String> getUserAllOrgId(String userName);
|
||||
|
||||
String getPassword(String userName);
|
||||
|
||||
void resetPassword(String userName, String password, String newPassword);
|
||||
|
||||
UserToken generateToken(String name, String userName, long expireTime);
|
||||
|
||||
void deleteUserToken(Long id);
|
||||
|
||||
UserToken getUserToken(Long id);
|
||||
|
||||
List<UserToken> getUserTokens(String userName);
|
||||
}
|
||||
|
||||
@@ -1,53 +1,36 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.config;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
public class AuthenticationConfig {
|
||||
|
||||
@Value("${s2.authentication.exclude.path:XXX}")
|
||||
@Value("${authentication.exclude.path:XXX}")
|
||||
private String excludePath;
|
||||
|
||||
@Value("${s2.authentication.include.path:/api}")
|
||||
@Value("${authentication.include.path:/api}")
|
||||
private String includePath;
|
||||
|
||||
@Value("${s2.authentication.enable:false}")
|
||||
@Value("${authentication.enable:false}")
|
||||
private boolean enabled;
|
||||
|
||||
@Value("${s2.authentication.token.default.appKey:supersonic}")
|
||||
private String tokenDefaultAppKey;
|
||||
@Value("${authentication.token.secret:secret}")
|
||||
private String tokenSecret;
|
||||
|
||||
@Value("${s2.authentication.token.appSecret:supersonic:WIaO9YRRVt+7QtpPvyWsARFngnEcbaKBk"
|
||||
+ "783uGFwMrbJBaochsqCH62L4Kijcb0sZCYoSsiKGV/zPml5MnZ3uQ==}")
|
||||
private String tokenAppSecret;
|
||||
|
||||
@Value("${s2.authentication.token.http.header.key:Authorization}")
|
||||
@Value("${authentication.token.http.header.key:Authorization}")
|
||||
private String tokenHttpHeaderKey;
|
||||
|
||||
@Value("${s2.authentication.token.http.app.key:App-Key}")
|
||||
private String tokenHttpHeaderAppKey;
|
||||
|
||||
@Value("${s2.authentication.app.appId:appId}")
|
||||
@Value("${authentication.app.appId:appId}")
|
||||
private String appId;
|
||||
|
||||
@Value("${s2.authentication.app.timestamp:timestamp}")
|
||||
@Value("${authentication.app.timestamp:timestamp}")
|
||||
private String timestamp;
|
||||
|
||||
@Value("${s2.authentication.app.signature:signature}")
|
||||
@Value("${authentication.app.signature:signature}")
|
||||
private String signature;
|
||||
|
||||
@Value("${s2.authentication.token.timeout:72000000}")
|
||||
private Long tokenTimeout;
|
||||
|
||||
public Map<String, String> getAppKeyToSecretMap() {
|
||||
return Arrays.stream(this.tokenAppSecret.split(",")).map(s -> s.split(":"))
|
||||
.collect(Collectors.toMap(e -> e[0].trim(), e -> e[1].trim()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,16 @@ public class UserConstants {
|
||||
public static final String TOKEN_USER_EMAIL = "token_user_email";
|
||||
|
||||
public static final String TOKEN_IS_ADMIN = "token_is_admin";
|
||||
|
||||
public static final String TOKEN_ALGORITHM = "HS512";
|
||||
|
||||
public static final String TOKEN_CREATE_TIME = "token_create_time";
|
||||
|
||||
public static final String TOKEN_PREFIX = "Bearer";
|
||||
|
||||
public static final Long TOKEN_TIME_OUT = 25920000000L;
|
||||
|
||||
public static final String INTERNAL = "internal";
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -23,4 +23,5 @@ public class Organization {
|
||||
private List<Organization> subOrganizations = Lists.newArrayList();
|
||||
|
||||
private boolean isRoot;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package com.tencent.supersonic.common.pojo;
|
||||
package com.tencent.supersonic.auth.api.authentication.pojo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class User implements Serializable {
|
||||
public class User {
|
||||
|
||||
private Long id;
|
||||
|
||||
@@ -22,8 +20,7 @@ public class User implements Serializable {
|
||||
|
||||
private Integer isAdmin;
|
||||
|
||||
public static User get(Long id, String name, String displayName, String email,
|
||||
Integer isAdmin) {
|
||||
public static User get(Long id, String name, String displayName, String email, Integer isAdmin) {
|
||||
return new User(id, name, displayName, email, isAdmin);
|
||||
}
|
||||
|
||||
@@ -31,14 +28,10 @@ public class User implements Serializable {
|
||||
return new User(id, name, name, name, 0);
|
||||
}
|
||||
|
||||
public static User getDefaultUser() {
|
||||
public static User getFakeUser() {
|
||||
return new User(1L, "admin", "admin", "admin@email", 1);
|
||||
}
|
||||
|
||||
public static User getVisitUser() {
|
||||
return new User(1L, "visit", "visit", "visit@email", 0);
|
||||
}
|
||||
|
||||
public static User getAppUser(int appId) {
|
||||
String name = String.format("app_%s", appId);
|
||||
return new User(1L, name, name, "", 1);
|
||||
@@ -51,4 +44,5 @@ public class User implements Serializable {
|
||||
public boolean isSuperAdmin() {
|
||||
return isAdmin != null && isAdmin == 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.pojo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserToken {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private String userName;
|
||||
private String token;
|
||||
private Long expireTime;
|
||||
private Date createDate;
|
||||
private Date expireDate;
|
||||
}
|
||||
@@ -1,19 +1,7 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.pojo;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_CREATE_TIME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_IS_ADMIN;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_DISPLAY_NAME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_ID;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_NAME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_PASSWORD;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@@ -21,26 +9,14 @@ public class UserWithPassword extends User {
|
||||
|
||||
private String password;
|
||||
|
||||
public UserWithPassword(Long id, String name, String displayName, String email, String password,
|
||||
Integer isAdmin) {
|
||||
public UserWithPassword(Long id, String name, String displayName, String email, String password, Integer isAdmin) {
|
||||
super(id, name, displayName, email, isAdmin);
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public static UserWithPassword get(Long id, String name, String displayName, String email,
|
||||
String password, Integer isAdmin) {
|
||||
public static UserWithPassword get(Long id, String name, String displayName,
|
||||
String email, String password, Integer isAdmin) {
|
||||
return new UserWithPassword(id, name, displayName, email, password, isAdmin);
|
||||
}
|
||||
|
||||
public static Map<String, Object> convert(UserWithPassword user) {
|
||||
Map<String, Object> claims = new HashMap<>(5);
|
||||
claims.put(TOKEN_USER_ID, user.getId());
|
||||
claims.put(TOKEN_USER_NAME, StringUtils.isEmpty(user.getName()) ? "" : user.getName());
|
||||
claims.put(TOKEN_USER_PASSWORD,
|
||||
StringUtils.isEmpty(user.getPassword()) ? "" : user.getPassword());
|
||||
claims.put(TOKEN_USER_DISPLAY_NAME, user.getDisplayName());
|
||||
claims.put(TOKEN_CREATE_TIME, System.currentTimeMillis());
|
||||
claims.put(TOKEN_IS_ADMIN, user.getIsAdmin());
|
||||
return claims;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.request;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@@ -12,4 +12,6 @@ public class UserReq {
|
||||
|
||||
@NotBlank(message = "password can not be null")
|
||||
private String password;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.request;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserTokenReq {
|
||||
@NotBlank(message = "name can not be null")
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "expireTime can not be null")
|
||||
private long expireTime;
|
||||
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.service;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.Organization;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.request.UserReq;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.Organization;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserToken;
|
||||
import com.tencent.supersonic.auth.api.authentication.request.UserReq;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface UserService {
|
||||
|
||||
User getCurrentUser(HttpServletRequest httpServletRequest,
|
||||
HttpServletResponse httpServletResponse);
|
||||
User getCurrentUser(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse);
|
||||
|
||||
List<String> getUserNames();
|
||||
|
||||
@@ -22,25 +19,11 @@ public interface UserService {
|
||||
|
||||
void register(UserReq userCmd);
|
||||
|
||||
String login(UserReq userCmd, HttpServletRequest request);
|
||||
|
||||
String login(UserReq userCmd, String appKey);
|
||||
String login(UserReq userCmd);
|
||||
|
||||
Set<String> getUserAllOrgId(String userName);
|
||||
|
||||
List<User> getUserByOrg(String key);
|
||||
|
||||
List<Organization> getOrganizationTree();
|
||||
|
||||
String getPassword(String userName);
|
||||
|
||||
void resetPassword(String userName, String password, String newPassword);
|
||||
|
||||
UserToken generateToken(String name, String userName, long expireTime);
|
||||
|
||||
List<UserToken> getUserTokens(String userName);
|
||||
|
||||
UserToken getUserToken(Long id);
|
||||
|
||||
void deleteUserToken(Long id);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.service;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
|
||||
public interface UserStrategy {
|
||||
|
||||
boolean accept(boolean isEnableAuthentication);
|
||||
|
||||
User findUser(HttpServletRequest request, HttpServletResponse response);
|
||||
|
||||
User findUser(String token, String appKey);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.utils;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserStrategy;
|
||||
import com.tencent.supersonic.common.pojo.SysParameter;
|
||||
import com.tencent.supersonic.common.service.SysParameterService;
|
||||
import com.tencent.supersonic.common.util.ContextUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserStrategy;
|
||||
import com.tencent.supersonic.common.config.SystemConfig;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import com.tencent.supersonic.common.service.SystemConfigService;
|
||||
import com.tencent.supersonic.common.util.ContextUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
public final class UserHolder {
|
||||
|
||||
private static UserStrategy REPO;
|
||||
@@ -20,21 +20,13 @@ public final class UserHolder {
|
||||
|
||||
public static User findUser(HttpServletRequest request, HttpServletResponse response) {
|
||||
User user = REPO.findUser(request, response);
|
||||
return getUser(user);
|
||||
}
|
||||
|
||||
public static User findUser(String token, String appKey) {
|
||||
User user = REPO.findUser(token, appKey);
|
||||
return getUser(user);
|
||||
}
|
||||
|
||||
private static User getUser(User user) {
|
||||
SystemConfigService sysParameterService = ContextUtils.getBean(SystemConfigService.class);
|
||||
SystemConfig systemConfig = sysParameterService.getSystemConfig();
|
||||
if (!CollectionUtils.isEmpty(systemConfig.getAdmins())
|
||||
&& systemConfig.getAdmins().contains(user.getName())) {
|
||||
SysParameterService sysParameterService = ContextUtils.getBean(SysParameterService.class);
|
||||
SysParameter sysParameter = sysParameterService.getSysParameter();
|
||||
if (!CollectionUtils.isEmpty(sysParameter.getAdmins())
|
||||
&& sysParameter.getAdmins().contains(user.getName())) {
|
||||
user.setIsAdmin(1);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AuthGroup {
|
||||
@@ -11,12 +10,18 @@ public class AuthGroup {
|
||||
private String name;
|
||||
private Integer groupId;
|
||||
private List<AuthRule> authRules;
|
||||
/** row permission expression */
|
||||
/**
|
||||
* row permission expression
|
||||
*/
|
||||
private List<String> dimensionFilters;
|
||||
/** row permission expression description information */
|
||||
/**
|
||||
* row permission expression description information
|
||||
*/
|
||||
private String dimensionFilterDescription;
|
||||
|
||||
private List<String> authorizedUsers;
|
||||
/** authorization Department Id */
|
||||
/**
|
||||
* authorization Department Id
|
||||
*/
|
||||
private List<String> authorizedDepartmentIds;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ public class AuthRes {
|
||||
private Long modelId;
|
||||
private String name;
|
||||
|
||||
public AuthRes() {}
|
||||
public AuthRes() {
|
||||
}
|
||||
|
||||
public AuthRes(Long modelId, String name) {
|
||||
this.modelId = modelId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.pojo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AuthResGrp {
|
||||
|
||||
private List<AuthRes> group = new ArrayList<>();
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.beans.Transient;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AuthRule {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DimensionFilter {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AddUsersToGroupReq {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.request;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthRes;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -14,6 +15,8 @@ public class QueryAuthResReq {
|
||||
|
||||
private List<String> departmentIds = new ArrayList<>();
|
||||
|
||||
private List<AuthRes> resources;
|
||||
|
||||
private Long modelId;
|
||||
|
||||
private List<Long> modelIds;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.request;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.PageBaseReq;
|
||||
import lombok.Data;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.PageBaseReq;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class QueryGroupReq extends PageBaseReq {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RemoveGroupReq {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RemoveUsersFromGroupReq {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.response;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthRes;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthResGrp;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.DimensionFilter;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AuthorizedResourceResp {
|
||||
|
||||
private List<AuthRes> authResList = new ArrayList<>();
|
||||
private List<AuthResGrp> resources = new ArrayList<>();
|
||||
|
||||
private List<DimensionFilter> filters = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.tencent.supersonic.auth.api.authorization.service;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthGroup;
|
||||
import com.tencent.supersonic.auth.api.authorization.request.QueryAuthResReq;
|
||||
import com.tencent.supersonic.auth.api.authorization.response.AuthorizedResourceResp;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AuthService {
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
<artifactId>druid</artifactId>
|
||||
<version>${alibaba.druid.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -46,6 +50,7 @@
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper</artifactId>
|
||||
<version>${pagehelper.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
package com.tencent.supersonic.auth.authentication.adaptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.tencent.supersonic.auth.api.authentication.adaptor.UserAdaptor;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.Organization;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserToken;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
|
||||
import com.tencent.supersonic.auth.api.authentication.request.UserReq;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserTokenDO;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.repository.UserRepository;
|
||||
import com.tencent.supersonic.auth.authentication.utils.TokenService;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import com.tencent.supersonic.common.util.AESEncryptionUtil;
|
||||
import com.tencent.supersonic.auth.authentication.utils.UserTokenUtils;
|
||||
import com.tencent.supersonic.common.util.ContextUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* DefaultUserAdaptor provides a default method to obtain user and organization information
|
||||
*/
|
||||
@Slf4j
|
||||
public class DefaultUserAdaptor implements UserAdaptor {
|
||||
|
||||
private List<UserDO> getUserDOList() {
|
||||
@@ -53,14 +44,14 @@ public class DefaultUserAdaptor implements UserAdaptor {
|
||||
|
||||
@Override
|
||||
public List<Organization> getOrganizationTree() {
|
||||
Organization superSonic =
|
||||
new Organization("1", "0", "SuperSonic", "SuperSonic", Lists.newArrayList(), true);
|
||||
Organization hr =
|
||||
new Organization("2", "1", "Hr", "SuperSonic/Hr", Lists.newArrayList(), false);
|
||||
Organization sales = new Organization("3", "1", "Sales", "SuperSonic/Sales",
|
||||
Lists.newArrayList(), false);
|
||||
Organization marketing = new Organization("4", "1", "Marketing", "SuperSonic/Marketing",
|
||||
Lists.newArrayList(), false);
|
||||
Organization superSonic = new Organization("1", "0",
|
||||
"SuperSonic", "SuperSonic", Lists.newArrayList(), true);
|
||||
Organization hr = new Organization("2", "1",
|
||||
"Hr", "SuperSonic/Hr", Lists.newArrayList(), false);
|
||||
Organization sales = new Organization("3", "1",
|
||||
"Sales", "SuperSonic/Sales", Lists.newArrayList(), false);
|
||||
Organization marketing = new Organization("4", "1",
|
||||
"Marketing", "SuperSonic/Marketing", Lists.newArrayList(), false);
|
||||
List<Organization> subOrganization = Lists.newArrayList(hr, sales, marketing);
|
||||
superSonic.setSubOrganizations(subOrganization);
|
||||
return Lists.newArrayList(superSonic);
|
||||
@@ -81,116 +72,22 @@ public class DefaultUserAdaptor implements UserAdaptor {
|
||||
}
|
||||
UserDO userDO = new UserDO();
|
||||
BeanUtils.copyProperties(userReq, userDO);
|
||||
try {
|
||||
byte[] salt = AESEncryptionUtil.generateSalt(userDO.getName());
|
||||
userDO.setSalt(AESEncryptionUtil.getStringFromBytes(salt));
|
||||
userDO.setPassword(AESEncryptionUtil.encrypt(userReq.getPassword(), salt));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("password encrypt error, please try again");
|
||||
}
|
||||
userRepository.addUser(userDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String login(UserReq userReq, HttpServletRequest request) {
|
||||
TokenService tokenService = ContextUtils.getBean(TokenService.class);
|
||||
String appKey = tokenService.getAppKey(request);
|
||||
return login(userReq, appKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String login(UserReq userReq, String appKey) {
|
||||
TokenService tokenService = ContextUtils.getBean(TokenService.class);
|
||||
try {
|
||||
UserWithPassword user = getUserWithPassword(userReq);
|
||||
return tokenService.generateToken(UserWithPassword.convert(user), appKey);
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
throw new RuntimeException("password encrypt error, please try again");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword(String userName) {
|
||||
UserDO userDO = getUser(userName);
|
||||
if (userDO == null) {
|
||||
throw new RuntimeException("user not exist,please register");
|
||||
}
|
||||
return userDO.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPassword(String userName, String password, String newPassword) {
|
||||
UserRepository userRepository = ContextUtils.getBean(UserRepository.class);
|
||||
Optional<UserDO> userDOOptional = Optional.ofNullable(getUser(userName));
|
||||
|
||||
UserDO userDO = userDOOptional
|
||||
.orElseThrow(() -> new RuntimeException("User does not exist, please register"));
|
||||
|
||||
try {
|
||||
validateOldPassword(userDO, password);
|
||||
updatePassword(userDO, newPassword, userRepository);
|
||||
} catch (PasswordEncryptionException e) {
|
||||
throw new RuntimeException("Password encryption error, please try again", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void validateOldPassword(UserDO userDO, String password)
|
||||
throws PasswordEncryptionException {
|
||||
String oldPassword = encryptPassword(password, userDO.getSalt());
|
||||
if (!userDO.getPassword().equals(oldPassword)) {
|
||||
throw new RuntimeException("Old password is not correct, please try again");
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePassword(UserDO userDO, String newPassword, UserRepository userRepository)
|
||||
throws PasswordEncryptionException {
|
||||
try {
|
||||
byte[] salt = AESEncryptionUtil.generateSalt(userDO.getName());
|
||||
userDO.setSalt(AESEncryptionUtil.getStringFromBytes(salt));
|
||||
userDO.setPassword(AESEncryptionUtil.encrypt(newPassword, salt));
|
||||
userRepository.updateUser(userDO);
|
||||
} catch (Exception e) {
|
||||
throw new PasswordEncryptionException("Error encrypting password", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String encryptPassword(String password, String salt)
|
||||
throws PasswordEncryptionException {
|
||||
try {
|
||||
return AESEncryptionUtil.encrypt(password, AESEncryptionUtil.getBytesFromString(salt));
|
||||
} catch (Exception e) {
|
||||
throw new PasswordEncryptionException("Error encrypting password", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PasswordEncryptionException extends Exception {
|
||||
public PasswordEncryptionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
private UserWithPassword getUserWithPassword(UserReq userReq) {
|
||||
public String login(UserReq userReq) {
|
||||
UserTokenUtils userTokenUtils = ContextUtils.getBean(UserTokenUtils.class);
|
||||
UserDO userDO = getUser(userReq.getName());
|
||||
if (userDO == null) {
|
||||
throw new RuntimeException("user not exist,please register");
|
||||
}
|
||||
try {
|
||||
String password = AESEncryptionUtil.encrypt(userReq.getPassword(),
|
||||
AESEncryptionUtil.getBytesFromString(userDO.getSalt()));
|
||||
if (userDO.getPassword().equals(password)) {
|
||||
UserWithPassword user = UserWithPassword.get(userDO.getId(), userDO.getName(),
|
||||
userDO.getDisplayName(), userDO.getEmail(), userDO.getPassword(),
|
||||
userDO.getIsAdmin());
|
||||
return user;
|
||||
} else {
|
||||
throw new RuntimeException("password not correct, please try again");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("password encrypt error, please try again");
|
||||
if (userDO.getPassword().equals(userReq.getPassword())) {
|
||||
UserWithPassword user = UserWithPassword.get(userDO.getId(), userDO.getName(), userDO.getDisplayName(),
|
||||
userDO.getEmail(), userDO.getPassword(), userDO.getIsAdmin());
|
||||
return userTokenUtils.generateToken(user);
|
||||
}
|
||||
throw new RuntimeException("password not correct, please try again");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -203,69 +100,4 @@ public class DefaultUserAdaptor implements UserAdaptor {
|
||||
return Sets.newHashSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserToken generateToken(String name, String userName, long expireTime) {
|
||||
TokenService tokenService = ContextUtils.getBean(TokenService.class);
|
||||
UserDO userDO = getUser(userName);
|
||||
if (userDO == null) {
|
||||
throw new RuntimeException("user not exist,please register");
|
||||
}
|
||||
UserWithPassword userWithPassword =
|
||||
new UserWithPassword(userDO.getId(), userDO.getName(), userDO.getDisplayName(),
|
||||
userDO.getEmail(), userDO.getPassword(), userDO.getIsAdmin());
|
||||
|
||||
String token =
|
||||
tokenService.generateToken(UserWithPassword.convert(userWithPassword), expireTime);
|
||||
UserTokenDO userTokenDO = saveUserToken(name, userName, token, expireTime);
|
||||
return convertUserToken(userTokenDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserToken(Long id) {
|
||||
UserRepository userRepository = ContextUtils.getBean(UserRepository.class);
|
||||
userRepository.deleteUserToken(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserToken getUserToken(Long id) {
|
||||
UserRepository userRepository = ContextUtils.getBean(UserRepository.class);
|
||||
return convertUserToken(userRepository.getUserToken(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserToken> getUserTokens(String userName) {
|
||||
UserRepository userRepository = ContextUtils.getBean(UserRepository.class);
|
||||
List<UserToken> userTokens = userRepository.getUserTokenListByName(userName).stream()
|
||||
.map(this::convertUserToken).collect(Collectors.toList());
|
||||
return userTokens;
|
||||
}
|
||||
|
||||
private UserTokenDO saveUserToken(String tokenName, String userName, String token,
|
||||
long expireTime) {
|
||||
UserTokenDO userTokenDO = new UserTokenDO();
|
||||
userTokenDO.setName(tokenName);
|
||||
userTokenDO.setUserName(userName);
|
||||
userTokenDO.setToken(token);
|
||||
userTokenDO.setExpireTime(expireTime);
|
||||
userTokenDO.setCreateTime(new java.util.Date());
|
||||
userTokenDO.setCreateBy(userName);
|
||||
userTokenDO.setUpdateBy(userName);
|
||||
userTokenDO.setExpireDateTime(new java.util.Date(System.currentTimeMillis() + expireTime));
|
||||
UserRepository userRepository = ContextUtils.getBean(UserRepository.class);
|
||||
userRepository.addUserToken(userTokenDO);
|
||||
|
||||
return userTokenDO;
|
||||
}
|
||||
|
||||
private UserToken convertUserToken(UserTokenDO userTokenDO) {
|
||||
UserToken userToken = new UserToken();
|
||||
userToken.setId(userTokenDO.getId());
|
||||
userToken.setName(userTokenDO.getName());
|
||||
userToken.setUserName(userTokenDO.getUserName());
|
||||
userToken.setToken(userTokenDO.getToken());
|
||||
userToken.setExpireTime(userTokenDO.getExpireTime());
|
||||
userToken.setCreateDate(userTokenDO.getCreateTime());
|
||||
userToken.setExpireDate(userTokenDO.getExpireDateTime());
|
||||
return userToken;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
package com.tencent.supersonic.auth.api.authentication.annotation;
|
||||
package com.tencent.supersonic.auth.authentication.interceptor;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -8,4 +9,5 @@ import java.lang.annotation.Target;
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AuthenticationIgnore {
|
||||
|
||||
}
|
||||
@@ -1,38 +1,38 @@
|
||||
package com.tencent.supersonic.auth.authentication.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
|
||||
import com.tencent.supersonic.auth.api.authentication.constant.UserConstants;
|
||||
import com.tencent.supersonic.auth.authentication.service.UserServiceImpl;
|
||||
import com.tencent.supersonic.auth.authentication.utils.TokenService;
|
||||
import com.tencent.supersonic.auth.authentication.utils.UserTokenUtils;
|
||||
import com.tencent.supersonic.common.util.S2ThreadContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.catalina.connector.RequestFacade;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.apache.tomcat.util.http.MimeHeaders;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
|
||||
public abstract class AuthenticationInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
protected AuthenticationConfig authenticationConfig;
|
||||
|
||||
protected UserServiceImpl userServiceImpl;
|
||||
|
||||
protected TokenService tokenService;
|
||||
protected UserTokenUtils userTokenUtils;
|
||||
|
||||
|
||||
protected S2ThreadContext s2ThreadContext;
|
||||
|
||||
protected boolean isExcludedUri(String uri) {
|
||||
String excludePathStr = authenticationConfig.getExcludePath();
|
||||
if (StringUtils.isEmpty(excludePathStr)) {
|
||||
if (Strings.isEmpty(excludePathStr)) {
|
||||
return false;
|
||||
}
|
||||
List<String> excludePaths = Arrays.asList(excludePathStr.split(","));
|
||||
@@ -44,7 +44,7 @@ public abstract class AuthenticationInterceptor implements HandlerInterceptor {
|
||||
|
||||
protected boolean isIncludedUri(String uri) {
|
||||
String includePathStr = authenticationConfig.getIncludePath();
|
||||
if (StringUtils.isEmpty(includePathStr)) {
|
||||
if (Strings.isEmpty(includePathStr)) {
|
||||
return false;
|
||||
}
|
||||
List<String> includePaths = Arrays.asList(includePathStr.split(","));
|
||||
@@ -64,12 +64,11 @@ public abstract class AuthenticationInterceptor implements HandlerInterceptor {
|
||||
return StringUtils.isNotBlank(appId);
|
||||
}
|
||||
|
||||
protected void reflectSetParam(HttpServletRequest request, String key, String value) {
|
||||
protected void reflectSetparam(HttpServletRequest request, String key, String value) {
|
||||
try {
|
||||
if (request instanceof StandardMultipartHttpServletRequest) {
|
||||
RequestFacade servletRequest =
|
||||
(RequestFacade) ((StandardMultipartHttpServletRequest) request)
|
||||
.getRequest();
|
||||
(RequestFacade) ((StandardMultipartHttpServletRequest) request).getRequest();
|
||||
Class<? extends HttpServletRequest> servletRequestClazz = servletRequest.getClass();
|
||||
Field request1 = servletRequestClazz.getDeclaredField("request");
|
||||
request1.setAccessible(true);
|
||||
@@ -99,7 +98,7 @@ public abstract class AuthenticationInterceptor implements HandlerInterceptor {
|
||||
o2.addValue(key).setString(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("reflectSetParam error:", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,31 @@
|
||||
package com.tencent.supersonic.auth.authentication.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.annotation.AuthenticationIgnore;
|
||||
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
|
||||
import com.tencent.supersonic.auth.authentication.service.UserServiceImpl;
|
||||
import com.tencent.supersonic.auth.authentication.utils.TokenService;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import com.tencent.supersonic.auth.authentication.utils.UserTokenUtils;
|
||||
import com.tencent.supersonic.common.pojo.exception.AccessException;
|
||||
import com.tencent.supersonic.common.util.ContextUtils;
|
||||
import com.tencent.supersonic.common.util.S2ThreadContext;
|
||||
import com.tencent.supersonic.common.util.ThreadContext;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_IS_ADMIN;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_DISPLAY_NAME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_EMAIL;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_ID;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_NAME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_PASSWORD;
|
||||
|
||||
@Slf4j
|
||||
public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws AccessException {
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws AccessException {
|
||||
authenticationConfig = ContextUtils.getBean(AuthenticationConfig.class);
|
||||
userServiceImpl = ContextUtils.getBean(UserServiceImpl.class);
|
||||
tokenService = ContextUtils.getBean(TokenService.class);
|
||||
userTokenUtils = ContextUtils.getBean(UserTokenUtils.class);
|
||||
s2ThreadContext = ContextUtils.getBean(S2ThreadContext.class);
|
||||
if (!authenticationConfig.isEnabled()) {
|
||||
setFakerUser(request);
|
||||
@@ -67,8 +57,8 @@ public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor
|
||||
return true;
|
||||
}
|
||||
|
||||
UserWithPassword user = getUserWithPassword(request);
|
||||
if (user != null) {
|
||||
UserWithPassword user = userTokenUtils.getUserWithPassword(request);
|
||||
if (StringUtils.isNotBlank(user.getName())) {
|
||||
setContext(user.getName(), request);
|
||||
return true;
|
||||
}
|
||||
@@ -76,41 +66,17 @@ public class DefaultAuthenticationInterceptor extends AuthenticationInterceptor
|
||||
}
|
||||
|
||||
private void setFakerUser(HttpServletRequest request) {
|
||||
String token = generateAdminToken(request);
|
||||
reflectSetParam(request, authenticationConfig.getTokenHttpHeaderKey(), token);
|
||||
setContext(User.getDefaultUser().getName(), request);
|
||||
String token = userTokenUtils.generateAdminToken();
|
||||
reflectSetparam(request, authenticationConfig.getTokenHttpHeaderKey(), token);
|
||||
setContext(User.getFakeUser().getName(), request);
|
||||
}
|
||||
|
||||
private void setContext(String userName, HttpServletRequest request) {
|
||||
ThreadContext threadContext = ThreadContext.builder()
|
||||
.token(request.getHeader(authenticationConfig.getTokenHttpHeaderKey()))
|
||||
.userName(userName).build();
|
||||
.userName(userName)
|
||||
.build();
|
||||
s2ThreadContext.set(threadContext);
|
||||
}
|
||||
|
||||
public String generateAdminToken(HttpServletRequest request) {
|
||||
UserWithPassword admin = new UserWithPassword("admin");
|
||||
admin.setId(1L);
|
||||
admin.setName("admin");
|
||||
admin.setPassword("c3VwZXJzb25pY0BiaWNvbdktJJYWw6A3rEmBUPzbn/6DNeYnD+y3mAwDKEMS3KVT");
|
||||
admin.setDisplayName("admin");
|
||||
admin.setIsAdmin(1);
|
||||
return tokenService.generateToken(UserWithPassword.convert(admin), request);
|
||||
}
|
||||
|
||||
public UserWithPassword getUserWithPassword(HttpServletRequest request) {
|
||||
final Optional<Claims> claimsOptional = tokenService.getClaims(request);
|
||||
if (!claimsOptional.isPresent()) {
|
||||
return null;
|
||||
}
|
||||
Claims claims = claimsOptional.get();
|
||||
Long userId = Long.parseLong(claims.getOrDefault(TOKEN_USER_ID, 0).toString());
|
||||
String userName = String.valueOf(claims.get(TOKEN_USER_NAME));
|
||||
String email = String.valueOf(claims.get(TOKEN_USER_EMAIL));
|
||||
String displayName = String.valueOf(claims.get(TOKEN_USER_DISPLAY_NAME));
|
||||
String password = String.valueOf(claims.get(TOKEN_USER_PASSWORD));
|
||||
Integer isAdmin = claims.get(TOKEN_IS_ADMIN) == null ? 0
|
||||
: Integer.parseInt(claims.get(TOKEN_IS_ADMIN).toString());
|
||||
return UserWithPassword.get(userId, userName, displayName, email, password, isAdmin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,12 @@ import java.util.List;
|
||||
@Configuration
|
||||
public class InterceptorFactory implements WebMvcConfigurer {
|
||||
|
||||
|
||||
private List<AuthenticationInterceptor> authenticationInterceptors;
|
||||
|
||||
public InterceptorFactory() {
|
||||
authenticationInterceptors = SpringFactoriesLoader.loadFactories(
|
||||
AuthenticationInterceptor.class, Thread.currentThread().getContextClassLoader());
|
||||
authenticationInterceptors = SpringFactoriesLoader.loadFactories(AuthenticationInterceptor.class,
|
||||
Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -24,4 +25,5 @@ public class InterceptorFactory implements WebMvcConfigurer {
|
||||
.excludePathPatterns("/", "/webapp/**", "/error");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,91 +1,129 @@
|
||||
package com.tencent.supersonic.auth.authentication.persistence.dataobject;
|
||||
|
||||
public class UserDO {
|
||||
/** */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/** */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/** */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private String password;
|
||||
|
||||
private String salt;
|
||||
|
||||
/** */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private String displayName;
|
||||
|
||||
/** */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/** */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Integer isAdmin;
|
||||
|
||||
/** @return id */
|
||||
/**
|
||||
*
|
||||
* @return id
|
||||
*/
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @param id */
|
||||
/**
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/** @return name */
|
||||
/**
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/** @param name */
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name == null ? null : name.trim();
|
||||
}
|
||||
|
||||
/** @return password */
|
||||
/**
|
||||
*
|
||||
* @return password
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/** @param password */
|
||||
/**
|
||||
*
|
||||
* @param password
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password == null ? null : password.trim();
|
||||
}
|
||||
|
||||
public String getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(String salt) {
|
||||
this.salt = salt == null ? null : salt.trim();
|
||||
}
|
||||
|
||||
/** @return display_name */
|
||||
/**
|
||||
*
|
||||
* @return display_name
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/** @param displayName */
|
||||
/**
|
||||
*
|
||||
* @param displayName
|
||||
*/
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName == null ? null : displayName.trim();
|
||||
}
|
||||
|
||||
/** @return email */
|
||||
/**
|
||||
*
|
||||
* @return email
|
||||
*/
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
/** @param email */
|
||||
/**
|
||||
*
|
||||
* @param email
|
||||
*/
|
||||
public void setEmail(String email) {
|
||||
this.email = email == null ? null : email.trim();
|
||||
}
|
||||
|
||||
/** @return is_admin */
|
||||
/**
|
||||
*
|
||||
* @return is_admin
|
||||
*/
|
||||
public Integer getIsAdmin() {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
/** @param isAdmin */
|
||||
/**
|
||||
*
|
||||
* @param isAdmin
|
||||
*/
|
||||
public void setIsAdmin(Integer isAdmin) {
|
||||
this.isAdmin = isAdmin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,64 +4,101 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UserDOExample {
|
||||
/** s2_user */
|
||||
/**
|
||||
* s2_user
|
||||
*/
|
||||
protected String orderByClause;
|
||||
|
||||
/** s2_user */
|
||||
/**
|
||||
* s2_user
|
||||
*/
|
||||
protected boolean distinct;
|
||||
|
||||
/** s2_user */
|
||||
/**
|
||||
* s2_user
|
||||
*/
|
||||
protected List<Criteria> oredCriteria;
|
||||
|
||||
/** s2_user */
|
||||
/**
|
||||
* s2_user
|
||||
*/
|
||||
protected Integer limitStart;
|
||||
|
||||
/** s2_user */
|
||||
/**
|
||||
* s2_user
|
||||
*/
|
||||
protected Integer limitEnd;
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public UserDOExample() {
|
||||
oredCriteria = new ArrayList<Criteria>();
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public void setOrderByClause(String orderByClause) {
|
||||
this.orderByClause = orderByClause;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public String getOrderByClause() {
|
||||
return orderByClause;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public void setDistinct(boolean distinct) {
|
||||
this.distinct = distinct;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public boolean isDistinct() {
|
||||
return distinct;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public List<Criteria> getOredCriteria() {
|
||||
return oredCriteria;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public void or(Criteria criteria) {
|
||||
oredCriteria.add(criteria);
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public Criteria or() {
|
||||
Criteria criteria = createCriteriaInternal();
|
||||
oredCriteria.add(criteria);
|
||||
return criteria;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public Criteria createCriteria() {
|
||||
Criteria criteria = createCriteriaInternal();
|
||||
if (oredCriteria.size() == 0) {
|
||||
@@ -70,40 +107,60 @@ public class UserDOExample {
|
||||
return criteria;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
protected Criteria createCriteriaInternal() {
|
||||
Criteria criteria = new Criteria();
|
||||
return criteria;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public void clear() {
|
||||
oredCriteria.clear();
|
||||
orderByClause = null;
|
||||
distinct = false;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public void setLimitStart(Integer limitStart) {
|
||||
this.limitStart = limitStart;
|
||||
this.limitStart=limitStart;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public Integer getLimitStart() {
|
||||
return limitStart;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public void setLimitEnd(Integer limitEnd) {
|
||||
this.limitEnd = limitEnd;
|
||||
this.limitEnd=limitEnd;
|
||||
}
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
*
|
||||
* @mbg.generated
|
||||
*/
|
||||
public Integer getLimitEnd() {
|
||||
return limitEnd;
|
||||
}
|
||||
|
||||
/** s2_user null */
|
||||
/**
|
||||
* s2_user null
|
||||
*/
|
||||
protected abstract static class GeneratedCriteria {
|
||||
protected List<Criterion> criteria;
|
||||
|
||||
@@ -138,8 +195,7 @@ public class UserDOExample {
|
||||
criteria.add(new Criterion(condition, value));
|
||||
}
|
||||
|
||||
protected void addCriterion(String condition, Object value1, Object value2,
|
||||
String property) {
|
||||
protected void addCriterion(String condition, Object value1, Object value2, String property) {
|
||||
if (value1 == null || value2 == null) {
|
||||
throw new RuntimeException("Between values for " + property + " cannot be null");
|
||||
}
|
||||
@@ -547,7 +603,9 @@ public class UserDOExample {
|
||||
}
|
||||
}
|
||||
|
||||
/** s2_user */
|
||||
/**
|
||||
* s2_user
|
||||
*/
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
||||
protected Criteria() {
|
||||
@@ -555,7 +613,9 @@ public class UserDOExample {
|
||||
}
|
||||
}
|
||||
|
||||
/** s2_user null */
|
||||
/**
|
||||
* s2_user null
|
||||
*/
|
||||
public static class Criterion {
|
||||
private String condition;
|
||||
|
||||
@@ -628,8 +688,7 @@ public class UserDOExample {
|
||||
this(condition, value, null);
|
||||
}
|
||||
|
||||
protected Criterion(String condition, Object value, Object secondValue,
|
||||
String typeHandler) {
|
||||
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
|
||||
super();
|
||||
this.condition = condition;
|
||||
this.value = value;
|
||||
@@ -642,4 +701,4 @@ public class UserDOExample {
|
||||
this(condition, value, secondValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.tencent.supersonic.auth.authentication.persistence.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("s2_user_token")
|
||||
public class UserTokenDO {
|
||||
@TableId(type = IdType.AUTO)
|
||||
Integer id;
|
||||
String name;
|
||||
String userName;
|
||||
Long expireTime;
|
||||
String token;
|
||||
String salt;
|
||||
Date createTime;
|
||||
Date updateTime;
|
||||
String createBy;
|
||||
String updateBy;
|
||||
Date expireDateTime;
|
||||
}
|
||||
@@ -1,19 +1,22 @@
|
||||
package com.tencent.supersonic.auth.authentication.persistence.mapper;
|
||||
|
||||
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDOExample;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface UserDOMapper {
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
* @mbg.generated
|
||||
*/
|
||||
int insert(UserDO record);
|
||||
|
||||
/** @mbg.generated */
|
||||
/**
|
||||
* @mbg.generated
|
||||
*/
|
||||
List<UserDO> selectByExample(UserDOExample example);
|
||||
|
||||
void updateByPrimaryKey(UserDO userDO);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.tencent.supersonic.auth.authentication.persistence.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserTokenDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface UserTokenDOMapper extends BaseMapper<UserTokenDO> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.tencent.supersonic.auth.authentication.persistence.repository.impl;
|
||||
|
||||
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDOExample;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.repository.UserRepository;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.mapper.UserDOMapper;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class UserRepositoryImpl implements UserRepository {
|
||||
|
||||
|
||||
private UserDOMapper userDOMapper;
|
||||
|
||||
|
||||
public UserRepositoryImpl(UserDOMapper userDOMapper) {
|
||||
this.userDOMapper = userDOMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDO> getUserList() {
|
||||
return userDOMapper.selectByExample(new UserDOExample());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUser(UserDO userDO) {
|
||||
userDOMapper.insert(userDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDO getUser(String name) {
|
||||
UserDOExample userDOExample = new UserDOExample();
|
||||
userDOExample.createCriteria().andNameEqualTo(name);
|
||||
List<UserDO> userDOS = userDOMapper.selectByExample(userDOExample);
|
||||
Optional<UserDO> userDOOptional = userDOS.stream().findFirst();
|
||||
return userDOOptional.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.tencent.supersonic.auth.authentication.persistence.repository;
|
||||
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserTokenDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserRepository {
|
||||
@@ -11,17 +9,5 @@ public interface UserRepository {
|
||||
|
||||
void addUser(UserDO userDO);
|
||||
|
||||
List<UserTokenDO> getUserTokenListByName(String userName);
|
||||
|
||||
UserDO getUser(String name);
|
||||
|
||||
void updateUser(UserDO userDO);
|
||||
|
||||
void addUserToken(UserTokenDO userTokenDO);
|
||||
|
||||
UserTokenDO getUserToken(Long tokenId);
|
||||
|
||||
void deleteUserTokenByName(String userName);
|
||||
|
||||
void deleteUserToken(Long tokenId);
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.tencent.supersonic.auth.authentication.persistence.repository.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDOExample;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserTokenDO;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.mapper.UserDOMapper;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.mapper.UserTokenDOMapper;
|
||||
import com.tencent.supersonic.auth.authentication.persistence.repository.UserRepository;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class UserRepositoryImpl implements UserRepository {
|
||||
|
||||
private UserDOMapper userDOMapper;
|
||||
|
||||
private UserTokenDOMapper userTokenDOMapper;
|
||||
|
||||
public UserRepositoryImpl(UserDOMapper userDOMapper, UserTokenDOMapper userTokenDOMapper) {
|
||||
this.userDOMapper = userDOMapper;
|
||||
this.userTokenDOMapper = userTokenDOMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDO> getUserList() {
|
||||
return userDOMapper.selectByExample(new UserDOExample());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUser(UserDO userDO) {
|
||||
userDOMapper.updateByPrimaryKey(userDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUser(UserDO userDO) {
|
||||
userDOMapper.insert(userDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDO getUser(String name) {
|
||||
UserDOExample userDOExample = new UserDOExample();
|
||||
userDOExample.createCriteria().andNameEqualTo(name);
|
||||
List<UserDO> userDOS = userDOMapper.selectByExample(userDOExample);
|
||||
Optional<UserDO> userDOOptional = userDOS.stream().findFirst();
|
||||
return userDOOptional.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserTokenDO> getUserTokenListByName(String userName) {
|
||||
QueryWrapper<UserTokenDO> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_name", userName);
|
||||
return userTokenDOMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUserToken(UserTokenDO userTokenDO) {
|
||||
userTokenDOMapper.insert(userTokenDO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserTokenDO getUserToken(Long tokenId) {
|
||||
return userTokenDOMapper.selectById(tokenId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserTokenByName(String userName) {
|
||||
QueryWrapper<UserTokenDO> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_name", userName);
|
||||
userTokenDOMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserToken(Long tokenId) {
|
||||
userTokenDOMapper.deleteById(tokenId);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
package com.tencent.supersonic.auth.authentication.rest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.Organization;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserToken;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.request.UserReq;
|
||||
import com.tencent.supersonic.auth.api.authentication.request.UserTokenReq;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserService;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -33,8 +30,7 @@ public class UserController {
|
||||
}
|
||||
|
||||
@GetMapping("/getCurrentUser")
|
||||
public User getCurrentUser(HttpServletRequest httpServletRequest,
|
||||
HttpServletResponse httpServletResponse) {
|
||||
public User getCurrentUser(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
|
||||
return userService.getCurrentUser(httpServletRequest, httpServletResponse);
|
||||
}
|
||||
|
||||
@@ -69,31 +65,8 @@ public class UserController {
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public String login(@RequestBody UserReq userCmd, HttpServletRequest request) {
|
||||
return userService.login(userCmd, request);
|
||||
public String login(@RequestBody UserReq userCmd) {
|
||||
return userService.login(userCmd);
|
||||
}
|
||||
|
||||
@PostMapping("/generateToken")
|
||||
public UserToken generateToken(@RequestBody UserTokenReq userTokenReq,
|
||||
HttpServletRequest request, HttpServletResponse response) {
|
||||
User user = userService.getCurrentUser(request, response);
|
||||
return userService.generateToken(userTokenReq.getName(), user.getName(),
|
||||
userTokenReq.getExpireTime());
|
||||
}
|
||||
|
||||
@GetMapping("/getUserTokens")
|
||||
public List<UserToken> getUserTokens(HttpServletRequest request, HttpServletResponse response) {
|
||||
User user = userService.getCurrentUser(request, response);
|
||||
return userService.getUserTokens(user.getName());
|
||||
}
|
||||
|
||||
@GetMapping("/getUserToken")
|
||||
public UserToken getUserToken(@RequestParam(name = "tokenId") Long tokenId) {
|
||||
return userService.getUserToken(tokenId);
|
||||
}
|
||||
|
||||
@PostMapping("/deleteUserToken")
|
||||
public void deleteUserToken(@RequestParam(name = "tokenId") Long tokenId) {
|
||||
userService.deleteUserToken(tokenId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
package com.tencent.supersonic.auth.authentication.service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.Organization;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserToken;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.request.UserReq;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserService;
|
||||
import com.tencent.supersonic.auth.api.authentication.utils.UserHolder;
|
||||
import com.tencent.supersonic.auth.authentication.utils.ComponentFactory;
|
||||
import com.tencent.supersonic.common.config.SystemConfig;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import com.tencent.supersonic.common.service.SystemConfigService;
|
||||
import com.tencent.supersonic.common.pojo.SysParameter;
|
||||
import com.tencent.supersonic.common.service.SysParameterService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private SystemConfigService sysParameterService;
|
||||
private SysParameterService sysParameterService;
|
||||
|
||||
public UserServiceImpl(SystemConfigService sysParameterService) {
|
||||
public UserServiceImpl(SysParameterService sysParameterService) {
|
||||
this.sysParameterService = sysParameterService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getCurrentUser(HttpServletRequest httpServletRequest,
|
||||
HttpServletResponse httpServletResponse) {
|
||||
public User getCurrentUser(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
|
||||
User user = UserHolder.findUser(httpServletRequest, httpServletResponse);
|
||||
if (user != null) {
|
||||
SystemConfig systemConfig = sysParameterService.getSystemConfig();
|
||||
if (!CollectionUtils.isEmpty(systemConfig.getAdmins())
|
||||
&& systemConfig.getAdmins().contains(user.getName())) {
|
||||
SysParameter sysParameter = sysParameterService.getSysParameter();
|
||||
if (!CollectionUtils.isEmpty(sysParameter.getAdmins())
|
||||
&& sysParameter.getAdmins().contains(user.getName())) {
|
||||
user.setIsAdmin(1);
|
||||
}
|
||||
}
|
||||
@@ -72,42 +68,8 @@ public class UserServiceImpl implements UserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String login(UserReq userReq, HttpServletRequest request) {
|
||||
return ComponentFactory.getUserAdaptor().login(userReq, request);
|
||||
public String login(UserReq userReq) {
|
||||
return ComponentFactory.getUserAdaptor().login(userReq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String login(UserReq userReq, String appKey) {
|
||||
return ComponentFactory.getUserAdaptor().login(userReq, appKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword(String userName) {
|
||||
return ComponentFactory.getUserAdaptor().getPassword(userName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPassword(String userName, String password, String newPassword) {
|
||||
ComponentFactory.getUserAdaptor().resetPassword(userName, password, newPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserToken generateToken(String name, String userName, long expireTime) {
|
||||
return ComponentFactory.getUserAdaptor().generateToken(name, userName, expireTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserToken> getUserTokens(String userName) {
|
||||
return ComponentFactory.getUserAdaptor().getUserTokens(userName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserToken getUserToken(Long id) {
|
||||
return ComponentFactory.getUserAdaptor().getUserToken(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserToken(Long id) {
|
||||
ComponentFactory.getUserAdaptor().deleteUserToken(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.tencent.supersonic.auth.authentication.strategy;
|
||||
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserStrategy;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserStrategy;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@@ -17,11 +17,7 @@ public class FakeUserStrategy implements UserStrategy {
|
||||
|
||||
@Override
|
||||
public User findUser(HttpServletRequest request, HttpServletResponse response) {
|
||||
return User.getDefaultUser();
|
||||
return User.getFakeUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User findUser(String token, String appKey) {
|
||||
return User.getDefaultUser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
package com.tencent.supersonic.auth.authentication.strategy;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserStrategy;
|
||||
import com.tencent.supersonic.auth.authentication.utils.UserTokenUtils;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.constant.UserConstants;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserStrategy;
|
||||
import com.tencent.supersonic.auth.authentication.utils.TokenService;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class HttpHeaderUserStrategy implements UserStrategy {
|
||||
|
||||
private final TokenService tokenService;
|
||||
|
||||
public HttpHeaderUserStrategy(TokenService tokenService) {
|
||||
this.tokenService = tokenService;
|
||||
private final UserTokenUtils userTokenUtils;
|
||||
|
||||
|
||||
public HttpHeaderUserStrategy(UserTokenUtils userTokenUtils) {
|
||||
this.userTokenUtils = userTokenUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,33 +26,6 @@ public class HttpHeaderUserStrategy implements UserStrategy {
|
||||
|
||||
@Override
|
||||
public User findUser(HttpServletRequest request, HttpServletResponse response) {
|
||||
return getUser(request);
|
||||
return userTokenUtils.getUser(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User findUser(String token, String appKey) {
|
||||
return getUser(token, appKey);
|
||||
}
|
||||
|
||||
public User getUser(HttpServletRequest request) {
|
||||
final Optional<Claims> claimsOptional = tokenService.getClaims(request);
|
||||
return claimsOptional.map(this::getUser).orElse(User.getVisitUser());
|
||||
}
|
||||
|
||||
public User getUser(String token, String appKey) {
|
||||
final Optional<Claims> claimsOptional = tokenService.getClaims(token, appKey);
|
||||
return claimsOptional.map(this::getUser).orElse(User.getVisitUser());
|
||||
}
|
||||
|
||||
private User getUser(Claims claims) {
|
||||
Long userId =
|
||||
Long.parseLong(claims.getOrDefault(UserConstants.TOKEN_USER_ID, 0).toString());
|
||||
String userName = String.valueOf(claims.get(UserConstants.TOKEN_USER_NAME));
|
||||
String email = String.valueOf(claims.get(UserConstants.TOKEN_USER_EMAIL));
|
||||
String displayName = String.valueOf(claims.get(UserConstants.TOKEN_USER_DISPLAY_NAME));
|
||||
Integer isAdmin = claims.get(UserConstants.TOKEN_IS_ADMIN) == null ? 0
|
||||
: Integer.parseInt(claims.get(UserConstants.TOKEN_IS_ADMIN).toString());
|
||||
return User.get(userId, userName, displayName, email, isAdmin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
package com.tencent.supersonic.auth.authentication.strategy;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserStrategy;
|
||||
import com.tencent.supersonic.auth.api.authentication.utils.UserHolder;
|
||||
import java.util.List;
|
||||
import javax.annotation.PostConstruct;
|
||||
import lombok.Data;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@Data
|
||||
public class UserStrategyFactory {
|
||||
|
||||
|
||||
private List<UserStrategy> userStrategyList;
|
||||
|
||||
|
||||
private AuthenticationConfig authenticationConfig;
|
||||
|
||||
public UserStrategyFactory(AuthenticationConfig authenticationConfig,
|
||||
List<UserStrategy> userStrategyList) {
|
||||
public UserStrategyFactory(AuthenticationConfig authenticationConfig, List<UserStrategy> userStrategyList) {
|
||||
this.authenticationConfig = authenticationConfig;
|
||||
this.userStrategyList = userStrategyList;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.tencent.supersonic.auth.authentication.utils;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.adaptor.UserAdaptor;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ComponentFactory {
|
||||
@@ -17,7 +16,8 @@ public class ComponentFactory {
|
||||
}
|
||||
|
||||
private static <T> T init(Class<T> factoryType) {
|
||||
return SpringFactoriesLoader
|
||||
.loadFactories(factoryType, Thread.currentThread().getContextClassLoader()).get(0);
|
||||
return SpringFactoriesLoader.loadFactories(factoryType,
|
||||
Thread.currentThread().getContextClassLoader()).get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
package com.tencent.supersonic.auth.authentication.utils;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
|
||||
import com.tencent.supersonic.common.pojo.exception.AccessException;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_PREFIX;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_NAME;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TokenService {
|
||||
|
||||
private AuthenticationConfig authenticationConfig;
|
||||
|
||||
public TokenService(AuthenticationConfig authenticationConfig) {
|
||||
this.authenticationConfig = authenticationConfig;
|
||||
}
|
||||
|
||||
public String generateToken(Map<String, Object> claims, HttpServletRequest request) {
|
||||
String appKey = getAppKey(request);
|
||||
long expiration = System.currentTimeMillis() + authenticationConfig.getTokenTimeout();
|
||||
return generateToken(claims, appKey, expiration);
|
||||
}
|
||||
|
||||
public String generateToken(Map<String, Object> claims, long expiration) {
|
||||
String appKey = authenticationConfig.getTokenDefaultAppKey();
|
||||
long exp = System.currentTimeMillis() + expiration;
|
||||
return generateToken(claims, appKey, exp);
|
||||
}
|
||||
|
||||
public String generateToken(Map<String, Object> claims, String appKey) {
|
||||
long expiration = System.currentTimeMillis() + authenticationConfig.getTokenTimeout();
|
||||
return toTokenString(claims, appKey, expiration);
|
||||
}
|
||||
|
||||
public String generateToken(Map<String, Object> claims, String appKey, long expiration) {
|
||||
return toTokenString(claims, appKey, expiration);
|
||||
}
|
||||
|
||||
public String generateAppUserToken(HttpServletRequest request) {
|
||||
String appName = request.getHeader("AppId");
|
||||
if (StringUtils.isBlank(appName)) {
|
||||
String message = "AppId is blank, get app_user failed";
|
||||
log.warn("{}, uri: {}", message, request.getServletPath());
|
||||
throw new AccessException(message);
|
||||
}
|
||||
|
||||
UserWithPassword appUser = new UserWithPassword(appName);
|
||||
appUser.setId(1L);
|
||||
appUser.setName(appName);
|
||||
appUser.setPassword("c3VwZXJzb25pY0BiaWNvbdktJJYWw6A3rEmBUPzbn/6DNeYnD+y3mAwDKEMS3KVT");
|
||||
appUser.setDisplayName(appName);
|
||||
appUser.setIsAdmin(0);
|
||||
return generateToken(UserWithPassword.convert(appUser), request);
|
||||
}
|
||||
|
||||
public Optional<Claims> getClaims(HttpServletRequest request) {
|
||||
String token = request.getHeader(authenticationConfig.getTokenHttpHeaderKey());
|
||||
String appKey = getAppKey(request);
|
||||
return getClaims(token, appKey);
|
||||
}
|
||||
|
||||
private Optional<Claims> getClaims(String token, HttpServletRequest request) {
|
||||
Optional<Claims> claims;
|
||||
try {
|
||||
String appKey = getAppKey(request);
|
||||
claims = getClaims(token, appKey);
|
||||
} catch (Exception e) {
|
||||
throw new AccessException("parse user info from token failed :" + token);
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
public Optional<Claims> getClaims(String token, String appKey) {
|
||||
try {
|
||||
String tokenSecret = getTokenSecret(appKey);
|
||||
Claims claims =
|
||||
Jwts.parser().setSigningKey(tokenSecret.getBytes(StandardCharsets.UTF_8))
|
||||
.build().parseClaimsJws(getTokenString(token)).getBody();
|
||||
return Optional.of(claims);
|
||||
} catch (Exception e) {
|
||||
log.info("can not getClaims from appKey:{} token:{}, please login", appKey, token);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static String getTokenString(String token) {
|
||||
return token.startsWith(TOKEN_PREFIX)
|
||||
? token.substring(token.indexOf(TOKEN_PREFIX) + TOKEN_PREFIX.length()).trim()
|
||||
: token.trim();
|
||||
}
|
||||
|
||||
private String toTokenString(Map<String, Object> claims, String appKey, long expiration) {
|
||||
Date expirationDate = new Date(expiration);
|
||||
String tokenSecret = getTokenSecret(appKey);
|
||||
|
||||
return Jwts.builder().setClaims(claims).setSubject(claims.get(TOKEN_USER_NAME).toString())
|
||||
.setExpiration(expirationDate)
|
||||
.signWith(new SecretKeySpec(tokenSecret.getBytes(StandardCharsets.UTF_8),
|
||||
SignatureAlgorithm.HS512.getJcaName()), SignatureAlgorithm.HS512)
|
||||
.compact();
|
||||
}
|
||||
|
||||
private String getTokenSecret(String appKey) {
|
||||
Map<String, String> appKeyToSecretMap = authenticationConfig.getAppKeyToSecretMap();
|
||||
String secret = appKeyToSecretMap.get(appKey);
|
||||
if (StringUtils.isBlank(secret)) {
|
||||
throw new AccessException("get secret from appKey failed :" + appKey);
|
||||
}
|
||||
return secret;
|
||||
}
|
||||
|
||||
public String getAppKey(HttpServletRequest request) {
|
||||
String appKey = request.getHeader(authenticationConfig.getTokenHttpHeaderAppKey());
|
||||
if (StringUtils.isBlank(appKey)) {
|
||||
appKey = authenticationConfig.getTokenDefaultAppKey();
|
||||
}
|
||||
return appKey;
|
||||
}
|
||||
|
||||
public String getDefaultAppKey() {
|
||||
return authenticationConfig.getTokenDefaultAppKey();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.tencent.supersonic.auth.authentication.utils;
|
||||
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_ALGORITHM;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_CREATE_TIME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_IS_ADMIN;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_PREFIX;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_TIME_OUT;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_DISPLAY_NAME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_EMAIL;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_ID;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_NAME;
|
||||
import static com.tencent.supersonic.auth.api.authentication.constant.UserConstants.TOKEN_USER_PASSWORD;
|
||||
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
|
||||
import com.tencent.supersonic.common.pojo.exception.AccessException;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserTokenUtils {
|
||||
|
||||
private AuthenticationConfig authenticationConfig;
|
||||
|
||||
public UserTokenUtils(AuthenticationConfig authenticationConfig) {
|
||||
this.authenticationConfig = authenticationConfig;
|
||||
}
|
||||
|
||||
public String generateToken(UserWithPassword user) {
|
||||
Map<String, Object> claims = new HashMap<>(5);
|
||||
claims.put(TOKEN_USER_ID, user.getId());
|
||||
claims.put(TOKEN_USER_NAME, StringUtils.isEmpty(user.getName()) ? "" : user.getName());
|
||||
claims.put(TOKEN_USER_PASSWORD, StringUtils.isEmpty(user.getPassword()) ? "" : user.getPassword());
|
||||
claims.put(TOKEN_USER_DISPLAY_NAME, user.getDisplayName());
|
||||
claims.put(TOKEN_CREATE_TIME, System.currentTimeMillis());
|
||||
claims.put(TOKEN_IS_ADMIN, user.getIsAdmin());
|
||||
return generate(claims);
|
||||
}
|
||||
|
||||
public String generateAdminToken() {
|
||||
Map<String, Object> claims = new HashMap<>(5);
|
||||
claims.put(TOKEN_USER_ID, "1");
|
||||
claims.put(TOKEN_USER_NAME, "admin");
|
||||
claims.put(TOKEN_USER_PASSWORD, "admin");
|
||||
claims.put(TOKEN_USER_DISPLAY_NAME, "admin");
|
||||
claims.put(TOKEN_CREATE_TIME, System.currentTimeMillis());
|
||||
claims.put(TOKEN_IS_ADMIN, 1);
|
||||
return generate(claims);
|
||||
}
|
||||
|
||||
public User getUser(HttpServletRequest request) {
|
||||
String token = request.getHeader(authenticationConfig.getTokenHttpHeaderKey());
|
||||
final Claims claims = getClaims(token);
|
||||
Long userId = Long.parseLong(claims.getOrDefault(TOKEN_USER_ID, 0).toString());
|
||||
String userName = String.valueOf(claims.get(TOKEN_USER_NAME));
|
||||
String email = String.valueOf(claims.get(TOKEN_USER_EMAIL));
|
||||
String displayName = String.valueOf(claims.get(TOKEN_USER_DISPLAY_NAME));
|
||||
Integer isAdmin = claims.get(TOKEN_IS_ADMIN) == null
|
||||
? 0 : Integer.parseInt(claims.get(TOKEN_IS_ADMIN).toString());
|
||||
return User.get(userId, userName, displayName, email, isAdmin);
|
||||
}
|
||||
|
||||
public UserWithPassword getUserWithPassword(HttpServletRequest request) {
|
||||
String token = request.getHeader(authenticationConfig.getTokenHttpHeaderKey());
|
||||
if (StringUtils.isBlank(token)) {
|
||||
String message = "token is blank, get user failed";
|
||||
log.warn("{}, uri: {}", message, request.getServletPath());
|
||||
throw new AccessException(message);
|
||||
}
|
||||
final Claims claims = getClaims(token);
|
||||
Long userId = Long.parseLong(claims.getOrDefault(TOKEN_USER_ID, 0).toString());
|
||||
String userName = String.valueOf(claims.get(TOKEN_USER_NAME));
|
||||
String email = String.valueOf(claims.get(TOKEN_USER_EMAIL));
|
||||
String displayName = String.valueOf(claims.get(TOKEN_USER_DISPLAY_NAME));
|
||||
String password = String.valueOf(claims.get(TOKEN_USER_PASSWORD));
|
||||
Integer isAdmin = claims.get(TOKEN_IS_ADMIN) == null
|
||||
? 0 : Integer.parseInt(claims.get(TOKEN_IS_ADMIN).toString());
|
||||
return UserWithPassword.get(userId, userName, displayName, email, password, isAdmin);
|
||||
}
|
||||
|
||||
private Claims getClaims(String token) {
|
||||
Claims claims;
|
||||
try {
|
||||
claims = Jwts.parser()
|
||||
.setSigningKey(authenticationConfig.getTokenSecret().getBytes(StandardCharsets.UTF_8))
|
||||
.parseClaimsJws(token.startsWith(TOKEN_PREFIX)
|
||||
? token.substring(token.indexOf(TOKEN_PREFIX) + TOKEN_PREFIX.length()).trim() :
|
||||
token.trim()).getBody();
|
||||
} catch (Exception e) {
|
||||
throw new AccessException("parse user info from token failed :" + token);
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
private String generate(Map<String, Object> claims) {
|
||||
return toTokenString(claims);
|
||||
}
|
||||
|
||||
private String toTokenString(Map<String, Object> claims) {
|
||||
long expiration = Long.parseLong(claims.get(TOKEN_CREATE_TIME) + "") + TOKEN_TIME_OUT;
|
||||
|
||||
SignatureAlgorithm.valueOf(TOKEN_ALGORITHM);
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(claims.get(TOKEN_USER_NAME).toString())
|
||||
.setExpiration(new Date(expiration))
|
||||
.signWith(SignatureAlgorithm.valueOf(TOKEN_ALGORITHM),
|
||||
authenticationConfig.getTokenSecret().getBytes(StandardCharsets.UTF_8))
|
||||
.compact();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
<result column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="name" jdbcType="VARCHAR" property="name" />
|
||||
<result column="password" jdbcType="VARCHAR" property="password" />
|
||||
<result column="salt" jdbcType="VARCHAR" property="salt" />
|
||||
<result column="display_name" jdbcType="VARCHAR" property="displayName" />
|
||||
<result column="email" jdbcType="VARCHAR" property="email" />
|
||||
<result column="is_admin" jdbcType="INTEGER" property="isAdmin" />
|
||||
@@ -40,7 +39,7 @@
|
||||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, name, password, salt, display_name, email, is_admin
|
||||
id, name, password, display_name, email, is_admin
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDOExample" resultMap="BaseResultMap">
|
||||
select
|
||||
@@ -60,10 +59,10 @@
|
||||
</if>
|
||||
</select>
|
||||
<insert id="insert" parameterType="com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO">
|
||||
insert into s2_user (id, name, password, salt,
|
||||
insert into s2_user (id, name, password,
|
||||
display_name, email, is_admin
|
||||
)
|
||||
values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{salt,jdbcType=VARCHAR},
|
||||
values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR},
|
||||
#{displayName,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{isAdmin,jdbcType=INTEGER}
|
||||
)
|
||||
</insert>
|
||||
@@ -79,9 +78,6 @@
|
||||
<if test="password != null">
|
||||
password,
|
||||
</if>
|
||||
<if test="password != null">
|
||||
salt,
|
||||
</if>
|
||||
<if test="displayName != null">
|
||||
display_name,
|
||||
</if>
|
||||
@@ -102,9 +98,6 @@
|
||||
<if test="password != null">
|
||||
#{password,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="salt != null">
|
||||
#{salt,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="displayName != null">
|
||||
#{displayName,jdbcType=VARCHAR},
|
||||
</if>
|
||||
@@ -122,29 +115,4 @@
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<update id="updateByPrimaryKey" parameterType="com.tencent.supersonic.auth.authentication.persistence.dataobject.UserDO">
|
||||
update s2_user
|
||||
<set>
|
||||
<if test="name != null">
|
||||
name = #{name,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="password != null">
|
||||
password = #{password,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="salt != null">
|
||||
salt = #{salt,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="displayName != null">
|
||||
display_name = #{displayName,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="email != null">
|
||||
email = #{email,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="isAdmin != null">
|
||||
is_admin = #{isAdmin,jdbcType=INTEGER},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=BIGINT}
|
||||
</update>
|
||||
</mapper>
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.tencent.supersonic.auth.authentication.persistence.mapper.UserTokenDOMapper">
|
||||
<resultMap id="BaseResultMap" type="com.tencent.supersonic.auth.authentication.persistence.dataobject.UserTokenDO">
|
||||
<result column="id" jdbcType="BIGINT" property="id" />
|
||||
<result column="name" jdbcType="VARCHAR" property="name" />
|
||||
<result column="token" jdbcType="VARCHAR" property="token" />
|
||||
<result column="expire_time" jdbcType="TIMESTAMP" property="expireTime" />
|
||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
|
||||
</resultMap>
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.tencent.supersonic.auth.authorization.rest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.utils.UserHolder;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthGroup;
|
||||
import com.tencent.supersonic.auth.api.authorization.request.QueryAuthResReq;
|
||||
import com.tencent.supersonic.auth.api.authorization.response.AuthorizedResourceResp;
|
||||
import com.tencent.supersonic.auth.api.authorization.service.AuthService;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthGroup;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -17,8 +17,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
@Slf4j
|
||||
@@ -36,7 +34,9 @@ public class AuthController {
|
||||
return authService.queryAuthGroups(modelId, groupId);
|
||||
}
|
||||
|
||||
/** 新建权限组 */
|
||||
/**
|
||||
* 新建权限组
|
||||
*/
|
||||
@PostMapping("/createGroup")
|
||||
public void newAuthGroup(@RequestBody AuthGroup group) {
|
||||
group.setGroupId(null);
|
||||
@@ -69,7 +69,8 @@ public class AuthController {
|
||||
*/
|
||||
@PostMapping("/queryAuthorizedRes")
|
||||
public AuthorizedResourceResp queryAuthorizedResources(@RequestBody QueryAuthResReq req,
|
||||
HttpServletRequest request, HttpServletResponse response) {
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
User user = UserHolder.findUser(request, response);
|
||||
return authService.queryAuthorizedResources(req, user);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
package com.tencent.supersonic.auth.authorization.service;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.Gson;
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authentication.service.UserService;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthGroup;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthRes;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthRule;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthResGrp;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.DimensionFilter;
|
||||
import com.tencent.supersonic.auth.api.authorization.request.QueryAuthResReq;
|
||||
import com.tencent.supersonic.auth.api.authorization.response.AuthorizedResourceResp;
|
||||
import com.tencent.supersonic.auth.api.authorization.service.AuthService;
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthGroup;
|
||||
import com.tencent.supersonic.auth.api.authorization.pojo.AuthRule;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@@ -30,17 +32,16 @@ public class AuthServiceImpl implements AuthService {
|
||||
|
||||
private UserService userService;
|
||||
|
||||
public AuthServiceImpl(JdbcTemplate jdbcTemplate, UserService userService) {
|
||||
public AuthServiceImpl(JdbcTemplate jdbcTemplate,
|
||||
UserService userService) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
private List<AuthGroup> load() {
|
||||
List<String> rows =
|
||||
jdbcTemplate.queryForList("select config from s2_auth_groups", String.class);
|
||||
List<String> rows = jdbcTemplate.queryForList("select config from s2_auth_groups", String.class);
|
||||
Gson g = new Gson();
|
||||
return rows.stream().map(row -> g.fromJson(row, AuthGroup.class))
|
||||
.collect(Collectors.toList());
|
||||
return rows.stream().map(row -> g.fromJson(row, AuthGroup.class)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,11 +63,11 @@ public class AuthServiceImpl implements AuthService {
|
||||
nextGroupId = obj + 1;
|
||||
}
|
||||
group.setGroupId(nextGroupId);
|
||||
jdbcTemplate.update("insert into s2_auth_groups (group_id, config) values (?, ?);",
|
||||
nextGroupId, g.toJson(group));
|
||||
jdbcTemplate.update("insert into s2_auth_groups (group_id, config) values (?, ?);", nextGroupId,
|
||||
g.toJson(group));
|
||||
} else {
|
||||
jdbcTemplate.update("update s2_auth_groups set config = ? where group_id = ?;",
|
||||
g.toJson(group), group.getGroupId());
|
||||
jdbcTemplate.update("update s2_auth_groups set config = ? where group_id = ?;", g.toJson(group),
|
||||
group.getGroupId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,60 +78,77 @@ public class AuthServiceImpl implements AuthService {
|
||||
|
||||
@Override
|
||||
public AuthorizedResourceResp queryAuthorizedResources(QueryAuthResReq req, User user) {
|
||||
if (CollectionUtils.isEmpty(req.getModelIds())) {
|
||||
return new AuthorizedResourceResp();
|
||||
}
|
||||
Set<String> userOrgIds = userService.getUserAllOrgId(user.getName());
|
||||
List<AuthGroup> groups =
|
||||
getAuthGroups(req.getModelIds(), user.getName(), new ArrayList<>(userOrgIds));
|
||||
List<AuthGroup> groups = getAuthGroups(req.getModelIds(), user.getName(), new ArrayList<>(userOrgIds));
|
||||
AuthorizedResourceResp resource = new AuthorizedResourceResp();
|
||||
Map<Long, List<AuthGroup>> authGroupsByModelId =
|
||||
groups.stream().collect(Collectors.groupingBy(AuthGroup::getModelId));
|
||||
for (Long modelId : req.getModelIds()) {
|
||||
Map<Long, List<AuthGroup>> authGroupsByModelId = groups.stream()
|
||||
.collect(Collectors.groupingBy(AuthGroup::getModelId));
|
||||
Map<Long, List<AuthRes>> reqAuthRes = req.getResources().stream()
|
||||
.collect(Collectors.groupingBy(AuthRes::getModelId));
|
||||
|
||||
for (Long modelId : reqAuthRes.keySet()) {
|
||||
List<AuthRes> reqResourcesList = reqAuthRes.get(modelId);
|
||||
AuthResGrp rg = new AuthResGrp();
|
||||
if (authGroupsByModelId.containsKey(modelId)) {
|
||||
List<AuthGroup> authGroups = authGroupsByModelId.get(modelId);
|
||||
for (AuthGroup authRuleGroup : authGroups) {
|
||||
List<AuthRule> authRules = authRuleGroup.getAuthRules();
|
||||
for (AuthRule authRule : authRules) {
|
||||
for (String resBizName : authRule.resourceNames()) {
|
||||
resource.getAuthResList().add(new AuthRes(modelId, resBizName));
|
||||
for (AuthRes reqRes : reqResourcesList) {
|
||||
for (AuthGroup authRuleGroup : authGroups) {
|
||||
List<AuthRule> authRules = authRuleGroup.getAuthRules();
|
||||
List<String> allAuthItems = new ArrayList<>();
|
||||
authRules.forEach(authRule -> allAuthItems.addAll(authRule.resourceNames()));
|
||||
|
||||
if (allAuthItems.contains(reqRes.getName())) {
|
||||
rg.getGroup().add(reqRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(rg.getGroup())) {
|
||||
resource.getResources().add(rg);
|
||||
}
|
||||
}
|
||||
Set<Map.Entry<Long, List<AuthGroup>>> entries = authGroupsByModelId.entrySet();
|
||||
for (Map.Entry<Long, List<AuthGroup>> entry : entries) {
|
||||
List<AuthGroup> authGroups = entry.getValue();
|
||||
for (AuthGroup authGroup : authGroups) {
|
||||
DimensionFilter df = new DimensionFilter();
|
||||
df.setDescription(authGroup.getDimensionFilterDescription());
|
||||
df.setExpressions(authGroup.getDimensionFilters());
|
||||
resource.getFilters().add(df);
|
||||
|
||||
if (!CollectionUtils.isEmpty(req.getModelIds())) {
|
||||
List<AuthGroup> authGroups = Lists.newArrayList();
|
||||
for (Long modelId : authGroupsByModelId.keySet()) {
|
||||
authGroups.addAll(authGroupsByModelId.getOrDefault(modelId, Lists.newArrayList()));
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(authGroups)) {
|
||||
for (AuthGroup group : authGroups) {
|
||||
if (group.getDimensionFilters() != null
|
||||
&& group.getDimensionFilters().stream().anyMatch(expr -> !Strings.isNullOrEmpty(expr))) {
|
||||
DimensionFilter df = new DimensionFilter();
|
||||
df.setDescription(group.getDimensionFilterDescription());
|
||||
df.setExpressions(group.getDimensionFilters());
|
||||
resource.getFilters().add(df);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
private List<AuthGroup> getAuthGroups(List<Long> modelIds, String userName,
|
||||
List<String> departmentIds) {
|
||||
List<AuthGroup> groups = load().stream().filter(group -> {
|
||||
if (!modelIds.contains(group.getModelId())) {
|
||||
return false;
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(group.getAuthorizedUsers())
|
||||
&& group.getAuthorizedUsers().contains(userName)) {
|
||||
return true;
|
||||
}
|
||||
for (String departmentId : departmentIds) {
|
||||
if (!CollectionUtils.isEmpty(group.getAuthorizedDepartmentIds())
|
||||
&& group.getAuthorizedDepartmentIds().contains(departmentId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).collect(Collectors.toList());
|
||||
private List<AuthGroup> getAuthGroups(List<Long> modelIds, String userName, List<String> departmentIds) {
|
||||
List<AuthGroup> groups = load().stream()
|
||||
.filter(group -> {
|
||||
if (CollectionUtils.isEmpty(modelIds) || !modelIds.contains(group.getModelId())) {
|
||||
return false;
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(group.getAuthorizedUsers()) && group.getAuthorizedUsers()
|
||||
.contains(userName)) {
|
||||
return true;
|
||||
}
|
||||
for (String departmentId : departmentIds) {
|
||||
if (!CollectionUtils.isEmpty(group.getAuthorizedDepartmentIds())
|
||||
&& group.getAuthorizedDepartmentIds().contains(departmentId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).collect(Collectors.toList());
|
||||
log.info("user:{} department:{} authGroups:{}", userName, departmentIds, groups);
|
||||
return groups;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
## 使用场景
|
||||
产品上线阶段批量测试问答对话的问题,统计测试结果。
|
||||
注意:与evaluation模块的区别,evaluation是构建数据集多个模型的横向评估,benchmark是选定模型下,批量自动化业务问题的测试。
|
||||
## 功能说明
|
||||
批量自动化测试问答对话测试,支持单轮问答测试。
|
||||
|
||||
## 使用说明
|
||||
- 注意1:建议在开发测试环境的执行,如果需要在生产环境的测试,请避开用户使用高峰期。
|
||||
- 注意2: python版本要求3.8可以运行。3.8.5版本测试通过。以上python版本未测试。
|
||||
1. 准备测试问题
|
||||
|
||||
将问题写入`test_data.csv`文件,格式如下:
|
||||
```csv
|
||||
question
|
||||
各BG期间在职、入职、离职人员的平均薪资是多少?(注意:薪资不包括香港视源、广视以及并购控股子公司青松、仙视的数据。)
|
||||
各BG期间入职且仍在职的人数有多少?
|
||||
各BG当月的净增长人数及其增长率是多少?
|
||||
```
|
||||
将文件放入`benchmark/data`目录下。
|
||||
|
||||
2. 执行测试
|
||||
```bash
|
||||
python benchmark -u http://localhost:3100 -a 6 -c 141 -f data/renli.csv -p zds
|
||||
```
|
||||
参数说明:
|
||||
- -a: 问答对话的id
|
||||
- -c: chat_id
|
||||
- -f: 测试问题文件
|
||||
- -u: 用户id
|
||||
如果执行报错,没有安装相关python包,可以执行`pip install -r requirements.txt`安装相关包。
|
||||
|
||||
3. 查看测试结果
|
||||
当前,只能在数据库中查看测试结果。
|
||||
```sql
|
||||
select question_id,chat_id,create_time,query_text,
|
||||
JSON_EXTRACT(parse_info,'$.sqlInfo.s2SQL') as s2sql,
|
||||
JSON_EXTRACT(parse_info,'$.sqlInfo.correctS2SQL') as correctS2SQL,
|
||||
JSON_EXTRACT(parse_info,'$.sqlInfo.querySQL') as querySQL,
|
||||
'请标记正确的SQL' as correctSQL,
|
||||
'请标记生成SQL是否正确' as isOk,
|
||||
'请分类不正确的原因' as reason
|
||||
from s2_chat_parse scp where user_name = 'zhaodongsheng' and chat_id = '141';
|
||||
|
||||
select question_id,chat_id,create_time,query_text,
|
||||
JSON_EXTRACT(query_result,'$.querySql') as querySql,
|
||||
JSON_EXTRACT(query_result,'$.queryResults') as queryResults
|
||||
from s2_chat_query where user_name = 'zhaodongsheng' and chat_id = '141' and query_state = 1;
|
||||
|
||||
```
|
||||
4. 查看帮助
|
||||
```bash
|
||||
python benchmark.py --help
|
||||
usage: benchmark.py [-h] -u URL -a AGENTID -c CHATID -f FILEPATH -p USERNAME
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-u URL, --url URL url:问答系统url,例如:https://chatdata-dev.test.com
|
||||
-a AGENTID, --agentId AGENTID
|
||||
agentId:助手ID
|
||||
-c CHATID, --chatId CHATID
|
||||
chatId:会话ID,需要通过浏览器开发者模式获取
|
||||
-f FILEPATH, --filePath FILEPATH
|
||||
filePath:问题文件路径, csv格式. 请提前上传到benchmark/data目录下
|
||||
-p USERNAME, --userName USERNAME
|
||||
userName:用户名,用户获取登录token
|
||||
```
|
||||
|
||||
## 演示效果
|
||||
```bash
|
||||
python benchmark.py -u https://chatdata-dev.test.com -a 3 -c 35 -f data/shuce.csv -p zds
|
||||
批量测试配置信息[url: https://chatdata-dev.test.com agentId: 3 chatId: 35 filePath: data/shuce.csv userName: zds ]
|
||||
请确认输入的压力测试信息是否正确:
|
||||
1. Yes
|
||||
2. No
|
||||
1
|
||||
start to ask question: 各BG期间在职、入职、离职人员的平均薪资是多少?(注意:薪资不包括香港视源、广视以及并购控股子公司青松、仙视的数据。)
|
||||
start to ask question: 各BG期间入职且仍在职的人数有多少?
|
||||
start to ask question: 各BG当月的净增长人数及其增长率是多少?
|
||||
```
|
||||
|
||||
## TODO
|
||||
- [x] 问答对话测试
|
||||
- [ ] 多轮对话测试
|
||||
- [ ] 问答对话测试结果展示
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
# -----------------------------------------------------------------------------------
|
||||
'''
|
||||
@filename : batchmark.py
|
||||
@time : 2024/06/20
|
||||
@author : zhaodongsheng
|
||||
@Version : 1.0
|
||||
@description : 批量问答测试
|
||||
'''
|
||||
# -----------------------------------------------------------------------------------
|
||||
import pandas as pd
|
||||
import json
|
||||
import requests
|
||||
import time
|
||||
import jwt
|
||||
import traceback
|
||||
|
||||
class BatchTest:
|
||||
def __init__(self, url, agentId, chatId, userName):
|
||||
self.base_url = url + '/api/chat/query/'
|
||||
self.agentId = agentId
|
||||
self.auth_token = self.__get_authorization(userName)
|
||||
self.chatId = chatId
|
||||
|
||||
def parse(self, query_text):
|
||||
url = self.base_url + 'parse'
|
||||
data = {
|
||||
'queryText': query_text,
|
||||
'agentId': self.agentId,
|
||||
'chatId': self.chatId,
|
||||
}
|
||||
headers = {
|
||||
'Authorization': 'Bearer ' + self.auth_token,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, data=json.dumps(data))
|
||||
return response.json()
|
||||
|
||||
def execute(self, agentId, query_text, queryId):
|
||||
url = self.base_url + 'execute'
|
||||
data = {
|
||||
'agentId': agentId,
|
||||
'queryText': query_text,
|
||||
'parseId': 1,
|
||||
'chatId': self.chatId,
|
||||
'queryId': queryId,
|
||||
}
|
||||
headers = {
|
||||
'Authorization': 'Bearer ' + self.auth_token,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, data=json.dumps(data))
|
||||
return response.json()
|
||||
|
||||
def read_question_from_csv(self, filePath):
|
||||
df = pd.read_csv(filePath)
|
||||
return df
|
||||
|
||||
def __get_authorization(self, userName):
|
||||
# secret 请和 com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig.tokenAppSecret 保持一致
|
||||
secret = "WIaO9YRRVt+7QtpPvyWsARFngnEcbaKBk783uGFwMrbJBaochsqCH62L4Kijcb0sZCYoSsiKGV/zPml5MnZ3uQ=="
|
||||
exp = time.time() + 100000000
|
||||
token= jwt.encode({"token_user_name": userName,"exp": exp}, secret, algorithm="HS512")
|
||||
return token
|
||||
|
||||
|
||||
def benchmark(url:str, agentId:str, chatId:str, filePath:str, userName:str):
|
||||
batch_test = BatchTest(url, agentId, chatId, userName)
|
||||
df = batch_test.read_question_from_csv(filePath)
|
||||
for index, row in df.iterrows():
|
||||
question = row['question']
|
||||
print('start to ask question:', question)
|
||||
# 捕获异常,防止程序中断
|
||||
try:
|
||||
parse_resp = batch_test.parse(question)
|
||||
batch_test.execute(agentId, question, parse_resp['data']['queryId'])
|
||||
except Exception as e:
|
||||
print('error:', e)
|
||||
traceback.print_exc()
|
||||
continue
|
||||
time.sleep(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-u', '--url', type=str, required=True, help='url:问答系统url,例如:https://chatdata-dev.test.com')
|
||||
parser.add_argument('-a', '--agentId', type=str, required=True, help='agentId:助手ID')
|
||||
parser.add_argument('-c', '--chatId', type=str, required=True, help='chatId:会话ID,需要通过浏览器开发者模式获取')
|
||||
parser.add_argument('-f', '--filePath', type=str, required=True, help='filePath:问题文件路径, csv格式. 请提前上传到benchmark/data目录下')
|
||||
parser.add_argument('-p', '--userName', type=str, required=True, help='userName:用户名,用户获取登录token')
|
||||
args = parser.parse_args()
|
||||
|
||||
print('批量测试配置信息[url:', args.url,'agentId:', args.agentId, 'chatId:', args.chatId, 'filePath:', args.filePath, 'userName:', args.userName, ']')
|
||||
print('请确认输入的压力测试信息是否正确:')
|
||||
print('1. Yes')
|
||||
print('2. No')
|
||||
confirm = input()
|
||||
if confirm == '1' or confirm == 'Yes' or confirm == 'yes' or confirm == 'YES':
|
||||
benchmark(args.url, args.agentId, args.chatId, args.filePath, args.userName)
|
||||
else:
|
||||
print('请重新输入压力测试配置信息: url, agentId, chatId, filePath, userName')
|
||||
@@ -1,3 +0,0 @@
|
||||
question
|
||||
每个业务组(BG)的员工人数是多少?
|
||||
每个业务组的损益情况如何?
|
||||
|
@@ -1,8 +0,0 @@
|
||||
question
|
||||
在职人员的男女比例是多少?
|
||||
期间入职且离职的人数及其占比如何?
|
||||
期间新入职社招人员的平均年龄是多少?
|
||||
期间入职且在职的人数有多少?
|
||||
期间在职人员的平均年龄是多少?
|
||||
当月的净增长人数及其增长率是多少?
|
||||
期间新入职社招人员的年龄分布情况如何?
|
||||
|
@@ -1,4 +0,0 @@
|
||||
question
|
||||
在广东省内,哪一个学校的累计集备数最多,请返回该学校的学校名称
|
||||
在广东省内,哪一个学校的累计集体备课数最多,请返回该学校的学校名称
|
||||
|
||||
|
@@ -1,5 +0,0 @@
|
||||
pandas==2.2.2
|
||||
PyJWT==2.8.0
|
||||
requests==2.28.2
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.tencent.supersonic.chat.api.pojo;
|
||||
|
||||
import com.tencent.supersonic.headless.api.pojo.SchemaElement;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SchemaElementMatch {
|
||||
|
||||
SchemaElement element;
|
||||
double similarity;
|
||||
String detectWord;
|
||||
String word;
|
||||
Long frequency;
|
||||
boolean isInherited;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.tencent.supersonic.chat.api.pojo;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SchemaMapInfo {
|
||||
|
||||
private Map<Long, List<SchemaElementMatch>> viewElementMatches = new HashMap<>();
|
||||
|
||||
public Set<Long> getMatchedViewInfos() {
|
||||
return viewElementMatches.keySet();
|
||||
}
|
||||
|
||||
public List<SchemaElementMatch> getMatchedElements(Long view) {
|
||||
return viewElementMatches.getOrDefault(view, Lists.newArrayList());
|
||||
}
|
||||
|
||||
public Map<Long, List<SchemaElementMatch>> getViewElementMatches() {
|
||||
return viewElementMatches;
|
||||
}
|
||||
|
||||
public void setViewElementMatches(Map<Long, List<SchemaElementMatch>> viewElementMatches) {
|
||||
this.viewElementMatches = viewElementMatches;
|
||||
}
|
||||
|
||||
public void setMatchedElements(Long view, List<SchemaElementMatch> elementMatches) {
|
||||
viewElementMatches.put(view, elementMatches);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.tencent.supersonic.headless.api.pojo;
|
||||
package com.tencent.supersonic.chat.api.pojo;
|
||||
|
||||
import com.tencent.supersonic.headless.api.pojo.request.QueryFilters;
|
||||
import com.tencent.supersonic.chat.api.pojo.request.QueryFilters;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.tencent.supersonic.chat.api.pojo;
|
||||
|
||||
|
||||
import com.tencent.supersonic.chat.api.pojo.response.SqlInfo;
|
||||
import com.tencent.supersonic.chat.api.pojo.request.QueryFilter;
|
||||
import com.tencent.supersonic.chat.api.pojo.response.EntityInfo;
|
||||
import com.tencent.supersonic.common.pojo.DateConf;
|
||||
import com.tencent.supersonic.common.pojo.Order;
|
||||
import com.tencent.supersonic.common.pojo.enums.QueryType;
|
||||
import com.tencent.supersonic.common.pojo.enums.AggregateTypeEnum;
|
||||
import com.tencent.supersonic.common.pojo.enums.FilterType;
|
||||
import com.tencent.supersonic.headless.api.pojo.SchemaElement;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@Data
|
||||
public class SemanticParseInfo {
|
||||
|
||||
private Integer id;
|
||||
private String queryMode;
|
||||
private SchemaElement view;
|
||||
private Set<SchemaElement> metrics = new TreeSet<>(new SchemaNameLengthComparator());
|
||||
private Set<SchemaElement> dimensions = new LinkedHashSet();
|
||||
private SchemaElement entity;
|
||||
private AggregateTypeEnum aggType = AggregateTypeEnum.NONE;
|
||||
private FilterType filterType = FilterType.UNION;
|
||||
private Set<QueryFilter> dimensionFilters = new LinkedHashSet();
|
||||
private Set<QueryFilter> metricFilters = new LinkedHashSet();
|
||||
private Set<Order> orders = new LinkedHashSet();
|
||||
private DateConf dateInfo;
|
||||
private Long limit;
|
||||
private double score;
|
||||
private List<SchemaElementMatch> elementMatches = new ArrayList<>();
|
||||
private Map<String, Object> properties = new HashMap<>();
|
||||
private EntityInfo entityInfo;
|
||||
private SqlInfo sqlInfo = new SqlInfo();
|
||||
private QueryType queryType = QueryType.ID;
|
||||
|
||||
private static class SchemaNameLengthComparator implements Comparator<SchemaElement> {
|
||||
|
||||
@Override
|
||||
public int compare(SchemaElement o1, SchemaElement o2) {
|
||||
if (o1.getOrder() != o2.getOrder()) {
|
||||
if (o1.getOrder() < o2.getOrder()) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int len1 = o1.getName().length();
|
||||
int len2 = o2.getName().length();
|
||||
if (len1 != len2) {
|
||||
return len1 - len2;
|
||||
} else {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<SchemaElement> getMetrics() {
|
||||
Set<SchemaElement> metricSet = new TreeSet<>(new SchemaNameLengthComparator());
|
||||
metricSet.addAll(metrics);
|
||||
metrics = metricSet;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public Long getViewId() {
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
return view.getView();
|
||||
}
|
||||
|
||||
public SchemaElement getModel() {
|
||||
return view;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.tencent.supersonic.chat.api.pojo;
|
||||
|
||||
import com.tencent.supersonic.headless.api.pojo.SchemaElement;
|
||||
import com.tencent.supersonic.headless.api.pojo.SchemaElementType;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
public class SemanticSchema implements Serializable {
|
||||
|
||||
private List<ViewSchema> viewSchemaList;
|
||||
|
||||
public SemanticSchema(List<ViewSchema> viewSchemaList) {
|
||||
this.viewSchemaList = viewSchemaList;
|
||||
}
|
||||
|
||||
public void add(ViewSchema schema) {
|
||||
viewSchemaList.add(schema);
|
||||
}
|
||||
|
||||
public SchemaElement getElement(SchemaElementType elementType, long elementID) {
|
||||
Optional<SchemaElement> element = Optional.empty();
|
||||
|
||||
switch (elementType) {
|
||||
case ENTITY:
|
||||
element = getElementsById(elementID, getEntities());
|
||||
break;
|
||||
case VIEW:
|
||||
element = getElementsById(elementID, getViews());
|
||||
break;
|
||||
case METRIC:
|
||||
element = getElementsById(elementID, getMetrics());
|
||||
break;
|
||||
case DIMENSION:
|
||||
element = getElementsById(elementID, getDimensions());
|
||||
break;
|
||||
case VALUE:
|
||||
element = getElementsById(elementID, getDimensionValues());
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
if (element.isPresent()) {
|
||||
return element.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Long, String> getViewIdToName() {
|
||||
return viewSchemaList.stream()
|
||||
.collect(Collectors.toMap(a -> a.getView().getId(), a -> a.getView().getName(), (k1, k2) -> k1));
|
||||
}
|
||||
|
||||
public List<SchemaElement> getDimensionValues() {
|
||||
List<SchemaElement> dimensionValues = new ArrayList<>();
|
||||
viewSchemaList.stream().forEach(d -> dimensionValues.addAll(d.getDimensionValues()));
|
||||
return dimensionValues;
|
||||
}
|
||||
|
||||
public List<SchemaElement> getDimensions() {
|
||||
List<SchemaElement> dimensions = new ArrayList<>();
|
||||
viewSchemaList.stream().forEach(d -> dimensions.addAll(d.getDimensions()));
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
public List<SchemaElement> getDimensions(Long viewId) {
|
||||
List<SchemaElement> dimensions = getDimensions();
|
||||
return getElementsByViewId(viewId, dimensions);
|
||||
}
|
||||
|
||||
public SchemaElement getDimension(Long id) {
|
||||
List<SchemaElement> dimensions = getDimensions();
|
||||
Optional<SchemaElement> dimension = getElementsById(id, dimensions);
|
||||
return dimension.orElse(null);
|
||||
}
|
||||
|
||||
public List<SchemaElement> getTags() {
|
||||
List<SchemaElement> tags = new ArrayList<>();
|
||||
viewSchemaList.stream().forEach(d -> tags.addAll(d.getTags()));
|
||||
return tags;
|
||||
}
|
||||
|
||||
public List<SchemaElement> getTags(Long viewId) {
|
||||
List<SchemaElement> tags = new ArrayList<>();
|
||||
viewSchemaList.stream().filter(schemaElement ->
|
||||
viewId.equals(schemaElement.getView().getView()))
|
||||
.forEach(d -> tags.addAll(d.getTags()));
|
||||
return tags;
|
||||
}
|
||||
|
||||
public List<SchemaElement> getMetrics() {
|
||||
List<SchemaElement> metrics = new ArrayList<>();
|
||||
viewSchemaList.stream().forEach(d -> metrics.addAll(d.getMetrics()));
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public List<SchemaElement> getMetrics(Long viewId) {
|
||||
List<SchemaElement> metrics = getMetrics();
|
||||
return getElementsByViewId(viewId, metrics);
|
||||
}
|
||||
|
||||
public List<SchemaElement> getEntities() {
|
||||
List<SchemaElement> entities = new ArrayList<>();
|
||||
viewSchemaList.stream().forEach(d -> entities.add(d.getEntity()));
|
||||
return entities;
|
||||
}
|
||||
|
||||
public List<SchemaElement> getEntities(Long viewId) {
|
||||
List<SchemaElement> entities = getEntities();
|
||||
return getElementsByViewId(viewId, entities);
|
||||
}
|
||||
|
||||
private List<SchemaElement> getElementsByViewId(Long viewId, List<SchemaElement> elements) {
|
||||
return elements.stream()
|
||||
.filter(schemaElement -> viewId.equals(schemaElement.getView()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Optional<SchemaElement> getElementsById(Long id, List<SchemaElement> elements) {
|
||||
return elements.stream()
|
||||
.filter(schemaElement -> id.equals(schemaElement.getId()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public SchemaElement getView(Long viewId) {
|
||||
List<SchemaElement> views = getViews();
|
||||
return getElementsById(viewId, views).orElse(null);
|
||||
}
|
||||
|
||||
public List<SchemaElement> getViews() {
|
||||
List<SchemaElement> views = new ArrayList<>();
|
||||
viewSchemaList.stream().forEach(d -> views.add(d.getView()));
|
||||
return views;
|
||||
}
|
||||
|
||||
public Map<String, String> getBizNameToName(Long viewId) {
|
||||
List<SchemaElement> allElements = new ArrayList<>();
|
||||
allElements.addAll(getDimensions(viewId));
|
||||
allElements.addAll(getMetrics(viewId));
|
||||
return allElements.stream()
|
||||
.collect(Collectors.toMap(SchemaElement::getBizName, SchemaElement::getName, (k1, k2) -> k1));
|
||||
}
|
||||
|
||||
public Map<Long, ViewSchema> getViewSchemaMap() {
|
||||
if (CollectionUtils.isEmpty(viewSchemaList)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return viewSchemaList.stream().collect(Collectors.toMap(viewSchema
|
||||
-> viewSchema.getView().getView(), viewSchema -> viewSchema));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.tencent.supersonic.chat.api.pojo;
|
||||
|
||||
import com.tencent.supersonic.headless.api.pojo.QueryConfig;
|
||||
import com.tencent.supersonic.headless.api.pojo.SchemaElement;
|
||||
import com.tencent.supersonic.headless.api.pojo.SchemaElementType;
|
||||
import com.tencent.supersonic.headless.api.pojo.TagTypeDefaultConfig;
|
||||
import com.tencent.supersonic.headless.api.pojo.TimeDefaultConfig;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
public class ViewSchema {
|
||||
|
||||
private SchemaElement view;
|
||||
private Set<SchemaElement> metrics = new HashSet<>();
|
||||
private Set<SchemaElement> dimensions = new HashSet<>();
|
||||
private Set<SchemaElement> dimensionValues = new HashSet<>();
|
||||
private Set<SchemaElement> tags = new HashSet<>();
|
||||
private SchemaElement entity = new SchemaElement();
|
||||
private QueryConfig queryConfig;
|
||||
|
||||
public SchemaElement getElement(SchemaElementType elementType, long elementID) {
|
||||
Optional<SchemaElement> element = Optional.empty();
|
||||
|
||||
switch (elementType) {
|
||||
case ENTITY:
|
||||
element = Optional.ofNullable(entity);
|
||||
break;
|
||||
case VIEW:
|
||||
element = Optional.of(view);
|
||||
break;
|
||||
case METRIC:
|
||||
element = metrics.stream().filter(e -> e.getId() == elementID).findFirst();
|
||||
break;
|
||||
case DIMENSION:
|
||||
element = dimensions.stream().filter(e -> e.getId() == elementID).findFirst();
|
||||
break;
|
||||
case VALUE:
|
||||
element = dimensionValues.stream().filter(e -> e.getId() == elementID).findFirst();
|
||||
break;
|
||||
case TAG:
|
||||
element = tags.stream().filter(e -> e.getId() == elementID).findFirst();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
if (element.isPresent()) {
|
||||
return element.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public SchemaElement getElement(SchemaElementType elementType, String name) {
|
||||
Optional<SchemaElement> element = Optional.empty();
|
||||
|
||||
switch (elementType) {
|
||||
case ENTITY:
|
||||
element = Optional.ofNullable(entity);
|
||||
break;
|
||||
case VIEW:
|
||||
element = Optional.of(view);
|
||||
break;
|
||||
case METRIC:
|
||||
element = metrics.stream().filter(e -> name.equals(e.getName())).findFirst();
|
||||
break;
|
||||
case DIMENSION:
|
||||
element = dimensions.stream().filter(e -> name.equals(e.getName())).findFirst();
|
||||
break;
|
||||
case VALUE:
|
||||
element = dimensionValues.stream().filter(e -> name.equals(e.getName())).findFirst();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
if (element.isPresent()) {
|
||||
return element.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public TimeDefaultConfig getTagTypeTimeDefaultConfig() {
|
||||
if (queryConfig == null) {
|
||||
return null;
|
||||
}
|
||||
if (queryConfig.getTagTypeDefaultConfig() == null) {
|
||||
return null;
|
||||
}
|
||||
return queryConfig.getTagTypeDefaultConfig().getTimeDefaultConfig();
|
||||
}
|
||||
|
||||
public TimeDefaultConfig getMetricTypeTimeDefaultConfig() {
|
||||
if (queryConfig == null) {
|
||||
return null;
|
||||
}
|
||||
if (queryConfig.getMetricTypeDefaultConfig() == null) {
|
||||
return null;
|
||||
}
|
||||
return queryConfig.getMetricTypeDefaultConfig().getTimeDefaultConfig();
|
||||
}
|
||||
|
||||
public TagTypeDefaultConfig getTagTypeDefaultConfig() {
|
||||
if (queryConfig == null) {
|
||||
return null;
|
||||
}
|
||||
return queryConfig.getTagTypeDefaultConfig();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.enums;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.exception.InvalidArgumentException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public enum MemoryReviewResult {
|
||||
POSITIVE, NEGATIVE;
|
||||
|
||||
public static MemoryReviewResult getMemoryReviewResult(String value) {
|
||||
String validValue = StringUtils.trim(value);
|
||||
for (MemoryReviewResult reviewRet : MemoryReviewResult.values()) {
|
||||
if (StringUtils.equalsIgnoreCase(reviewRet.name(), validValue)) {
|
||||
return reviewRet;
|
||||
}
|
||||
}
|
||||
throw new InvalidArgumentException("Invalid MemoryReviewResult type:[" + value + "]");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.enums;
|
||||
|
||||
public enum MemoryStatus {
|
||||
PENDING, ENABLED, DISABLED;
|
||||
}
|
||||
@@ -7,13 +7,18 @@ import java.util.List;
|
||||
@Data
|
||||
public class ChatAggConfigReq {
|
||||
|
||||
/** invisible dimensions/metrics */
|
||||
/**
|
||||
* invisible dimensions/metrics
|
||||
*/
|
||||
private ItemVisibility visibility;
|
||||
|
||||
/** information about dictionary about the model */
|
||||
/**
|
||||
* information about dictionary about the model
|
||||
*/
|
||||
private List<KnowledgeInfoReq> knowledgeInfos;
|
||||
|
||||
private KnowledgeAdvancedConfig globalKnowledgeConfig;
|
||||
|
||||
private ChatDefaultConfigReq chatDefaultConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +1,45 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.enums.StatusEnum;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** extended information command about model */
|
||||
/**
|
||||
* extended information command about model
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class ChatConfigBaseReq {
|
||||
|
||||
private Long modelId;
|
||||
|
||||
/** the recommended questions about the model */
|
||||
/**
|
||||
* the chatDetailConfig about the model
|
||||
*/
|
||||
private ChatDetailConfigReq chatDetailConfig;
|
||||
|
||||
/**
|
||||
* the chatAggConfig about the model
|
||||
*/
|
||||
private ChatAggConfigReq chatAggConfig;
|
||||
|
||||
|
||||
/**
|
||||
* the recommended questions about the model
|
||||
*/
|
||||
private List<RecommendedQuestionReq> recommendedQuestions;
|
||||
|
||||
/** the llm examples about the model */
|
||||
/**
|
||||
* the llm examples about the model
|
||||
*/
|
||||
private String llmExamples;
|
||||
|
||||
/** available status */
|
||||
/**
|
||||
* available status
|
||||
*/
|
||||
private StatusEnum status;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
public class ChatConfigEditReqReq extends ChatConfigBaseReq {
|
||||
|
||||
private Long id;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.tencent.supersonic.common.pojo.enums.StatusEnum;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ChatConfigFilter {
|
||||
@@ -11,4 +12,4 @@ public class ChatConfigFilter {
|
||||
private Long id;
|
||||
private Long modelId;
|
||||
private StatusEnum status = StatusEnum.ONLINE;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -10,4 +11,6 @@ public class ChatDefaultConfigReq {
|
||||
|
||||
private List<Long> dimensionIds = new ArrayList<>();
|
||||
private List<Long> metricIds = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -7,13 +7,18 @@ import java.util.List;
|
||||
@Data
|
||||
public class ChatDetailConfigReq {
|
||||
|
||||
/** invisible dimensions/metrics */
|
||||
/**
|
||||
* invisible dimensions/metrics
|
||||
*/
|
||||
private ItemVisibility visibility;
|
||||
|
||||
/** information about dictionary about the model */
|
||||
/**
|
||||
* information about dictionary about the model
|
||||
*/
|
||||
private List<KnowledgeInfoReq> knowledgeInfos;
|
||||
|
||||
private KnowledgeAdvancedConfig globalKnowledgeConfig;
|
||||
|
||||
private ChatDefaultConfigReq chatDefaultConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import com.tencent.supersonic.chat.api.pojo.enums.MemoryStatus;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ChatMemoryCreateReq {
|
||||
|
||||
private Integer agentId;
|
||||
|
||||
private String question;
|
||||
|
||||
private String dbSchema;
|
||||
|
||||
private String s2sql;
|
||||
|
||||
private MemoryStatus status;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
|
||||
import com.tencent.supersonic.chat.api.pojo.enums.MemoryStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ChatMemoryFilter {
|
||||
|
||||
private Integer agentId;
|
||||
|
||||
private String question;
|
||||
|
||||
private List<String> questions;
|
||||
|
||||
private MemoryStatus status;
|
||||
|
||||
private MemoryReviewResult llmReviewRet;
|
||||
|
||||
private MemoryReviewResult humanReviewRet;
|
||||
|
||||
private String sort = "desc";
|
||||
private String orderCondition;
|
||||
|
||||
public boolean isAsc() {
|
||||
return "asc".equalsIgnoreCase(sort);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.tencent.supersonic.chat.api.pojo.enums.MemoryReviewResult;
|
||||
import com.tencent.supersonic.chat.api.pojo.enums.MemoryStatus;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ChatMemoryUpdateReq {
|
||||
|
||||
@NotNull(message = "id不可为空")
|
||||
private Long id;
|
||||
|
||||
private String dbSchema;
|
||||
|
||||
private String s2sql;
|
||||
|
||||
private MemoryStatus status;
|
||||
|
||||
private MemoryReviewResult humanReviewRet;
|
||||
|
||||
private String humanReviewCmt;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
|
||||
import com.tencent.supersonic.headless.api.pojo.request.QueryFilters;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ChatParseReq {
|
||||
private String queryText;
|
||||
private Integer chatId;
|
||||
private Integer agentId;
|
||||
private Long dataSetId;
|
||||
private User user;
|
||||
private QueryFilters queryFilters;
|
||||
private boolean saveAnswer = true;
|
||||
private boolean disableLLM = false;
|
||||
private Long queryId;
|
||||
private SemanticParseInfo selectedParse;
|
||||
}
|
||||
@@ -1,22 +1,29 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** the entity info about the model */
|
||||
/**
|
||||
* the entity info about the model
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
public class Entity {
|
||||
|
||||
/** uniquely identifies an entity */
|
||||
/**
|
||||
* uniquely identifies an entity
|
||||
*/
|
||||
private Long entityId;
|
||||
|
||||
/** entity name list */
|
||||
/**
|
||||
* entity name list
|
||||
*/
|
||||
private List<String> names;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
package com.tencent.supersonic.chat.api.pojo.request;
|
||||
|
||||
import com.tencent.supersonic.common.pojo.User;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import com.tencent.supersonic.auth.api.authentication.pojo.User;
|
||||
import com.tencent.supersonic.chat.api.pojo.SemanticParseInfo;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ChatExecuteReq {
|
||||
@Data
|
||||
public class ExecuteQueryReq {
|
||||
private User user;
|
||||
private Integer agentId;
|
||||
private Long queryId;
|
||||
private Integer chatId;
|
||||
private int parseId;
|
||||
private String queryText;
|
||||
private Long queryId;
|
||||
private Integer parseId;
|
||||
private SemanticParseInfo parseInfo;
|
||||
private boolean saveAnswer;
|
||||
}
|
||||
@@ -6,13 +6,18 @@ import lombok.ToString;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
@ToString
|
||||
public class ItemNameVisibilityInfo {
|
||||
|
||||
/** invisible dimensions */
|
||||
/**
|
||||
* invisible dimensions
|
||||
*/
|
||||
private List<String> blackDimNameList = new ArrayList<>();
|
||||
|
||||
/** invisible metrics */
|
||||
/**
|
||||
* invisible metrics
|
||||
*/
|
||||
private List<String> blackMetricNameList = new ArrayList<>();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user