diff --git a/.github/workflows/build-binary-for-release.yml b/.github/workflows/build-binary-for-release.yml index 0356f97fc..5b2f0bdae 100644 --- a/.github/workflows/build-binary-for-release.yml +++ b/.github/workflows/build-binary-for-release.yml @@ -53,7 +53,7 @@ jobs: args: release --clean --skip=validate env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: answer path: ./dist/* diff --git a/.goreleaser.yaml b/.goreleaser.yaml index b517defba..c09196937 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -65,8 +65,6 @@ archives: dst: NOTICE - src: "docs/release/licenses/*" dst: licenses/ - - src: "DISCLAIMER" - dst: DISCLAIMER wrap_in_directory: true checksum: name_template: 'checksums.txt' diff --git a/DISCLAIMER b/DISCLAIMER deleted file mode 100644 index 009fd82bb..000000000 --- a/DISCLAIMER +++ /dev/null @@ -1,10 +0,0 @@ -Apache Answer is an effort undergoing incubation at the Apache -Software Foundation (ASF), sponsored by the Apache Incubator PMC. - -Incubation is required of all newly accepted projects until a further review -indicates that the infrastructure, communications, and decision making process -have stabilized in a manner consistent with other successful ASF projects. - -While incubation status is not necessarily a reflection of the completeness -or stability of the code, it does indicate that the project has yet to be -fully endorsed by the ASF. \ No newline at end of file diff --git a/configs/config.yaml b/configs/config.yaml index 526710109..865bea579 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -24,6 +24,12 @@ data: connection: "/data/sqlite3/answer.db" cache: file_path: "/data/cache/cache.db" + ssl: + enabled: "no" + mode: "require" + cert_file: "/data/cache/ssl/certs/server-ca.pem" + key_file: "/data/cache/ssl/certs/client-cert.pem" + pem_file: "/data/cache/ssl/certs/client-key.pem" i18n: bundle_dir: "/data/i18n" swaggerui: diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index f4c67a188..ea1d0a589 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1637,6 +1637,23 @@ ui: label: Database file placeholder: /data/answer.db msg: Database file cannot be empty. + ssl_enabled: + label: Enable SSL + ssl_enabled_on: + label: On + ssl_enabled_off: + label: Off + ssl_mode: + label: SSL Mode + key_file: + placeholder: Key file path + msg: Path to Key file cannot be empty + cert_file: + placeholder: Cert file path + msg: Path to Cert file cannot be empty + pem_file: + placeholder: Pem file path + msg: Path to Pem file cannot be empty config_yaml: title: Create config.yaml label: The config.yaml file created. diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 07f8d2ea4..a32628971 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1603,6 +1603,23 @@ ui: label: 数据库文件 placeholder: /data/answer.db msg: 数据库文件不能为空。 + ssl_enabled: + label: 使能够 SSL + ssl_enabled_on: + label: 开 + ssl_enabled_off: + label: 关 + ssl_mode: + label: SSL 模式 + key_file: + placeholder: Key 文件路径 + msg: 文件路径不能为空 + cert_file: + placeholder: Cert 文件路径 + msg: 文件路径不能为空 + pem_file: + placeholder: Pem 文件路径 + msg: 文件路径不能为空 config_yaml: title: 创建 config.yaml label: 已创建 config.yaml 文件。 diff --git a/internal/install/install_req.go b/internal/install/install_req.go index 8b2db0a83..939492c88 100644 --- a/internal/install/install_req.go +++ b/internal/install/install_req.go @@ -27,7 +27,9 @@ import ( "github.com/apache/answer/internal/base/reason" "github.com/apache/answer/internal/base/validator" "github.com/apache/answer/pkg/checker" + "github.com/apache/answer/pkg/dir" "github.com/segmentfault/pacman/errors" + "github.com/segmentfault/pacman/log" "xorm.io/xorm/schemas" ) @@ -40,12 +42,17 @@ type CheckConfigFileResp struct { // CheckDatabaseReq check database type CheckDatabaseReq struct { - DbType string `validate:"required,oneof=postgres sqlite3 mysql" json:"db_type"` - DbUsername string `json:"db_username"` - DbPassword string `json:"db_password"` - DbHost string `json:"db_host"` - DbName string `json:"db_name"` - DbFile string `json:"db_file"` + DbType string `validate:"required,oneof=postgres sqlite3 mysql" json:"db_type"` + DbUsername string `json:"db_username"` + DbPassword string `json:"db_password"` + DbHost string `json:"db_host"` + DbName string `json:"db_name"` + DbFile string `json:"db_file"` + Ssl bool `json:"ssl_enabled"` + SslMode string `json:"ssl_mode"` + SslCrt string `json:"pem_file"` + SslKey string `json:"key_file"` + SslCrtClient string `json:"cert_file"` } // GetConnection get connection string @@ -59,8 +66,25 @@ func (r *CheckDatabaseReq) GetConnection() string { } if r.DbType == string(schemas.POSTGRES) { host, port := parsePgSQLHostPort(r.DbHost) - return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", - host, port, r.DbUsername, r.DbPassword, r.DbName) + if !r.Ssl { + return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", + host, port, r.DbUsername, r.DbPassword, r.DbName) + } else if r.SslMode == "require" { + return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", + host, port, r.DbUsername, r.DbPassword, r.DbName, r.SslMode) + } else if r.SslMode == "verify-ca" || r.SslMode == "verify-full" { + if dir.CheckFileExist(r.SslCrt) { + log.Warnf("ssl crt file not exist: %s", r.SslCrt) + } + if dir.CheckFileExist(r.SslCrtClient) { + log.Warnf("ssl crt client file not exist: %s", r.SslCrtClient) + } + if dir.CheckFileExist(r.SslKey) { + log.Warnf("ssl key file not exist: %s", r.SslKey) + } + return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s sslrootcert=%s sslcert=%s sslkey=%s", + host, port, r.DbUsername, r.DbPassword, r.DbName, r.SslMode, r.SslCrt, r.SslCrtClient, r.SslKey) + } } return "" } diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go index 3b3595c47..620b11d06 100644 --- a/internal/service/question_common/question.go +++ b/internal/service/question_common/question.go @@ -23,11 +23,12 @@ import ( "context" "encoding/json" "fmt" - "github.com/apache/answer/internal/service/siteinfo_common" "math" "strings" "time" + "github.com/apache/answer/internal/service/siteinfo_common" + "github.com/apache/answer/internal/base/constant" "github.com/apache/answer/internal/base/data" "github.com/apache/answer/internal/base/handler" diff --git a/ui/src/pages/Install/components/SecondStep/index.tsx b/ui/src/pages/Install/components/SecondStep/index.tsx index bf43f5547..3206f82c6 100644 --- a/ui/src/pages/Install/components/SecondStep/index.tsx +++ b/ui/src/pages/Install/components/SecondStep/index.tsx @@ -18,7 +18,7 @@ */ import { FC, FormEvent } from 'react'; -import { Form, Button } from 'react-bootstrap'; +import { Form, Button, Row, Col } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import Progress from '../Progress'; @@ -46,13 +46,36 @@ const sqlData = [ }, ]; +const sslModes = [ + { + value: 'require', + }, + { + value: 'verify-ca', + }, + { + value: 'verify-full', + }, +]; + const Index: FC = ({ visible, data, changeCallback, nextCallback }) => { const { t } = useTranslation('translation', { keyPrefix: 'install' }); const checkValidated = (): boolean => { let bol = true; - const { db_type, db_username, db_password, db_host, db_name, db_file } = - data; + const { + db_type, + db_username, + db_password, + db_host, + db_name, + db_file, + ssl_enabled, + ssl_mode, + key_file, + cert_file, + pem_file, + } = data; if (db_type.value !== 'sqlite3') { if (!db_username.value) { @@ -63,7 +86,6 @@ const Index: FC = ({ visible, data, changeCallback, nextCallback }) => { errorMsg: t('db_username.msg'), }; } - if (!db_password.value) { bol = false; data.db_password = { @@ -81,7 +103,6 @@ const Index: FC = ({ visible, data, changeCallback, nextCallback }) => { errorMsg: t('db_host.msg'), }; } - if (!db_name.value) { bol = false; data.db_name = { @@ -90,6 +111,34 @@ const Index: FC = ({ visible, data, changeCallback, nextCallback }) => { errorMsg: t('db_name.msg'), }; } + if (db_type.value === 'postgres') { + if (ssl_enabled.value && ssl_mode.value !== 'require') { + if (!key_file.value) { + bol = false; + data.key_file = { + value: '', + isInvalid: true, + errorMsg: t('key_file.msg'), + }; + } + if (!pem_file.value) { + bol = false; + data.pem_file = { + value: '', + isInvalid: true, + errorMsg: t('pem_file.msg'), + }; + } + if (!cert_file.value) { + bol = false; + data.cert_file = { + value: '', + isInvalid: true, + errorMsg: t('cert_file.msg'), + }; + } + } + } } else if (!db_file.value) { bol = false; data.db_file = { @@ -179,12 +228,147 @@ const Index: FC = ({ visible, data, changeCallback, nextCallback }) => { }); }} /> - {data.db_password.errorMsg} - + {data.db_type.value === 'postgres' && ( + + {t('ssl_enabled.label')} + { + changeCallback({ + ssl_enabled: { + value: e.target.checked, + isInvalid: false, + errorMsg: '', + }, + ssl_mode: { + value: 'require', + isInvalid: false, + errorMsg: '', + }, + key_file: { + value: '', + isInvalid: false, + errorMsg: '', + }, + cert_file: { + value: '', + isInvalid: false, + errorMsg: '', + }, + pem_file: { + value: '', + isInvalid: false, + errorMsg: '', + }, + }); + }} + /> + + )} + {data.db_type.value === 'postgres' && data.ssl_enabled.value && ( + + {t('ssl_mode.label')} + { + changeCallback({ + ssl_mode: { + value: e.target.value, + isInvalid: false, + errorMsg: '', + }, + }); + }}> + {sslModes.map((item) => { + return ( + + ); + })} + + + )} + {data.db_type.value === 'postgres' && + data.ssl_enabled.value && + (data.ssl_mode.value === 'verify-ca' || + data.ssl_mode.value === 'verify-full') && ( + + + { + changeCallback({ + key_file: { + value: e.target.value, + isInvalid: false, + errorMsg: '', + }, + }); + }} + required + /> + + {`${data.key_file.errorMsg}`} + + + + { + changeCallback({ + cert_file: { + value: e.target.value, + isInvalid: false, + errorMsg: '', + }, + }); + }} + required + /> + + {`${data.cert_file.errorMsg}`} + + + + { + changeCallback({ + pem_file: { + value: e.target.value, + isInvalid: false, + errorMsg: '', + }, + }); + }} + required + /> + + {`${data.pem_file.errorMsg}`} + + + + )} {t('db_host.label')} = ({ visible, data, changeCallback, nextCallback }) => { {data.db_host.errorMsg} - {t('db_name.label')} { isInvalid: false, errorMsg: '', }, + ssl_enabled: { + value: false, + isInvalid: false, + errorMsg: '', + }, + ssl_mode: { + value: '', + isInvalid: false, + errorMsg: '', + }, + pem_file: { + value: '', + isInvalid: false, + errorMsg: '', + }, + key_file: { + value: '', + isInvalid: false, + errorMsg: '', + }, + cert_file: { + value: '', + isInvalid: false, + errorMsg: '', + }, }); const updateFormData = (params: FormDataType) => { @@ -148,6 +173,8 @@ const Index: FC = () => { db_username: { ...updatedFormData.db_username, value: 'postgres' }, db_password: { ...updatedFormData.db_password, value: 'postgres' }, db_host: { ...updatedFormData.db_host, value: 'db:5432' }, + ssl_enabled: { ...updatedFormData.ssl_enabled, value: false }, + ssl_mode: { ...updatedFormData.ssl_mode, value: '' }, }; } return updatedFormData; @@ -184,6 +211,11 @@ const Index: FC = () => { db_host: formData.db_host.value, db_name: formData.db_name.value, db_file: formData.db_file.value, + ssl_enabled: formData.ssl_enabled.value, + ssl_mode: formData.ssl_mode.value, + pem_file: formData.pem_file.value, + key_file: formData.key_file.value, + cert_file: formData.cert_file.value, }; installInit(params) .then(() => { @@ -203,6 +235,11 @@ const Index: FC = () => { db_host: formData.db_host.value, db_name: formData.db_name.value, db_file: formData.db_file.value, + ssl_enabled: formData.ssl_enabled.value, + ssl_mode: formData.ssl_mode.value, + pem_file: formData.pem_file.value, + key_file: formData.key_file.value, + cert_file: formData.cert_file.value, }; dbCheck(params) .then(() => {